Skip to content
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 @@ -551,28 +554,29 @@ 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}
isHarvestCreatedExpenseReport={shouldShowHarvestCreatedAction}
/>
<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}
isHarvestCreatedExpenseReport={shouldShowHarvestCreatedAction}
/>
</ReportActionIndexContext.Provider>
);
},
[
visibleReportActions,
parentReportAction,
report,
reportStable,
isOffline,
transactionThreadReport,
unreadMarkerReportActionID,
Expand Down
12 changes: 7 additions & 5 deletions src/components/Search/SearchList/ListItem/ChatListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import FS from '@libs/Fullstory';
import ReportActionItem from '@pages/inbox/report/ReportActionItem';
import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import {getStableReportSelector} from '@src/selectors/Report';
import type {ChatListItemProps, ReportActionListItemType} from './types';

/**
Expand All @@ -28,7 +29,7 @@ function ChatListItem<TItem extends ListItem>({
shouldSyncFocus,
}: ChatListItemProps<TItem>) {
const reportActionItem = item as unknown as ReportActionListItemType;
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportActionItem?.reportID}`);
const [reportStable] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportActionItem?.reportID}`, {selector: getStableReportSelector});
const personalDetails = usePersonalDetails();
const styles = useThemeStyles();
const theme = useTheme();
Expand All @@ -50,7 +51,9 @@ function ChatListItem<TItem extends ListItem>({
item.cursorStyle,
];

const fsClass = FS.getChatFSClass(report);
const fsClass = FS.getChatFSClass(reportStable);

const handlePress = () => onSelectRow(item);

return (
<BaseListItem
Expand All @@ -75,12 +78,11 @@ function ChatListItem<TItem extends ListItem>({
>
<ReportActionItem
action={reportActionItem}
report={report}
onPress={() => onSelectRow(item)}
report={reportStable}
onPress={handlePress}
parentReportAction={undefined}
displayAsGroup={false}
shouldDisplayNewMarker={false}
index={item.index ?? 0}
isFirstVisibleReportAction={false}
shouldDisplayContextMenu={false}
shouldShowBorder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ function DebugReportActionCreatePage({
parentReportAction={undefined}
displayAsGroup={false}
shouldDisplayNewMarker={false}
index={0}
isFirstVisibleReportAction={false}
shouldDisplayContextMenu={false}
personalDetails={personalDetailsList}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ function DebugReportActionPreview({reportAction, reportID}: DebugReportActionPre
parentReportAction={undefined}
displayAsGroup={false}
shouldDisplayNewMarker={false}
index={0}
isFirstVisibleReportAction={false}
shouldDisplayContextMenu={false}
personalDetails={personalDetails}
Expand Down
17 changes: 8 additions & 9 deletions src/pages/TransactionDuplicate/DuplicateTransactionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,29 @@ import ReportActionItem from '@pages/inbox/report/ReportActionItem';
import {ReportActionItemActionsContext, ReportActionItemStateContext} from '@pages/inbox/report/ReportActionItemContext';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {getStableReportSelector} from '@src/selectors/Report';
import type {Transaction} from '@src/types/onyx';

type DuplicateTransactionItemProps = {
transaction: OnyxEntry<Transaction>;
index: number;
onPreviewPressed: (reportID: string) => void;
};

const linkedTransactionRouteErrorSelector = (transaction: OnyxEntry<Transaction>) => transaction?.errorFields?.route ?? null;

function DuplicateTransactionItem({transaction, index, onPreviewPressed}: DuplicateTransactionItemProps) {
function DuplicateTransactionItem({transaction, onPreviewPressed}: DuplicateTransactionItemProps) {
const styles = useThemeStyles();
const personalDetails = usePersonalDetails();

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

const action = Object.values(reportActions ?? {})?.find((reportAction) => {
const IOUTransactionID = isMoneyRequestAction(reportAction) ? getOriginalMessage(reportAction)?.IOUTransactionID : CONST.DEFAULT_NUMBER_ID;
return IOUTransactionID === transaction?.transactionID;
});

const originalReportID = getOriginalReportID(report?.reportID, action, reportActions);
const originalReportID = getOriginalReportID(reportStable?.reportID, action, reportActions);

const [draftMessage] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_DRAFTS}${originalReportID}`);

Expand All @@ -47,7 +47,7 @@ function DuplicateTransactionItem({transaction, index, onPreviewPressed}: Duplic
const stateValue = useMemo(() => ({shouldOpenReportInRHP: true}), []);
const actionsValue = useMemo(() => ({onPreviewPressed}), [onPreviewPressed]);

if (!action || !report) {
if (!action || !reportStable) {
return null;
}

Expand All @@ -60,9 +60,8 @@ function DuplicateTransactionItem({transaction, index, onPreviewPressed}: Duplic
<ReportActionItemActionsContext.Provider value={actionsValue}>
<ReportActionItem
action={action}
report={report}
parentReportAction={getReportAction(report?.parentReportID, report?.parentReportActionID)}
index={index}
report={reportStable}
parentReportAction={getReportAction(reportStable?.parentReportID, reportStable?.parentReportActionID)}
displayAsGroup={false}
shouldDisplayNewMarker={false}
isFirstVisibleReportAction={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ function DuplicateTransactionsList({transactions, onPreviewPressed}: DuplicateTr
const styles = useThemeStyles();

const renderItem = useCallback(
({item, index}: ListRenderItemInfo<OnyxEntry<Transaction>>) => (
({item}: ListRenderItemInfo<OnyxEntry<Transaction>>) => (
<DuplicateTransactionItem
transaction={item}
index={index}
onPreviewPressed={onPreviewPressed}
/>
),
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 @@ -102,9 +102,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 @@ -166,7 +163,6 @@ function PureReportActionItem({
transactionThreadReport,
linkedReportActionID,
displayAsGroup,
index,
parentReportAction,
shouldDisplayNewMarker,
shouldHideThreadDividerLine = false,
Expand Down Expand Up @@ -608,7 +604,6 @@ function PureReportActionItem({
personalDetails={personalDetails}
shouldShowBorder={shouldShowBorder}
isOnSearch={isOnSearch}
index={index}
setIsPaymentMethodPopoverActive={setIsPaymentMethodPopoverActive}
/>
{Permissions.canUseLinkPreviews() && !isHidden && (action.linkMetadata?.length ?? 0) > 0 && (
Expand Down Expand Up @@ -679,7 +674,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;
5 changes: 3 additions & 2 deletions src/pages/inbox/report/ReportActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useReportTransactions from '@hooks/useReportTransactions';
import {getIOUReportIDFromReportActionPreview, getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {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 All @@ -22,7 +23,7 @@ type ReportActionItemProps = PureReportActionItemProps & {
function ReportActionItem({action, report, draftMessage: draftMessageProp, personalDetails, linkedTransactionRouteError: linkedTransactionRouteErrorProp, ...props}: ReportActionItemProps) {
const reportID = report?.reportID;
const originalReportID = useOriginalReportID(reportID, action);
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 @@ -51,7 +52,7 @@ function ReportActionItem({action, report, draftMessage: draftMessageProp, perso
linkedTransactionRouteError={linkedTransactionRouteError}
personalDetails={personalDetails}
originalReportID={originalReportID}
originalReport={originalReport}
originalReport={stableOriginalReport}
isClosedExpenseReportWithNoExpenses={isClosedExpenseReportWithNoExpenses(iouReport, transactionsOnIOUReport)}
/>
);
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 @@ -52,6 +52,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 @@ -69,9 +70,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;

/** Reference to the outer element */
ref?: React.Ref<ComposerRef | undefined>;
};
Expand All @@ -83,7 +81,8 @@ const DEFAULT_MODAL_VALUE = {
isVisible: false,
};

function ReportActionItemMessageEdit({action, reportID, originalReportID, policyID, index, ref}: ReportActionItemMessageEditProps) {
function ReportActionItemMessageEdit({action, reportID, originalReportID, policyID, 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 [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(reportID)}`);
const isOriginalReportArchived = useReportIsArchived(originalReportID);
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 @@ -35,9 +35,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 @@ -69,7 +66,6 @@ function ReportActionItemParentAction({
action,
transactionThreadReport,
parentReportAction,
index = 0,
shouldHideThreadDividerLine = false,
shouldDisplayReplyDivider,
isFirstVisibleReportAction = false,
Expand Down Expand Up @@ -193,7 +189,6 @@ function ReportActionItemParentAction({
action={ancestorReportAction}
displayAsGroup={false}
shouldDisplayNewMarker={ancestor.shouldDisplayNewMarker}
index={index}
isFirstVisibleReportAction={isFirstVisibleReportAction}
shouldUseThreadDividerLine={shouldUseThreadDividerLine}
isThreadReportParentAction
Expand Down
Loading
Loading