Skip to content
Merged
Changes from all commits
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
24 changes: 20 additions & 4 deletions src/pages/inbox/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -695,11 +695,26 @@ function ReportActionsList({
return isExpenseReport(report) || isIOUReport(report) || isInvoiceReport(report);
}, [parentReportAction, report, sortedVisibleReportActions]);

// Precompute a reportActionID → index map so renderItem can resolve the real index in O(1)
// instead of scanning sortedVisibleReportActions with indexOf on every render.
const actionIndexMap = useMemo(() => {
const map = new Map<string, number>();
for (const [i, action] of sortedVisibleReportActions.entries()) {
map.set(action.reportActionID, i);
}
return map;
}, [sortedVisibleReportActions]);

const renderItem = useCallback(
({item: reportAction, index}: ListRenderItemInfo<OnyxTypes.ReportAction>) => {
const originalReportID = getOriginalReportID(report.reportID, reportAction, reportActionsFromOnyx);
const showPreviousMessagesButton = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && !!isConciergeSidePanel && !!showHiddenHistory && !!hasPreviousMessages;

// Use the action's actual index in sortedVisibleReportActions rather than the FlashList-provided index,
// because useFlashListScrollKey may slice the data for deep-link scroll positioning, making the
// FlashList index offset from the full array and causing wrong displayAsGroup computation.
const safeIndex = actionIndexMap.get(reportAction.reportActionID) ?? index;

return (
<>
<ReportActionsListItemRenderer
Expand All @@ -711,8 +726,8 @@ function ReportActionsList({
transactionThreadReport={transactionThreadReport}
linkedReportActionID={linkedReportActionID}
displayAsGroup={
!isConsecutiveChronosAutomaticTimerAction(sortedVisibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID), isOffline) &&
isConsecutiveActionMadeByPreviousActor(sortedVisibleReportActions, index, isOffline)
!isConsecutiveChronosAutomaticTimerAction(sortedVisibleReportActions, safeIndex, chatIncludesChronosWithID(reportAction?.reportID), isOffline) &&
isConsecutiveActionMadeByPreviousActor(sortedVisibleReportActions, safeIndex, isOffline)
}
shouldHideThreadDividerLine={shouldHideThreadDividerLine}
shouldDisplayNewMarker={reportAction.reportActionID === unreadMarkerReportActionID}
Expand Down Expand Up @@ -754,6 +769,7 @@ function ReportActionsList({
isOffline,
transactionThreadReport,
linkedReportActionID,
actionIndexMap,
sortedVisibleReportActions,
shouldHideThreadDividerLine,
unreadMarkerReportActionID,
Expand Down Expand Up @@ -847,14 +863,14 @@ function ReportActionsList({
<View key={action.reportActionID}>
{renderItem({
item: action,
index: sortedVisibleReportActions.indexOf(action),
index: actionIndexMap.get(action.reportActionID) ?? 0,
} as ListRenderItemInfo<OnyxTypes.ReportAction>)}
Comment on lines 865 to 867

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 global action index in static preview

renderTopReportActions() now passes the preview-local index instead of the action’s index in sortedVisibleReportActions, but this value is forwarded as index to ReportActionsListItemRenderer and ultimately used by ReportActionItemMessageEdit (e.g., deleteDraft treats index === 0 as a special “latest message” case when scrolling). In StaticReportActionsPreview (shown while shouldScrollToEndAfterLayout is true), the sliced/reversed preview indices do not match global list indices, so dismissing/saving a draft can scroll to the wrong item or skip the expected scroll behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in cceddfb

</View>
))}
</StaticReportActionsPreview>
</>
);
}, [hideComposer, initialNumToRender, renderItem, shouldShowReportRecipientLocalTime, sortedVisibleReportActions, styles]);
}, [actionIndexMap, hideComposer, initialNumToRender, renderItem, shouldShowReportRecipientLocalTime, sortedVisibleReportActions, styles]);

const onStartReached = useCallback(() => {
if (!isSearchTopmostFullScreenRoute()) {
Expand Down
Loading