Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import Visibility from '@libs/Visibility';
import isSearchTopmostFullScreenRoute from '@navigation/helpers/isSearchTopmostFullScreenRoute';
import FloatingMessageCounter from '@pages/inbox/report/FloatingMessageCounter';
import getInitialNumToRender from '@pages/inbox/report/getInitialNumReportActionsToRender';
import ReportActionIndexContext from '@pages/inbox/report/ReportActionIndexContext';
import ReportActionsListItemRenderer from '@pages/inbox/report/ReportActionsListItemRenderer';
import {getUnreadMarkerReportAction} from '@pages/inbox/report/shouldDisplayNewMarkerOnReportAction';
import useReportUnreadMessageScrollTracking from '@pages/inbox/report/useReportUnreadMessageScrollTracking';
Expand All @@ -59,6 +60,7 @@ import {getOlderActions, openReport, readNewestAction, subscribeToNewActionEvent
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import {getStableReportSelector} from '@src/selectors/Report';
import type * as OnyxTypes from '@src/types/onyx';
import MoneyRequestReportTransactionList from './MoneyRequestReportTransactionList';
import MoneyRequestViewReportFields from './MoneyRequestViewReportFields';
Expand Down Expand Up @@ -98,6 +100,7 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)
// Self-subscribe to report, policy, metadata, actions, transactions
// report is guaranteed to exist — callers only render this component when report is loaded
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`) as unknown as [OnyxTypes.Report];
const [reportStable] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`, {selector: getStableReportSelector});
const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(report?.policyID)}`);
const [reportLoadingState] = useOnyx(`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${reportIDFromRoute}`);
const [reportPaginationState] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_PAGINATION_STATE}${reportIDFromRoute}`);
Expand Down Expand Up @@ -554,32 +557,33 @@ function MoneyRequestReportActionsList({onLayout}: MoneyRequestReportListProps)
hasNextActionMadeBySameActor(visibleReportActions, index, isOffline);

return (
<ReportActionsListItemRenderer
reportAction={reportAction}
parentReportAction={parentReportAction}
parentReportActionForTransactionThread={EmptyParentReportActionForTransactionThread}
index={index}
report={report}
transactionThreadReport={transactionThreadReport}
displayAsGroup={displayAsGroup}
shouldDisplayNewMarker={reportAction.reportActionID === unreadMarkerReportActionID}
shouldDisplayReplyDivider={visibleReportActions.length > 1}
isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID}
shouldHideThreadDividerLine
linkedReportActionID={linkedReportActionID}
personalDetails={personalDetails}
userBillingFundID={userBillingFundID}
isReportArchived={isReportArchived}
isTryNewDotNVPDismissed={isTryNewDotNVPDismissed}
reportNameValuePairsOrigin={reportNameValuePairs?.origin}
reportNameValuePairsOriginalID={reportNameValuePairs?.originalID}
/>
<ReportActionIndexContext.Provider value={index}>
<ReportActionsListItemRenderer
reportAction={reportAction}
parentReportAction={parentReportAction}
parentReportActionForTransactionThread={EmptyParentReportActionForTransactionThread}
report={reportStable}
transactionThreadReport={transactionThreadReport}
displayAsGroup={displayAsGroup}
shouldDisplayNewMarker={reportAction.reportActionID === unreadMarkerReportActionID}
shouldDisplayReplyDivider={visibleReportActions.length > 1}
isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID}
shouldHideThreadDividerLine
linkedReportActionID={linkedReportActionID}
personalDetails={personalDetails}
userBillingFundID={userBillingFundID}
isReportArchived={isReportArchived}
isTryNewDotNVPDismissed={isTryNewDotNVPDismissed}
reportNameValuePairsOrigin={reportNameValuePairs?.origin}
reportNameValuePairsOriginalID={reportNameValuePairs?.originalID}
/>
</ReportActionIndexContext.Provider>
);
},
[
visibleReportActions,
parentReportAction,
report,
reportStable,
isOffline,
transactionThreadReport,
unreadMarkerReportActionID,
Expand Down
6 changes: 0 additions & 6 deletions src/pages/inbox/report/PureReportActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,6 @@ type PureReportActionItemProps = {
/** Should we display the new marker on top of the comment? */
shouldDisplayNewMarker: boolean;

/** Position index of the report action in the overall report FlatList view */
index: number;

/** Flag to show, hide the thread divider line */
shouldHideThreadDividerLine?: boolean;

Expand Down Expand Up @@ -185,7 +182,6 @@ function PureReportActionItem({
transactionThreadReport,
linkedReportActionID,
displayAsGroup,
index,
parentReportAction,
shouldDisplayNewMarker,
shouldHideThreadDividerLine = false,
Expand Down Expand Up @@ -631,7 +627,6 @@ function PureReportActionItem({
shouldShowBorder={shouldShowBorder}
isOnSearch={isOnSearch}
userBillingFundID={userBillingFundID}
index={index}
setIsPaymentMethodPopoverActive={setIsPaymentMethodPopoverActive}
/>
{Permissions.canUseLinkPreviews() && !isHidden && (action.linkMetadata?.length ?? 0) > 0 && (
Expand Down Expand Up @@ -701,7 +696,6 @@ export default memo(PureReportActionItem, (prevProps, nextProps) => {
prevProps.report?.description === nextProps.report?.description &&
isCompletedTaskReport(prevProps.report) === isCompletedTaskReport(nextProps.report) &&
prevProps.report?.managerID === nextProps.report?.managerID &&
prevProps.index === nextProps.index &&
prevProps.shouldHideThreadDividerLine === nextProps.shouldHideThreadDividerLine &&
prevProps.report?.total === nextProps.report?.total &&
prevProps.report?.nonReimbursableTotal === nextProps.report?.nonReimbursableTotal &&
Expand Down
13 changes: 13 additions & 0 deletions src/pages/inbox/report/ReportActionIndexContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {createContext} from 'react';

/**
* Carries an action item's position index from the list renderer down to the rare consumers that
* actually need it (e.g. `ReportActionItemMessageEdit` for scroll-to-index during edit mode).
*
* Using context keeps `index` out of the prop signatures of every intermediate component, so a
* position shift caused by a new message arriving doesn't cascade re-renders through items that
* never read it. Only components that `useContext(ReportActionIndexContext)` re-render on change.
*/
const ReportActionIndexContext = createContext<number>(0);

export default ReportActionIndexContext;
7 changes: 4 additions & 3 deletions src/pages/inbox/report/ReportActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import useReportTransactions from '@hooks/useReportTransactions';
import {getIOUReportIDFromReportActionPreview, getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {isArchivedNonExpenseReport, isClosedExpenseReportWithNoExpenses} from '@libs/ReportUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import {getStableReportSelector} from '@src/selectors/Report';
import type {PersonalDetailsList, Transaction} from '@src/types/onyx';
import type {PureReportActionItemProps} from './PureReportActionItem';
import PureReportActionItem from './PureReportActionItem';
Expand Down Expand Up @@ -39,7 +40,7 @@ function ReportActionItem({
const reportID = report?.reportID;
const originalReportID = useOriginalReportID(reportID, action);
const isOriginalReportArchived = useReportIsArchived(originalReportID);
const [originalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`);
const [stableOriginalReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${originalReportID}`, {selector: getStableReportSelector});
const [iouReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getIOUReportIDFromReportActionPreview(action)}`);

const transactionsOnIOUReport = useReportTransactions(iouReport?.reportID);
Expand Down Expand Up @@ -68,8 +69,8 @@ function ReportActionItem({
linkedTransactionRouteError={linkedTransactionRouteError}
personalDetails={personalDetails}
originalReportID={originalReportID}
originalReport={originalReport}
isArchivedRoom={isArchivedNonExpenseReport(originalReport, isOriginalReportArchived)}
originalReport={stableOriginalReport}
isArchivedRoom={isArchivedNonExpenseReport(stableOriginalReport, isOriginalReportArchived)}
isClosedExpenseReportWithNoExpenses={isClosedExpenseReportWithNoExpenses(iouReport, transactionsOnIOUReport)}
userBillingFundID={userBillingFundID}
isTryNewDotNVPDismissed={isTryNewDotNVPDismissed}
Expand Down
9 changes: 4 additions & 5 deletions src/pages/inbox/report/ReportActionItemMessageEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
// eslint-disable-next-line no-restricted-imports
import {InteractionManager, View} from 'react-native';
import type {MeasureInWindowOnSuccessCallback, TextInputKeyPressEvent, TextInputScrollEvent} from 'react-native';
Expand Down Expand Up @@ -47,6 +47,7 @@ import Suggestions from './ReportActionCompose/Suggestions';
import useDebouncedCommentMaxLengthValidation from './ReportActionCompose/useDebouncedCommentMaxLengthValidation';
import useEditMessage from './ReportActionCompose/useEditMessage';
import {useReportActionActiveEdit, useReportActionActiveEditActions} from './ReportActionEditMessageContext';
import ReportActionIndexContext from './ReportActionIndexContext';
import shouldUseEmojiPickerSelection from './shouldUseEmojiPickerSelection';
import useDebouncedSaveDraft from './useDebouncedSaveDraft';
import useDraftMessageVideoAttributeCache from './useDraftMessageVideoAttributeCache';
Expand All @@ -64,9 +65,6 @@ type ReportActionItemMessageEditProps = {
/** PolicyID of the policy the report belongs to */
policyID?: string;

/** Position index of the report action in the overall report FlatList view */
index: number;

/** Whether or not the emoji picker is disabled */
shouldDisableEmojiPicker?: boolean;

Expand All @@ -84,7 +82,8 @@ const DEFAULT_MODAL_VALUE = {
isVisible: false,
};

function ReportActionItemMessageEdit({action, reportID, originalReportID, policyID, index, isGroupPolicyReport, shouldDisableEmojiPicker = false, ref}: ReportActionItemMessageEditProps) {
function ReportActionItemMessageEdit({action, reportID, originalReportID, policyID, isGroupPolicyReport, shouldDisableEmojiPicker = false, ref}: ReportActionItemMessageEditProps) {
const index = useContext(ReportActionIndexContext);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve edit index outside list context

ReportActionItemMessageEdit now reads index only from ReportActionIndexContext, whose default value is 0. ReportActionItem is rendered in flows that are not wrapped by that provider (for example src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx and src/components/Search/SearchList/ListItem/ChatListItem.tsx), so those paths will treat every edited action as index 0 and useEditMessage will incorrectly execute scrollToIndex(0) on save/cancel for non-first items, causing scroll jumps/regressed edit behavior compared to the prior explicit index prop plumbing.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ChatListItem: old code was item.index ?? 0, but search never populates item.index, always undefined → always 0. Same as today.
  • DuplicateTransactionItem: renders IOU previews, not ReportActionItemMessageEdit; no report flatListRef in that modal anyway.

const [preferredSkinTone = CONST.EMOJI_DEFAULT_SKIN_TONE] = useOnyx(ONYXKEYS.PREFERRED_EMOJI_SKIN_TONE);
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
Expand Down
5 changes: 0 additions & 5 deletions src/pages/inbox/report/ReportActionItemParentAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ type ReportActionItemParentActionProps = {
/** Flag to show, hide the thread divider line */
shouldHideThreadDividerLine?: boolean;

/** Position index of the report parent action in the overall report FlatList view */
index: number;

/** The id of the report */

reportID: string;
Expand Down Expand Up @@ -77,7 +74,6 @@ function ReportActionItemParentAction({
action,
transactionThreadReport,
parentReportAction,
index = 0,
shouldHideThreadDividerLine = false,
shouldDisplayReplyDivider,
isFirstVisibleReportAction = false,
Expand Down Expand Up @@ -202,7 +198,6 @@ function ReportActionItemParentAction({
action={ancestorReportAction}
displayAsGroup={false}
shouldDisplayNewMarker={ancestor.shouldDisplayNewMarker}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
isThreadReportParentAction
Expand Down
29 changes: 17 additions & 12 deletions src/pages/inbox/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import {getStableReportSelector} from '@src/selectors/Report';
import type * as OnyxTypes from '@src/types/onyx';
import FloatingMessageCounter from './FloatingMessageCounter';
import ReportActionIndexContext from './ReportActionIndexContext';
import ReportActionsListHeader from './ReportActionsListHeader';
import ReportActionsListItemRenderer from './ReportActionsListItemRenderer';
import {getUnreadMarkerReportAction} from './shouldDisplayNewMarkerOnReportAction';
Expand Down Expand Up @@ -212,6 +214,8 @@ function ReportActionsList({
const prevIsLoadingInitialReportActions = usePrevious(reportLoadingState?.isLoadingInitialReportActions);
const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`);

const [reportStable] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, {selector: getStableReportSelector});

const backTo = route?.params?.backTo as string;
const linkedReportActionID = route?.params?.reportActionID;

Expand Down Expand Up @@ -733,13 +737,12 @@ function ReportActionsList({
const safeIndex = actionIndexMap.get(reportAction.reportActionID) ?? index;

return (
<>
<ReportActionIndexContext.Provider value={index}>
<ReportActionsListItemRenderer
reportAction={reportAction}
parentReportAction={parentReportAction}
parentReportActionForTransactionThread={parentReportActionForTransactionThread}
index={index}
report={report}
report={reportStable}
transactionThreadReport={transactionThreadReport}
linkedReportActionID={linkedReportActionID}
displayAsGroup={
Expand All @@ -758,14 +761,16 @@ function ReportActionsList({
reportNameValuePairsOrigin={reportNameValuePairs?.origin}
reportNameValuePairsOriginalID={reportNameValuePairs?.originalID}
/>
<ShowPreviousMessagesButton
reportID={report.reportID}
actionType={reportAction.actionName}
hasPreviousMessages={!!hasPreviousMessages}
showFullHistory={!showHiddenHistory}
onPress={onShowPreviousMessages}
/>
</>
{!!reportStable?.reportID && (
<ShowPreviousMessagesButton
reportID={reportStable.reportID}
actionType={reportAction.actionName}
hasPreviousMessages={!!hasPreviousMessages}
showFullHistory={!showHiddenHistory}
onPress={onShowPreviousMessages}
/>
)}
</ReportActionIndexContext.Provider>
);
},
[
Expand All @@ -781,7 +786,7 @@ function ReportActionsList({
parentReportActionForTransactionThread,
personalDetailsList,
renderedVisibleReportActions,
report,
reportStable,
reportNameValuePairs?.origin,
reportNameValuePairs?.originalID,
shouldHideThreadDividerLine,
Expand Down
6 changes: 0 additions & 6 deletions src/pages/inbox/report/ReportActionsListItemRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ type ReportActionsListItemRendererProps = {
/** The transaction thread report's parentReportAction */
parentReportActionForTransactionThread: OnyxEntry<ReportAction>;

/** Position index of the report action in the overall report FlatList view */
index: number;

/** Report for this action */
report: OnyxEntry<Report>;

Expand Down Expand Up @@ -71,7 +68,6 @@ type ReportActionsListItemRendererProps = {
function ReportActionsListItemRenderer({
reportAction,
parentReportAction,
index,
report,
transactionThreadReport,
displayAsGroup,
Expand Down Expand Up @@ -175,7 +171,6 @@ function ReportActionsListItemRenderer({
report={report}
action={action}
transactionThreadReport={transactionThreadReport}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
personalDetails={personalDetails}
Expand All @@ -197,7 +192,6 @@ function ReportActionsListItemRenderer({
linkedReportActionID={linkedReportActionID}
displayAsGroup={displayAsGroup}
shouldDisplayNewMarker={shouldDisplayNewMarker}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
shouldHighlight={shouldHighlight}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ type ActionContentRouterProps = {
/** User payment card ID */
userBillingFundID?: number;

/** Position index of the report action in the overall report FlatList view */
index: number;

/** Toggle whether the payment method popover is active */
setIsPaymentMethodPopoverActive: (value: boolean) => void;
};
Expand All @@ -156,7 +153,6 @@ function ActionContentRouter({
shouldShowBorder,
isOnSearch,
userBillingFundID,
index,
setIsPaymentMethodPopoverActive,
}: ActionContentRouterProps): React.JSX.Element | null {
const {translate, formatTravelDate} = useLocalize();
Expand Down Expand Up @@ -487,7 +483,6 @@ function ActionContentRouter({
originalReportID={originalReportID}
displayAsGroup={displayAsGroup}
draftMessage={draftMessage}
index={index}
isHidden={isHidden}
updateHiddenState={updateHiddenState}
isArchivedRoom={isArchivedRoom}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ type ChatMessageContentProps = {
originalReportID: string;
displayAsGroup: boolean;
draftMessage: string | undefined;
index: number;
isHidden: boolean;
updateHiddenState: (isHiddenValue: boolean) => void;
isArchivedRoom?: boolean;
Expand All @@ -50,7 +49,6 @@ function ChatMessageContent({
originalReportID,
displayAsGroup,
draftMessage,
index,
isHidden,
updateHiddenState,
isArchivedRoom,
Expand Down Expand Up @@ -87,7 +85,6 @@ function ChatMessageContent({
reportID={reportID}
originalReportID={originalReportID}
policyID={report?.policyID}
index={index}
shouldDisableEmojiPicker={(chatIncludesConcierge(report) && isBlockedFromConcierge(blockedFromConcierge)) || isArchivedNonExpenseReport(report, isArchivedRoom)}
isGroupPolicyReport={!!report?.policyID && report.policyID !== CONST.POLICY.ID_FAKE}
/>
Expand Down
Loading
Loading