From e0aa222ab40eb0af022a9e4c543b66781947c52f Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Sun, 15 Jun 2025 17:01:05 +0100
Subject: [PATCH 01/18] chore: add report skeleton when transaction data is
 available

---
 src/ROUTES.ts                                 |  10 ++
 .../MoneyRequestReportTransactionList.tsx     |   2 +-
 src/libs/Navigation/types.ts                  |   2 +
 src/pages/home/ReportScreen.tsx               | 102 ++++++++++--------
 src/pages/home/report/ReportActionsView.tsx   |  25 +++--
 5 files changed, 87 insertions(+), 54 deletions(-)

diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index b32c27c48144..e82b83df2b4e 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -81,6 +81,8 @@ const ROUTES = {
         getRoute: ({
             reportID,
             reportActionID,
+            parentReportID,
+            parentReportActionID,
             backTo,
             moneyRequestReportActionID,
             transactionID,
@@ -106,6 +108,14 @@ const ROUTES = {
                 queryParams.push(`moneyRequestReportActionID=${moneyRequestReportActionID}`);
             }
 
+            if (parentReportID) {
+                queryParams.push(`parentReportID=${parentReportID}`);
+            }
+
+            if (parentReportActionID) {
+                queryParams.push(`parentReportActionID=${parentReportActionID}`);
+            }
+
             const queryString = queryParams.length > 0 ? (`${baseRoute}?${queryParams.join('&')}` as const) : baseRoute;
             return getUrlWithBackToParam(queryString, backTo);
         },
diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
index 832edf869957..37080fd00116 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
@@ -203,7 +203,7 @@ function MoneyRequestReportTransactionList({
             const sortedSiblingTransactionReportIDs = getThreadReportIDsForTransactions(reportActions, sortedTransactions);
             setActiveTransactionThreadIDs(sortedSiblingTransactionReportIDs);
 
-            Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo}));
+            Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo, parentReportID: activeTransaction.reportID, parentReportActionID: iouAction?.reportActionID}));
         },
         [reportActions, sortedTransactions],
     );
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index de7a521f6ad5..1a657e6041c2 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -1758,6 +1758,8 @@ type ReportsSplitNavigatorParamList = {
     [SCREENS.REPORT]: {
         reportID: string;
         reportActionID?: string;
+        parentReportID?: string;
+        parentReportActionID?: string;
         openOnAdminRoom?: boolean;
         referrer?: string;
         backTo?: Routes;
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index c0ded2f99766..ea28bbf51cd0 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -135,6 +135,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
     const {translate} = useLocalize();
     const reportIDFromRoute = getNonEmptyStringOnyxID(route.params?.reportID);
     const reportActionIDFromRoute = route?.params?.reportActionID;
+    const parentReportIDFromRoute = route?.params?.parentReportID;
+    const parentReportActionIDFromRoute = route?.params?.parentReportActionID;
     const isFocused = useIsFocused();
     const prevIsFocused = usePrevious(isFocused);
     const firstRenderRef = useRef(true);
@@ -155,9 +157,9 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
     const [reportNameValuePairsOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportIDFromRoute}`, {allowStaleData: true, canBeMissing: true});
     const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, {canBeMissing: true, allowStaleData: true});
     const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, initialValue: {}, canBeMissing: false});
-    const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(reportOnyx?.parentReportID)}`, {
+    const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(reportOnyx?.parentReportID ?? parentReportIDFromRoute)}`, {
         canEvict: false,
-        selector: (parentReportActions) => getParentReportAction(parentReportActions, reportOnyx?.parentReportActionID),
+        selector: (parentReportActions) => getParentReportAction(parentReportActions, reportOnyx?.parentReportActionID ?? parentReportActionIDFromRoute),
         canBeMissing: true,
     });
     const deletedParentAction = isDeletedParentAction(parentReportAction);
@@ -213,48 +215,55 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
      */
     const report = useMemo(
         () =>
-            reportOnyx && {
-                lastReadTime: reportOnyx.lastReadTime,
-                reportID: reportOnyx.reportID,
-                policyID: reportOnyx.policyID,
-                lastVisibleActionCreated: reportOnyx.lastVisibleActionCreated,
-                statusNum: reportOnyx.statusNum,
-                stateNum: reportOnyx.stateNum,
-                writeCapability: reportOnyx.writeCapability,
-                type: reportOnyx.type,
-                errorFields: reportOnyx.errorFields,
-                parentReportID: reportOnyx.parentReportID,
-                parentReportActionID: reportOnyx.parentReportActionID,
-                chatType: reportOnyx.chatType,
-                pendingFields: reportOnyx.pendingFields,
-                isDeletedParentAction: reportOnyx.isDeletedParentAction,
-                reportName: reportOnyx.reportName,
-                description: reportOnyx.description,
-                managerID: reportOnyx.managerID,
-                total: reportOnyx.total,
-                nonReimbursableTotal: reportOnyx.nonReimbursableTotal,
-                fieldList: reportOnyx.fieldList,
-                ownerAccountID: reportOnyx.ownerAccountID,
-                currency: reportOnyx.currency,
-                unheldTotal: reportOnyx.unheldTotal,
-                unheldNonReimbursableTotal: reportOnyx.unheldNonReimbursableTotal,
-                participants: reportOnyx.participants,
-                isWaitingOnBankAccount: reportOnyx.isWaitingOnBankAccount,
-                iouReportID: reportOnyx.iouReportID,
-                isOwnPolicyExpenseChat: reportOnyx.isOwnPolicyExpenseChat,
-                isPinned: reportOnyx.isPinned,
-                chatReportID: reportOnyx.chatReportID,
-                visibility: reportOnyx.visibility,
-                oldPolicyName: reportOnyx.oldPolicyName,
-                policyName: reportOnyx.policyName,
-                private_isArchived: reportNameValuePairsOnyx?.private_isArchived,
-                lastMentionedTime: reportOnyx.lastMentionedTime,
-                avatarUrl: reportOnyx.avatarUrl,
-                permissions,
-                invoiceReceiver: reportOnyx.invoiceReceiver,
-                policyAvatar: reportOnyx.policyAvatar,
-            },
-        [reportOnyx, reportNameValuePairsOnyx, permissions],
+            parentReportIDFromRoute && parentReportActionIDFromRoute
+                ? ({
+                      ...reportOnyx,
+                      reportID: reportIDFromRoute,
+                      parentReportID: parentReportIDFromRoute,
+                      parentReportActionID: parentReportActionIDFromRoute,
+                  } as OnyxTypes.Report)
+                : reportOnyx && {
+                      lastReadTime: reportOnyx.lastReadTime,
+                      reportID: reportOnyx.reportID,
+                      policyID: reportOnyx.policyID,
+                      lastVisibleActionCreated: reportOnyx.lastVisibleActionCreated,
+                      statusNum: reportOnyx.statusNum,
+                      stateNum: reportOnyx.stateNum,
+                      writeCapability: reportOnyx.writeCapability,
+                      type: reportOnyx.type,
+                      errorFields: reportOnyx.errorFields,
+                      parentReportID: reportOnyx.parentReportID,
+                      parentReportActionID: reportOnyx.parentReportActionID,
+                      chatType: reportOnyx.chatType,
+                      pendingFields: reportOnyx.pendingFields,
+                      isDeletedParentAction: reportOnyx.isDeletedParentAction,
+                      reportName: reportOnyx.reportName,
+                      description: reportOnyx.description,
+                      managerID: reportOnyx.managerID,
+                      total: reportOnyx.total,
+                      nonReimbursableTotal: reportOnyx.nonReimbursableTotal,
+                      fieldList: reportOnyx.fieldList,
+                      ownerAccountID: reportOnyx.ownerAccountID,
+                      currency: reportOnyx.currency,
+                      unheldTotal: reportOnyx.unheldTotal,
+                      unheldNonReimbursableTotal: reportOnyx.unheldNonReimbursableTotal,
+                      participants: reportOnyx.participants,
+                      isWaitingOnBankAccount: reportOnyx.isWaitingOnBankAccount,
+                      iouReportID: reportOnyx.iouReportID,
+                      isOwnPolicyExpenseChat: reportOnyx.isOwnPolicyExpenseChat,
+                      isPinned: reportOnyx.isPinned,
+                      chatReportID: reportOnyx.chatReportID,
+                      visibility: reportOnyx.visibility,
+                      oldPolicyName: reportOnyx.oldPolicyName,
+                      policyName: reportOnyx.policyName,
+                      private_isArchived: reportNameValuePairsOnyx?.private_isArchived,
+                      lastMentionedTime: reportOnyx.lastMentionedTime,
+                      avatarUrl: reportOnyx.avatarUrl,
+                      permissions,
+                      invoiceReceiver: reportOnyx.invoiceReceiver,
+                      policyAvatar: reportOnyx.policyAvatar,
+                  },
+        [parentReportIDFromRoute, parentReportActionIDFromRoute, reportIDFromRoute, reportOnyx, reportNameValuePairsOnyx?.private_isArchived, permissions],
     );
     const reportID = report?.reportID;
 
@@ -819,15 +828,16 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                                 testID="report-actions-view-wrapper"
                             >
                                 {(!report || shouldWaitForTransactions) && <ReportActionsSkeletonView />}
-                                {!!report && !shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? (
+                                {!shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? (
                                     <ReportActionsView
                                         report={report}
                                         reportActions={reportActions}
-                                        isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions}
+                                        isLoadingInitialReportActions={reportMetadata?.isLoadingInitialReportActions && !isTransactionThreadView}
                                         hasNewerActions={hasNewerActions}
                                         hasOlderActions={hasOlderActions}
                                         parentReportAction={parentReportAction}
                                         transactionThreadReportID={transactionThreadReportID}
+                                        isReportTransactionThread={isTransactionThreadView}
                                     />
                                 ) : null}
                                 {!!report && shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? (
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index c0679a28f2b1..74c21f4168d3 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -61,6 +61,8 @@ type ReportActionsViewProps = {
 
     /** If the report has older actions to load */
     hasOlderActions: boolean;
+
+    isReportTransactionThread?: boolean;
 };
 
 let listOldID = Math.round(Math.random() * 100);
@@ -73,6 +75,7 @@ function ReportActionsView({
     transactionThreadReportID,
     hasNewerActions,
     hasOlderActions,
+    isReportTransactionThread,
 }: ReportActionsViewProps) {
     useCopySelectionHelper();
     const route = useRoute<PlatformStackRouteProp<ReportsSplitNavigatorParamList, typeof SCREENS.REPORT>>();
@@ -130,6 +133,12 @@ function ReportActionsView({
     // to display at least one expense action to match the total data.
     const reportActionsToDisplay = useMemo(() => {
         if (!isMoneyRequestReport(report) || !allReportActions?.length) {
+            if (!allReportActions?.length && isReportTransactionThread) {
+                const optimisticCreatedReportAction = buildOptimisticCreatedReportAction(CONST.REPORT.OWNER_EMAIL_FAKE);
+                optimisticCreatedReportAction.pendingAction = null;
+                return [optimisticCreatedReportAction];
+            }
+
             return allReportActions;
         }
 
@@ -196,13 +205,15 @@ function ReportActionsView({
     const canPerformWriteAction = canUserPerformWriteAction(report);
     const visibleReportActions = useMemo(
         () =>
-            reportActions.filter(
-                (reportAction) =>
-                    (isOffline || isDeletedParentAction(reportAction) || reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || reportAction.errors) &&
-                    shouldReportActionBeVisible(reportAction, reportAction.reportActionID, canPerformWriteAction) &&
-                    isIOUActionMatchingTransactionList(reportAction, reportTransactionIDs),
-            ),
-        [reportActions, isOffline, canPerformWriteAction, reportTransactionIDs],
+            isReportTransactionThread
+                ? reportActions
+                : reportActions.filter(
+                      (reportAction) =>
+                          (isOffline || isDeletedParentAction(reportAction) || reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || reportAction.errors) &&
+                          shouldReportActionBeVisible(reportAction, reportAction.reportActionID, canPerformWriteAction) &&
+                          isIOUActionMatchingTransactionList(reportAction, reportTransactionIDs),
+                  ),
+        [isReportTransactionThread, reportActions, isOffline, canPerformWriteAction, reportTransactionIDs],
     );
 
     const newestReportAction = useMemo(() => reportActions?.at(0), [reportActions]);

From e24c81977d44f7eebaf847882c2f924965c52a66 Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Sun, 15 Jun 2025 18:36:33 +0100
Subject: [PATCH 02/18] fix: missing transaction thread report name

---
 src/pages/home/ReportScreen.tsx | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index ea28bbf51cd0..bec34adfb508 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -205,6 +205,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
         return '';
     }, [accountManagerReportID, accountManagerReport, personalDetails, translate]);
 
+    const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReportIDFromRoute}`, {canBeMissing: true});
+
     /**
      * Create a lightweight Report so as to keep the re-rendering as light as possible by
      * passing in only the required props.
@@ -221,6 +223,8 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                       reportID: reportIDFromRoute,
                       parentReportID: parentReportIDFromRoute,
                       parentReportActionID: parentReportActionIDFromRoute,
+                      policyID: parentReport?.policyID,
+                      type: CONST.REPORT.TYPE.CHAT,
                   } as OnyxTypes.Report)
                 : reportOnyx && {
                       lastReadTime: reportOnyx.lastReadTime,
@@ -263,7 +267,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                       invoiceReceiver: reportOnyx.invoiceReceiver,
                       policyAvatar: reportOnyx.policyAvatar,
                   },
-        [parentReportIDFromRoute, parentReportActionIDFromRoute, reportIDFromRoute, reportOnyx, reportNameValuePairsOnyx?.private_isArchived, permissions],
+        [parentReportIDFromRoute, parentReportActionIDFromRoute, reportOnyx, reportIDFromRoute, parentReport?.policyID, reportNameValuePairsOnyx?.private_isArchived, permissions],
     );
     const reportID = report?.reportID;
 
@@ -314,7 +318,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
     const [transactionThreadReportActions = {}] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`, {canBeMissing: true});
     const combinedReportActions = getCombinedReportActions(reportActions, transactionThreadReportID ?? null, Object.values(transactionThreadReportActions));
     const lastReportAction = [...combinedReportActions, parentReportAction].find((action) => canEditReportAction(action) && !isMoneyRequestAction(action));
-    const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`];
+    const policy = policies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID ?? parentReport?.policyID}`];
     const isTopMostReportId = currentReportIDValue?.currentReportID === reportIDFromRoute;
     const didSubscribeToReportLeavingEvents = useRef(false);
     const isTransactionThreadView = isReportTransactionThread(report);

From 000f95c0404a4675f7d214719de76f626647decb Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Sun, 15 Jun 2025 19:15:30 +0100
Subject: [PATCH 03/18] fix: app crash when navigating between transaction
 threads

---
 src/pages/home/ReportScreen.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index bec34adfb508..7c0cf1207288 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -832,7 +832,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
                                 testID="report-actions-view-wrapper"
                             >
                                 {(!report || shouldWaitForTransactions) && <ReportActionsSkeletonView />}
-                                {!shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? (
+                                {!!report && !shouldDisplayMoneyRequestActionsList && !shouldWaitForTransactions ? (
                                     <ReportActionsView
                                         report={report}
                                         reportActions={reportActions}

From 633e0ecb38fd0961e65904457a324927acad39fe Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Sun, 15 Jun 2025 19:15:44 +0100
Subject: [PATCH 04/18] chore: add missing type declarations

---
 src/ROUTES.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index e82b83df2b4e..a82dd8271e37 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -89,6 +89,8 @@ const ROUTES = {
         }: {
             reportID: string | undefined;
             reportActionID?: string;
+            parentReportID?: string;
+            parentReportActionID?: string;
             backTo?: string;
             moneyRequestReportActionID?: string;
             transactionID?: string;

From 9f49b63b957565921670b7a18b697de831c8b940 Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Sun, 15 Jun 2025 20:18:06 +0100
Subject: [PATCH 05/18] fix: transaction thread navigation issues

---
 src/components/MoneyRequestHeader.tsx         | 12 +++++-
 ...neyRequestReportTransactionsNavigation.tsx | 41 ++++++++++++++-----
 src/pages/home/ReportScreen.tsx               |  1 +
 3 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 5af8ba1b6a3e..ebf34d505639 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -59,11 +59,14 @@ type MoneyRequestHeaderProps = {
     /** The report action the transaction is tied to from the parent report */
     parentReportAction: OnyxEntry<ReportAction>;
 
+    /** The report ID to navigate back to */
+    backTo?: string;
+
     /** Method to trigger when pressing close button of the header */
     onBackButtonPress: () => void;
 };
 
-function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPress}: MoneyRequestHeaderProps) {
+function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackButtonPress}: MoneyRequestHeaderProps) {
     // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout to use a correct layout for the hold expense modal https://github.com/Expensify/App/pull/47990#issuecomment-2362382026
     // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
     const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout();
@@ -282,7 +285,12 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
                         )}
                     </View>
                 )}
-                {shouldDisplayTransactionNavigation && <MoneyRequestReportTransactionsNavigation currentReportID={reportID} />}
+                {shouldDisplayTransactionNavigation && (
+                    <MoneyRequestReportTransactionsNavigation
+                        report={report}
+                        backTo={backTo}
+                    />
+                )}
             </HeaderWithBackButton>
             {shouldUseNarrowLayout && (
                 <View style={[styles.flexRow, styles.gap2, styles.pb3, styles.ph5, styles.w100, styles.alignItemsCenter, styles.justifyContentCenter]}>
diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
index c4d9f5fb053f..004866b9e6f6 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
@@ -1,5 +1,7 @@
 import {findFocusedRoute} from '@react-navigation/native';
 import React, {useEffect} from 'react';
+import {useOnyx} from 'react-native-onyx';
+import type {OnyxEntry} from 'react-native-onyx';
 import Icon from '@components/Icon';
 import * as Expensicons from '@components/Icon/Expensicons';
 import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
@@ -8,34 +10,45 @@ import useThemeStyles from '@hooks/useThemeStyles';
 import Navigation from '@navigation/Navigation';
 import navigationRef from '@navigation/navigationRef';
 import CONST from '@src/CONST';
+import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import SCREENS from '@src/SCREENS';
+import type {Report} from '@src/types/onyx';
 import {clearActiveTransactionThreadIDs, getActiveTransactionThreadIDs} from './TransactionThreadReportIDRepository';
 
 type MoneyRequestReportRHPNavigationButtonsProps = {
-    currentReportID: string;
+    /** The report for which we are displaying the navigation buttons */
+    report: OnyxEntry<Report>;
+
+    /** The route to navigate back to when the user clicks on the back button */
+    backTo?: string;
 };
 
-function MoneyRequestReportTransactionsNavigation({currentReportID}: MoneyRequestReportRHPNavigationButtonsProps) {
+function MoneyRequestReportTransactionsNavigation({report, backTo}: MoneyRequestReportRHPNavigationButtonsProps) {
     const styles = useThemeStyles();
     const theme = useTheme();
 
     const reportIDsList = getActiveTransactionThreadIDs();
-    const {prevReportID, nextReportID} = (() => {
+    const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID}`, {
+        canBeMissing: true,
+        selector: (reportActions) => Object.values(reportActions ?? {}),
+    });
+
+    const {prevReportID, prevParentReportActionID, nextReportID, nextParentReportActionID} = (() => {
         if (!reportIDsList) {
-            return {prevReportID: undefined, nextReportID: undefined};
+            return {prevReportID: undefined, prevParentReportActionID: undefined, nextReportID: undefined, nextParentReportActionID: undefined};
         }
 
-        const currentReportIndex = reportIDsList.findIndex((id) => id === currentReportID);
+        const currentReportIndex = reportIDsList.findIndex((id) => id === report?.reportID);
 
         const prevID = currentReportIndex > 0 ? reportIDsList.at(currentReportIndex - 1) : undefined;
         const nextID = currentReportIndex <= reportIDsList.length - 1 ? reportIDsList.at(currentReportIndex + 1) : undefined;
+        const prevReportActionID = currentReportIndex > 0 ? parentReportActions?.find((action) => action.childReportID === prevID)?.reportActionID : undefined;
+        const nextReportActionID = currentReportIndex <= reportIDsList.length - 1 ? parentReportActions?.find((action) => action.childReportID === nextID)?.reportActionID : undefined;
 
-        return {prevReportID: prevID, nextReportID: nextID};
+        return {prevReportID: prevID, prevParentReportActionID: prevReportActionID, nextReportID: nextID, nextParentReportActionID: nextReportActionID};
     })();
 
-    const backTo = Navigation.getActiveRoute();
-
     /**
      * We clear the sibling transactionThreadIDs when unmounting this component
      * only when the mount actually goes to a different SCREEN (and not a different version of the same SCREEN)
@@ -76,7 +89,12 @@ function MoneyRequestReportTransactionsNavigation({currentReportID}: MoneyReques
                 style={pressableStyle}
                 onPress={(e) => {
                     e?.preventDefault();
-                    Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: prevReportID, backTo}), {forceReplace: true});
+                    Navigation.navigate(
+                        ROUTES.SEARCH_REPORT.getRoute({reportID: prevReportID, parentReportActionID: prevParentReportActionID, parentReportID: report?.parentReportID, backTo}),
+                        {
+                            forceReplace: true,
+                        },
+                    );
                 }}
             >
                 <Icon
@@ -94,7 +112,10 @@ function MoneyRequestReportTransactionsNavigation({currentReportID}: MoneyReques
                 style={pressableStyle}
                 onPress={(e) => {
                     e?.preventDefault();
-                    Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: nextReportID, backTo}), {forceReplace: true});
+                    Navigation.navigate(
+                        ROUTES.SEARCH_REPORT.getRoute({reportID: nextReportID, parentReportActionID: nextParentReportActionID, parentReportID: report?.parentReportID, backTo}),
+                        {forceReplace: true},
+                    );
                 }}
             >
                 <Icon
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 7c0cf1207288..5c46a2c32035 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -390,6 +390,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
             <MoneyRequestHeader
                 report={report}
                 policy={policy}
+                backTo={backTo}
                 parentReportAction={parentReportAction}
                 onBackButtonPress={onBackButtonPress}
             />

From 0d38e80233d548e350c767e612fd685d0fea010c Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Sun, 15 Jun 2025 20:36:31 +0100
Subject: [PATCH 06/18] chore: resolve linter issues

---
 .../MoneyRequestReportTransactionList.tsx                     | 4 +++-
 src/pages/home/report/ReportActionsView.tsx                   | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
index 37080fd00116..3cd3248ae519 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
@@ -203,7 +203,9 @@ function MoneyRequestReportTransactionList({
             const sortedSiblingTransactionReportIDs = getThreadReportIDsForTransactions(reportActions, sortedTransactions);
             setActiveTransactionThreadIDs(sortedSiblingTransactionReportIDs);
 
-            Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo, parentReportID: activeTransaction.reportID, parentReportActionID: iouAction?.reportActionID}));
+            Navigation.navigate(
+                ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo, parentReportID: activeTransaction.reportID, parentReportActionID: iouAction?.reportActionID}),
+            );
         },
         [reportActions, sortedTransactions],
     );
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 74c21f4168d3..abcaf3cceab9 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -185,7 +185,7 @@ function ReportActionsView({
         }
 
         return [...actions, createdAction];
-    }, [allReportActions, report, transactionThreadReport, reportPreviewAction]);
+    }, [report, allReportActions, reportPreviewAction?.childMoneyRequestCount, transactionThreadReport, isReportTransactionThread]);
 
     // Get a sorted array of reportActions for both the current report and the transaction thread report associated with this report (if there is one)
     // so that we display transaction-level and report-level report actions in order in the one-transaction view

From 045f688da17bcadd6b59820679ba09d4ff6a3d8f Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Thu, 26 Jun 2025 23:41:19 +0100
Subject: [PATCH 07/18] fix(63253): content shifting when opening a transaction
 thread view

---
 src/pages/home/report/ReportActionsList.tsx | 96 ++++++++++++---------
 1 file changed, 57 insertions(+), 39 deletions(-)

diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index 35d4ba034b5c..4e18ff229a0c 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -1,11 +1,12 @@
 import type {ListRenderItemInfo} from '@react-native/virtualized-lists/Lists/VirtualizedList';
 import {useIsFocused, useRoute} from '@react-navigation/native';
 import React, {memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
-import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
+import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
 import {DeviceEventEmitter, InteractionManager, View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import {useOnyx} from 'react-native-onyx';
 import {renderScrollComponent} from '@components/ActionSheetAwareScrollView';
+import FlatList from '@components/FlatList';
 import InvertedFlatList from '@components/InvertedFlatList';
 import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList';
 import {usePersonalDetails} from '@components/OnyxProvider';
@@ -190,16 +191,21 @@ function ReportActionsList({
     const hasHeaderRendered = useRef(false);
     const linkedReportActionID = route?.params?.reportActionID;
 
-    const lastAction = sortedVisibleReportActions.at(0);
-    const sortedVisibleReportActionsObjects: OnyxTypes.ReportActions = useMemo(
+    const reportActions = useMemo(() => (isTransactionThread(parentReportAction) ? sortedReportActions.toReversed() : sortedReportActions), [parentReportAction, sortedReportActions]);
+    const visibleReportActions = useMemo(
+        () => (isTransactionThread(parentReportAction) ? sortedVisibleReportActions.toReversed() : sortedVisibleReportActions),
+        [parentReportAction, sortedVisibleReportActions],
+    );
+    const lastAction = visibleReportActions.at(0);
+    const visibleReportActionsObjects: OnyxTypes.ReportActions = useMemo(
         () =>
-            sortedVisibleReportActions.reduce((actions, action) => {
+            visibleReportActions.reduce((actions, action) => {
                 Object.assign(actions, {[action.reportActionID]: action});
                 return actions;
             }, {}),
-        [sortedVisibleReportActions],
+        [visibleReportActions],
     );
-    const prevSortedVisibleReportActionsObjects = usePrevious(sortedVisibleReportActionsObjects);
+    const prevVisibleReportActionsObjects = usePrevious(visibleReportActionsObjects);
 
     const reportLastReadTime = report.lastReadTime ?? '';
 
@@ -225,7 +231,7 @@ function ReportActionsList({
      */
     const earliestReceivedOfflineMessageIndex = useMemo(() => {
         // Create a list of (sorted) indices of message that were received while offline
-        const receivedOfflineMessages = sortedReportActions.reduce<number[]>((acc, message, index) => {
+        const receivedOfflineMessages = reportActions.reduce<number[]>((acc, message, index) => {
             if (wasMessageReceivedWhileOffline(message, isOffline, lastOfflineAt.current, lastOnlineAt.current, preferredLocale)) {
                 acc[index] = index;
             }
@@ -235,7 +241,7 @@ function ReportActionsList({
 
         // The last index in the list is the earliest message that was received while offline
         return receivedOfflineMessages.at(-1);
-    }, [isOffline, lastOfflineAt, lastOnlineAt, preferredLocale, sortedReportActions]);
+    }, [isOffline, lastOfflineAt, lastOnlineAt, preferredLocale, reportActions]);
 
     /**
      * The reportActionID the unread marker should display above
@@ -246,9 +252,9 @@ function ReportActionsList({
         const startIndex = earliestReceivedOfflineMessageIndex ?? 0;
 
         // Scan through each visible report action until we find the appropriate action to show the unread marker
-        for (let index = startIndex; index < sortedVisibleReportActions.length; index++) {
-            const reportAction = sortedVisibleReportActions.at(index);
-            const nextAction = sortedVisibleReportActions.at(index + 1);
+        for (let index = startIndex; index < visibleReportActions.length; index++) {
+            const reportAction = visibleReportActions.at(index);
+            const nextAction = visibleReportActions.at(index + 1);
             const isEarliestReceivedOfflineMessage = index === earliestReceivedOfflineMessageIndex;
 
             // eslint-disable-next-line react-compiler/react-compiler
@@ -259,7 +265,7 @@ function ReportActionsList({
                     nextMessage: nextAction,
                     isEarliestReceivedOfflineMessage,
                     accountID,
-                    prevSortedVisibleReportActionsObjects,
+                    prevSortedVisibleReportActionsObjects: prevVisibleReportActionsObjects,
                     unreadMarkerTime,
                     scrollingVerticalOffset: scrollingVerticalOffset.current,
                     prevUnreadMarkerReportActionID: prevUnreadMarkerReportActionID.current,
@@ -270,7 +276,7 @@ function ReportActionsList({
         }
 
         return null;
-    }, [accountID, earliestReceivedOfflineMessageIndex, prevSortedVisibleReportActionsObjects, sortedVisibleReportActions, unreadMarkerTime]);
+    }, [accountID, earliestReceivedOfflineMessageIndex, prevVisibleReportActionsObjects, visibleReportActions, unreadMarkerTime]);
     prevUnreadMarkerReportActionID.current = unreadMarkerReportActionID;
 
     /**
@@ -313,7 +319,7 @@ function ReportActionsList({
     }, [lastAction?.created]);
 
     const lastActionIndex = lastAction?.reportActionID;
-    const reportActionSize = useRef(sortedVisibleReportActions.length);
+    const reportActionSize = useRef(visibleReportActions.length);
     const lastVisibleActionCreated = getReportLastVisibleActionCreated(report, transactionThreadReport);
     const hasNewestReportAction = lastAction?.created === lastVisibleActionCreated;
     const hasNewestReportActionRef = useRef(hasNewestReportAction);
@@ -323,7 +329,7 @@ function ReportActionsList({
 
     // Display the new message indicator when comment linking and not close to the newest message.
     const reportActionID = route?.params?.reportActionID;
-    const indexOfLinkedAction = reportActionID ? sortedVisibleReportActions.findIndex((action) => action.reportActionID === reportActionID) : -1;
+    const indexOfLinkedAction = reportActionID ? visibleReportActions.findIndex((action) => action.reportActionID === reportActionID) : -1;
     const isLinkedActionCloseToNewest = indexOfLinkedAction < IS_CLOSE_TO_NEWEST_THRESHOLD;
 
     const {isFloatingMessageCounterVisible, setIsFloatingMessageCounterVisible, trackVerticalScrolling} = useReportUnreadMessageScrollTracking({
@@ -349,15 +355,15 @@ function ReportActionsList({
         if (
             scrollingVerticalOffset.current < AUTOSCROLL_TO_TOP_THRESHOLD &&
             previousLastIndex.current !== lastActionIndex &&
-            reportActionSize.current > sortedVisibleReportActions.length &&
+            reportActionSize.current > visibleReportActions.length &&
             hasNewestReportAction
         ) {
             setIsFloatingMessageCounterVisible(false);
             reportScrollManager.scrollToBottom();
         }
         previousLastIndex.current = lastActionIndex;
-        reportActionSize.current = sortedVisibleReportActions.length;
-    }, [lastActionIndex, sortedVisibleReportActions, reportScrollManager, hasNewestReportAction, linkedReportActionID, setIsFloatingMessageCounterVisible]);
+        reportActionSize.current = visibleReportActions.length;
+    }, [lastActionIndex, visibleReportActions, reportScrollManager, hasNewestReportAction, linkedReportActionID, setIsFloatingMessageCounterVisible]);
 
     useEffect(() => {
         userActiveSince.current = DateUtils.getDBTime();
@@ -403,13 +409,13 @@ function ReportActionsList({
         if (!isSafari()) {
             return;
         }
-        const prevSorted = lastAction?.reportActionID ? prevSortedVisibleReportActionsObjects[lastAction?.reportActionID] : null;
+        const prevSorted = lastAction?.reportActionID ? prevVisibleReportActionsObjects[lastAction?.reportActionID] : null;
         if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER && !prevSorted) {
             InteractionManager.runAfterInteractions(() => {
                 reportScrollManager.scrollToBottom();
             });
         }
-    }, [lastAction, prevSortedVisibleReportActionsObjects, reportScrollManager]);
+    }, [lastAction, prevVisibleReportActionsObjects, reportScrollManager]);
 
     const scrollToBottomForCurrentUserAction = useCallback(
         (isFromCurrentUser: boolean) => {
@@ -468,7 +474,7 @@ function ReportActionsList({
     }, [report.reportID]);
 
     const [reportActionsListTestID, reportActionsListFSClass] = getChatFSAttributes(participantsContext, 'ReportActionsList', report);
-    const lastIOUActionWithError = sortedVisibleReportActions.find((action) => action.errors);
+    const lastIOUActionWithError = visibleReportActions.find((action) => action.errors);
     const prevLastIOUActionWithError = usePrevious(lastIOUActionWithError);
 
     useEffect(() => {
@@ -514,14 +520,14 @@ function ReportActionsList({
      * This is so that it will not be conflicting with header's separator line.
      */
     const shouldHideThreadDividerLine = useMemo(
-        (): boolean => getFirstVisibleReportActionID(sortedReportActions, isOffline) === unreadMarkerReportActionID,
-        [sortedReportActions, isOffline, unreadMarkerReportActionID],
+        (): boolean => getFirstVisibleReportActionID(reportActions, isOffline) === unreadMarkerReportActionID,
+        [reportActions, isOffline, unreadMarkerReportActionID],
     );
 
-    const firstVisibleReportActionID = useMemo(() => getFirstVisibleReportActionID(sortedReportActions, isOffline), [sortedReportActions, isOffline]);
+    const firstVisibleReportActionID = useMemo(() => getFirstVisibleReportActionID(reportActions, isOffline), [reportActions, isOffline]);
 
     const shouldUseThreadDividerLine = useMemo(() => {
-        const topReport = sortedVisibleReportActions.length > 0 ? sortedVisibleReportActions.at(sortedVisibleReportActions.length - 1) : null;
+        const topReport = visibleReportActions.length > 0 ? visibleReportActions.at(visibleReportActions.length - 1) : null;
 
         if (topReport && topReport.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED) {
             return false;
@@ -536,7 +542,7 @@ function ReportActionsList({
         }
 
         return isExpenseReport(report) || isIOUReport(report) || isInvoiceReport(report);
-    }, [parentReportAction, report, sortedVisibleReportActions]);
+    }, [parentReportAction, report, visibleReportActions]);
 
     useEffect(() => {
         if (report.reportID !== prevReportID) {
@@ -557,7 +563,7 @@ function ReportActionsList({
 
         const isArchivedReport = isArchivedNonExpenseReport(report, reportNameValuePairs);
         const hasNewMessagesInView = scrollingVerticalOffset.current < CONST.REPORT.ACTIONS.ACTION_VISIBLE_THRESHOLD;
-        const hasUnreadReportAction = sortedVisibleReportActions.some(
+        const hasUnreadReportAction = visibleReportActions.some(
             (reportAction) =>
                 newMessageTimeReference &&
                 newMessageTimeReference < reportAction.created &&
@@ -584,7 +590,7 @@ function ReportActionsList({
                 <ReportActionsListItemRenderer
                     allReports={allReports}
                     reportAction={reportAction}
-                    reportActions={sortedReportActions}
+                    reportActions={reportActions}
                     parentReportAction={parentReportAction}
                     parentReportActionForTransactionThread={parentReportActionForTransactionThread}
                     index={index}
@@ -593,13 +599,13 @@ function ReportActionsList({
                     transactionsAndViolationsByReport={transactionsAndViolationsByReport}
                     linkedReportActionID={linkedReportActionID}
                     displayAsGroup={
-                        !isConsecutiveChronosAutomaticTimerAction(sortedVisibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID)) &&
-                        isConsecutiveActionMadeByPreviousActor(sortedVisibleReportActions, index)
+                        !isConsecutiveChronosAutomaticTimerAction(visibleReportActions, index, chatIncludesChronosWithID(reportAction?.reportID)) &&
+                        isConsecutiveActionMadeByPreviousActor(visibleReportActions, index)
                     }
                     mostRecentIOUReportActionID={mostRecentIOUReportActionID}
                     shouldHideThreadDividerLine={shouldHideThreadDividerLine}
                     shouldDisplayNewMarker={reportAction.reportActionID === unreadMarkerReportActionID}
-                    shouldDisplayReplyDivider={sortedVisibleReportActions.length > 1}
+                    shouldDisplayReplyDivider={visibleReportActions.length > 1}
                     isFirstVisibleReportAction={firstVisibleReportActionID === reportAction.reportActionID}
                     shouldUseThreadDividerLine={shouldUseThreadDividerLine}
                     transactions={Object.values(transactions ?? {})}
@@ -607,7 +613,6 @@ function ReportActionsList({
             );
         },
         [
-            sortedReportActions,
             parentReportAction,
             parentReportActionForTransactionThread,
             report,
@@ -616,12 +621,13 @@ function ReportActionsList({
             allReports,
             transactions,
             linkedReportActionID,
-            sortedVisibleReportActions,
+            visibleReportActions,
             mostRecentIOUReportActionID,
             shouldHideThreadDividerLine,
-            unreadMarkerReportActionID,
-            firstVisibleReportActionID,
+            reportActions,
             shouldUseThreadDividerLine,
+            firstVisibleReportActionID,
+            unreadMarkerReportActionID,
         ],
     );
 
@@ -666,7 +672,7 @@ function ReportActionsList({
         );
     }, [canShowHeader, retryLoadNewerChatsError]);
 
-    const shouldShowSkeleton = isOffline && !sortedVisibleReportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED);
+    const shouldShowSkeleton = isOffline && !visibleReportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED);
 
     const listFooterComponent = useMemo(() => {
         if (!shouldShowSkeleton) {
@@ -692,6 +698,18 @@ function ReportActionsList({
     // Parse Fullstory attributes on initial render
     useLayoutEffect(parseFSAttributes, []);
 
+    const ListComponent = isTransactionThread(parentReportAction) ? FlatList : InvertedFlatList;
+
+    const contentContainerStyle = useMemo(() => {
+        const baseStyles: StyleProp<ViewStyle> = [styles.chatContentScrollView];
+
+        if (isTransactionThread(parentReportAction)) {
+            baseStyles.push(styles.pb0, styles.pt4);
+        }
+
+        return baseStyles;
+    }, [parentReportAction, styles.chatContentScrollView, styles.pb0, styles.pt4]);
+
     return (
         <>
             <FloatingMessageCounter
@@ -704,15 +722,15 @@ function ReportActionsList({
                 nativeID={reportActionsListTestID}
                 fsClass={reportActionsListFSClass}
             >
-                <InvertedFlatList
+                <ListComponent
                     accessibilityLabel={translate('sidebarScreen.listOfChatMessages')}
                     ref={reportScrollManager.ref}
                     testID="report-actions-list"
                     style={styles.overscrollBehaviorContain}
-                    data={sortedVisibleReportActions}
+                    data={visibleReportActions}
                     renderItem={renderItem}
                     renderScrollComponent={renderScrollComponent}
-                    contentContainerStyle={styles.chatContentScrollView}
+                    contentContainerStyle={contentContainerStyle}
                     keyExtractor={keyExtractor}
                     initialNumToRender={initialNumToRender}
                     onEndReached={onEndReached}

From 18d35dbac8a6c96b4a7e379b1e2e0f73f83a7465 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Thu, 3 Jul 2025 12:28:24 +0200
Subject: [PATCH 08/18] reduce re-renders amount

---
 src/components/MoneyRequestHeader.tsx         | 101 ++++++++++++------
 .../MoneyRequestReportView.tsx                |  26 ++---
 2 files changed, 82 insertions(+), 45 deletions(-)

diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index b3599cbf2afe..501af11b90d5 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -1,6 +1,6 @@
 import {useRoute} from '@react-navigation/native';
 import type {ReactNode} from 'react';
-import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import React, {memo, useCallback, useEffect, useMemo, useState} from 'react';
 import {View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import {useOnyx} from 'react-native-onyx';
@@ -74,12 +74,39 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
     const route = useRoute();
     const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`, {
         canBeMissing: false,
+        selector: (onyxReport) => onyxReport && {
+            reportID: onyxReport.reportID,
+            ownerAccountID: onyxReport.ownerAccountID,
+            policyID: onyxReport.policyID,
+            parentReportID: onyxReport.parentReportID,
+            type: onyxReport.type,
+        },
     });
     const [transaction] = useOnyx(
         `${ONYXKEYS.COLLECTION.TRANSACTION}${
             isMoneyRequestAction(parentReportAction) ? (getOriginalMessage(parentReportAction)?.IOUTransactionID ?? CONST.DEFAULT_NUMBER_ID) : CONST.DEFAULT_NUMBER_ID
         }`,
-        {canBeMissing: true},
+        {
+            canBeMissing: true,
+            selector: (onyxTransaction) => onyxTransaction && {
+                transactionID: onyxTransaction.transactionID,
+                reportID: onyxTransaction.reportID,
+                amount: onyxTransaction.amount,
+                currency: onyxTransaction.currency,
+                merchant: onyxTransaction.merchant,
+                created: onyxTransaction.created,
+                category: onyxTransaction.category,
+                tag: onyxTransaction.tag,
+                billable: onyxTransaction.billable,
+                reimbursable: onyxTransaction.reimbursable,
+                receipt: onyxTransaction.receipt,
+                filename: onyxTransaction.filename,
+                pendingAction: onyxTransaction.pendingAction,
+                errors: onyxTransaction.errors,
+                hasEReceipt: onyxTransaction.hasEReceipt,
+                cardID: onyxTransaction.cardID,
+            },
+        },
     );
     const transactionViolations = useTransactionViolations(transaction?.transactionID);
 
@@ -107,19 +134,42 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
     const shouldOpenParentReportInCurrentTab = !isSelfDM(parentReport);
 
     const markAsCash = useCallback(() => {
-        markAsCashAction(transaction?.transactionID, reportID);
+        if (!transaction?.transactionID || !reportID) {return;}
+        markAsCashAction(transaction.transactionID, reportID);
     }, [reportID, transaction?.transactionID]);
 
-    const getStatusIcon: (src: IconAsset) => ReactNode = (src) => (
+    const handleDownloadErrorModalClose = useCallback(() => {
+        setDownloadErrorModalVisible(false);
+    }, []);
+
+    const handleDeleteModalCancel = useCallback(() => {
+        setIsDeleteModalVisible(false);
+    }, []);
+
+    const handleDeleteModalConfirm = useCallback(() => {
+        setIsDeleteModalVisible(false);
+        if (!parentReportAction || !transaction) {
+            throw new Error('Data missing');
+        }
+        if (isTrackExpenseAction(parentReportAction)) {
+            deleteTrackExpense(report?.parentReportID, transaction.transactionID, parentReportAction, true);
+        } else {
+            deleteMoneyRequest(transaction.transactionID, parentReportAction, true);
+            removeTransaction(transaction.transactionID);
+        }
+        onBackButtonPress();
+    }, [parentReportAction, transaction, report?.parentReportID, removeTransaction, onBackButtonPress]);
+
+    const getStatusIcon: (src: IconAsset) => ReactNode = useCallback((src) => (
         <Icon
             src={src}
             height={variables.iconSizeSmall}
             width={variables.iconSizeSmall}
             fill={theme.icon}
         />
-    );
+    ), [theme.icon]);
 
-    const getStatusBarProps: () => MoneyRequestHeaderStatusBarProps | undefined = () => {
+    const getStatusBarProps: () => MoneyRequestHeaderStatusBarProps | undefined = useCallback(() => {
         if (isOnHold) {
             return {icon: getStatusIcon(Expensicons.Stopwatch), description: translate('iou.expenseOnHold')};
         }
@@ -149,9 +199,9 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         if (isScanning(transaction)) {
             return {icon: getStatusIcon(Expensicons.ReceiptScan), description: translate('iou.receiptScanInProgressDescription')};
         }
-    };
+    }, [isOnHold, isDuplicate, transaction, shouldShowBrokenConnectionViolation, parentReport, policy, hasPendingRTERViolation, translate, getStatusIcon]);
 
-    const statusBarProps = getStatusBarProps();
+    const statusBarProps = useMemo(() => getStatusBarProps(), [getStatusBarProps]);
 
     useEffect(() => {
         // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
@@ -168,7 +218,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         return getTransactionThreadPrimaryAction(report, parentReport, transaction, transactionViolations, policy);
     }, [parentReport, policy, report, transaction, transactionViolations]);
 
-    const primaryActionImplementation = {
+    const primaryActionImplementation = useMemo(() => ({
         [CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.REMOVE_HOLD]: (
             <Button
                 success
@@ -197,7 +247,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
                 onPress={markAsCash}
             />
         ),
-    };
+    }), [translate, parentReportAction, reportID, markAsCash]);
 
     const secondaryActions = useMemo(() => {
         const reportActions = !!parentReport && getReportActions(parentReport);
@@ -207,7 +257,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         return getSecondaryTransactionThreadActions(parentReport, transaction, Object.values(reportActions), policy);
     }, [parentReport, policy, transaction]);
 
-    const secondaryActionsImplementation: Record<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>, DropdownOption<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>>> = {
+    const secondaryActionsImplementation: Record<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>, DropdownOption<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>>> = useMemo(() => ({
         [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD]: {
             text: translate('iou.hold'),
             icon: Expensicons.Stopwatch,
@@ -244,9 +294,12 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
                 setIsDeleteModalVisible(true);
             },
         },
-    };
+    }), [translate, parentReportAction, transaction, reportID, report]);
 
-    const applicableSecondaryActions = secondaryActions.map((action) => secondaryActionsImplementation[action]);
+    const applicableSecondaryActions = useMemo(() =>
+        secondaryActions.map((action) => secondaryActionsImplementation[action]),
+        [secondaryActions, secondaryActionsImplementation]
+    );
 
     return (
         <View style={[styles.pl0, styles.borderBottom]}>
@@ -323,28 +376,16 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
                 title={translate('common.downloadFailedTitle')}
                 prompt={translate('common.downloadFailedDescription')}
                 isSmallScreenWidth={isSmallScreenWidth}
-                onSecondOptionSubmit={() => setDownloadErrorModalVisible(false)}
+                onSecondOptionSubmit={handleDownloadErrorModalClose}
                 secondOptionText={translate('common.buttonConfirm')}
                 isVisible={downloadErrorModalVisible}
-                onClose={() => setDownloadErrorModalVisible(false)}
+                onClose={handleDownloadErrorModalClose}
             />
             <ConfirmModal
                 title={translate('iou.deleteExpense', {count: 1})}
                 isVisible={isDeleteModalVisible}
-                onConfirm={() => {
-                    setIsDeleteModalVisible(false);
-                    if (!parentReportAction || !transaction) {
-                        throw new Error('Data missing');
-                    }
-                    if (isTrackExpenseAction(parentReportAction)) {
-                        deleteTrackExpense(report?.parentReportID, transaction.transactionID, parentReportAction, true);
-                    } else {
-                        deleteMoneyRequest(transaction.transactionID, parentReportAction, true);
-                        removeTransaction(transaction.transactionID);
-                    }
-                    onBackButtonPress();
-                }}
-                onCancel={() => setIsDeleteModalVisible(false)}
+                onConfirm={handleDeleteModalConfirm}
+                onCancel={handleDeleteModalCancel}
                 prompt={translate('iou.deleteConfirmation', {count: 1})}
                 confirmText={translate('common.delete')}
                 cancelText={translate('common.cancel')}
@@ -357,4 +398,4 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
 
 MoneyRequestHeader.displayName = 'MoneyRequestHeader';
 
-export default MoneyRequestHeader;
+export default memo(MoneyRequestHeader);
diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx
index 121722a32907..a73ab8b9721d 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportView.tsx
@@ -132,6 +132,14 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
     const isEmptyTransactionReport = transactions && transactions.length === 0 && transactionThreadReportID === undefined;
     const shouldDisplayMoneyRequestActionsList = !!isEmptyTransactionReport || shouldDisplayReportTableView(report, transactions ?? []);
 
+    const handleBackButtonPress = useCallback(() => {
+        if (!backToRoute) {
+            goBackFromSearchMoneyRequest();
+            return;
+        }
+        Navigation.goBack(backToRoute);
+    }, [backToRoute]);
+
     const reportHeaderView = useMemo(
         () =>
             isTransactionThreadView ? (
@@ -139,13 +147,7 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
                     report={report}
                     policy={policy}
                     parentReportAction={parentReportAction}
-                    onBackButtonPress={() => {
-                        if (!backToRoute) {
-                            goBackFromSearchMoneyRequest();
-                            return;
-                        }
-                        Navigation.goBack(backToRoute);
-                    }}
+                    onBackButtonPress={handleBackButtonPress}
                 />
             ) : (
                 <MoneyReportHeader
@@ -155,16 +157,10 @@ function MoneyRequestReportView({report, policy, reportMetadata, shouldDisplayRe
                     transactionThreadReportID={transactionThreadReportID}
                     isLoadingInitialReportActions={isLoadingInitialReportActions}
                     shouldDisplayBackButton
-                    onBackButtonPress={() => {
-                        if (!backToRoute) {
-                            goBackFromSearchMoneyRequest();
-                            return;
-                        }
-                        Navigation.goBack(backToRoute);
-                    }}
+                    onBackButtonPress={handleBackButtonPress}
                 />
             ),
-        [backToRoute, isLoadingInitialReportActions, isTransactionThreadView, parentReportAction, policy, report, reportActions, transactionThreadReportID],
+        [handleBackButtonPress, isLoadingInitialReportActions, isTransactionThreadView, parentReportAction, policy, report, reportActions, transactionThreadReportID],
     );
 
     if (!!(isLoadingInitialReportActions && reportActions.length === 0 && !isOffline) || shouldWaitForTransactions) {

From 5440605de387cdd7f3936dfccb9d3b9fbaf324d3 Mon Sep 17 00:00:00 2001
From: "marta.sudol" <msudol8@gmail.com>
Date: Thu, 3 Jul 2025 15:58:49 +0200
Subject: [PATCH 09/18] prettier

---
 src/components/MoneyRequestHeader.tsx         | 215 ++++++++++--------
 .../MoneyRequestReportTransactionList.tsx     |   4 +-
 src/pages/home/report/ReportActionsView.tsx   |   4 +-
 3 files changed, 120 insertions(+), 103 deletions(-)

diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 501af11b90d5..29b7ecfc76fa 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -74,13 +74,14 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
     const route = useRoute();
     const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`, {
         canBeMissing: false,
-        selector: (onyxReport) => onyxReport && {
-            reportID: onyxReport.reportID,
-            ownerAccountID: onyxReport.ownerAccountID,
-            policyID: onyxReport.policyID,
-            parentReportID: onyxReport.parentReportID,
-            type: onyxReport.type,
-        },
+        selector: (onyxReport) =>
+            onyxReport && {
+                reportID: onyxReport.reportID,
+                ownerAccountID: onyxReport.ownerAccountID,
+                policyID: onyxReport.policyID,
+                parentReportID: onyxReport.parentReportID,
+                type: onyxReport.type,
+            },
     });
     const [transaction] = useOnyx(
         `${ONYXKEYS.COLLECTION.TRANSACTION}${
@@ -88,24 +89,25 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         }`,
         {
             canBeMissing: true,
-            selector: (onyxTransaction) => onyxTransaction && {
-                transactionID: onyxTransaction.transactionID,
-                reportID: onyxTransaction.reportID,
-                amount: onyxTransaction.amount,
-                currency: onyxTransaction.currency,
-                merchant: onyxTransaction.merchant,
-                created: onyxTransaction.created,
-                category: onyxTransaction.category,
-                tag: onyxTransaction.tag,
-                billable: onyxTransaction.billable,
-                reimbursable: onyxTransaction.reimbursable,
-                receipt: onyxTransaction.receipt,
-                filename: onyxTransaction.filename,
-                pendingAction: onyxTransaction.pendingAction,
-                errors: onyxTransaction.errors,
-                hasEReceipt: onyxTransaction.hasEReceipt,
-                cardID: onyxTransaction.cardID,
-            },
+            selector: (onyxTransaction) =>
+                onyxTransaction && {
+                    transactionID: onyxTransaction.transactionID,
+                    reportID: onyxTransaction.reportID,
+                    amount: onyxTransaction.amount,
+                    currency: onyxTransaction.currency,
+                    merchant: onyxTransaction.merchant,
+                    created: onyxTransaction.created,
+                    category: onyxTransaction.category,
+                    tag: onyxTransaction.tag,
+                    billable: onyxTransaction.billable,
+                    reimbursable: onyxTransaction.reimbursable,
+                    receipt: onyxTransaction.receipt,
+                    filename: onyxTransaction.filename,
+                    pendingAction: onyxTransaction.pendingAction,
+                    errors: onyxTransaction.errors,
+                    hasEReceipt: onyxTransaction.hasEReceipt,
+                    cardID: onyxTransaction.cardID,
+                },
         },
     );
     const transactionViolations = useTransactionViolations(transaction?.transactionID);
@@ -134,7 +136,9 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
     const shouldOpenParentReportInCurrentTab = !isSelfDM(parentReport);
 
     const markAsCash = useCallback(() => {
-        if (!transaction?.transactionID || !reportID) {return;}
+        if (!transaction?.transactionID || !reportID) {
+            return;
+        }
         markAsCashAction(transaction.transactionID, reportID);
     }, [reportID, transaction?.transactionID]);
 
@@ -160,14 +164,17 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         onBackButtonPress();
     }, [parentReportAction, transaction, report?.parentReportID, removeTransaction, onBackButtonPress]);
 
-    const getStatusIcon: (src: IconAsset) => ReactNode = useCallback((src) => (
-        <Icon
-            src={src}
-            height={variables.iconSizeSmall}
-            width={variables.iconSizeSmall}
-            fill={theme.icon}
-        />
-    ), [theme.icon]);
+    const getStatusIcon: (src: IconAsset) => ReactNode = useCallback(
+        (src) => (
+            <Icon
+                src={src}
+                height={variables.iconSizeSmall}
+                width={variables.iconSizeSmall}
+                fill={theme.icon}
+            />
+        ),
+        [theme.icon],
+    );
 
     const getStatusBarProps: () => MoneyRequestHeaderStatusBarProps | undefined = useCallback(() => {
         if (isOnHold) {
@@ -218,36 +225,39 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         return getTransactionThreadPrimaryAction(report, parentReport, transaction, transactionViolations, policy);
     }, [parentReport, policy, report, transaction, transactionViolations]);
 
-    const primaryActionImplementation = useMemo(() => ({
-        [CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.REMOVE_HOLD]: (
-            <Button
-                success
-                text={translate('iou.unhold')}
-                onPress={() => {
-                    changeMoneyRequestHoldStatus(parentReportAction);
-                }}
-            />
-        ),
-        [CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.REVIEW_DUPLICATES]: (
-            <Button
-                success
-                text={translate('iou.reviewDuplicates')}
-                onPress={() => {
-                    if (!reportID) {
-                        return;
-                    }
-                    Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID, Navigation.getReportRHPActiveRoute()));
-                }}
-            />
-        ),
-        [CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.MARK_AS_CASH]: (
-            <Button
-                success
-                text={translate('iou.markAsCash')}
-                onPress={markAsCash}
-            />
-        ),
-    }), [translate, parentReportAction, reportID, markAsCash]);
+    const primaryActionImplementation = useMemo(
+        () => ({
+            [CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.REMOVE_HOLD]: (
+                <Button
+                    success
+                    text={translate('iou.unhold')}
+                    onPress={() => {
+                        changeMoneyRequestHoldStatus(parentReportAction);
+                    }}
+                />
+            ),
+            [CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.REVIEW_DUPLICATES]: (
+                <Button
+                    success
+                    text={translate('iou.reviewDuplicates')}
+                    onPress={() => {
+                        if (!reportID) {
+                            return;
+                        }
+                        Navigation.navigate(ROUTES.TRANSACTION_DUPLICATE_REVIEW_PAGE.getRoute(reportID, Navigation.getReportRHPActiveRoute()));
+                    }}
+                />
+            ),
+            [CONST.REPORT.TRANSACTION_PRIMARY_ACTIONS.MARK_AS_CASH]: (
+                <Button
+                    success
+                    text={translate('iou.markAsCash')}
+                    onPress={markAsCash}
+                />
+            ),
+        }),
+        [translate, parentReportAction, reportID, markAsCash],
+    );
 
     const secondaryActions = useMemo(() => {
         const reportActions = !!parentReport && getReportActions(parentReport);
@@ -257,50 +267,53 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         return getSecondaryTransactionThreadActions(parentReport, transaction, Object.values(reportActions), policy);
     }, [parentReport, policy, transaction]);
 
-    const secondaryActionsImplementation: Record<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>, DropdownOption<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>>> = useMemo(() => ({
-        [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD]: {
-            text: translate('iou.hold'),
-            icon: Expensicons.Stopwatch,
-            value: CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD,
-            onSelected: () => {
-                if (!parentReportAction) {
-                    throw new Error('Parent action does not exist');
-                }
+    const secondaryActionsImplementation: Record<
+        ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>,
+        DropdownOption<ValueOf<typeof CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS>>
+    > = useMemo(
+        () => ({
+            [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD]: {
+                text: translate('iou.hold'),
+                icon: Expensicons.Stopwatch,
+                value: CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.HOLD,
+                onSelected: () => {
+                    if (!parentReportAction) {
+                        throw new Error('Parent action does not exist');
+                    }
 
-                changeMoneyRequestHoldStatus(parentReportAction);
+                    changeMoneyRequestHoldStatus(parentReportAction);
+                },
             },
-        },
-        [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.SPLIT]: {
-            text: translate('iou.split'),
-            icon: Expensicons.ArrowSplit,
-            value: CONST.REPORT.SECONDARY_ACTIONS.SPLIT,
-            onSelected: () => {
-                initSplitExpense(transaction, reportID ?? String(CONST.DEFAULT_NUMBER_ID));
+            [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.SPLIT]: {
+                text: translate('iou.split'),
+                icon: Expensicons.ArrowSplit,
+                value: CONST.REPORT.SECONDARY_ACTIONS.SPLIT,
+                onSelected: () => {
+                    initSplitExpense(transaction, reportID ?? String(CONST.DEFAULT_NUMBER_ID));
+                },
             },
-        },
-        [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.VIEW_DETAILS]: {
-            value: CONST.REPORT.SECONDARY_ACTIONS.VIEW_DETAILS,
-            text: translate('iou.viewDetails'),
-            icon: Expensicons.Info,
-            onSelected: () => {
-                navigateToDetailsPage(report, Navigation.getReportRHPActiveRoute());
+            [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.VIEW_DETAILS]: {
+                value: CONST.REPORT.SECONDARY_ACTIONS.VIEW_DETAILS,
+                text: translate('iou.viewDetails'),
+                icon: Expensicons.Info,
+                onSelected: () => {
+                    navigateToDetailsPage(report, Navigation.getReportRHPActiveRoute());
+                },
             },
-        },
-        [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.DELETE]: {
-            text: translate('common.delete'),
-            icon: Expensicons.Trashcan,
-            value: CONST.REPORT.SECONDARY_ACTIONS.DELETE,
-            onSelected: () => {
-                setIsDeleteModalVisible(true);
+            [CONST.REPORT.TRANSACTION_SECONDARY_ACTIONS.DELETE]: {
+                text: translate('common.delete'),
+                icon: Expensicons.Trashcan,
+                value: CONST.REPORT.SECONDARY_ACTIONS.DELETE,
+                onSelected: () => {
+                    setIsDeleteModalVisible(true);
+                },
             },
-        },
-    }), [translate, parentReportAction, transaction, reportID, report]);
-
-    const applicableSecondaryActions = useMemo(() =>
-        secondaryActions.map((action) => secondaryActionsImplementation[action]),
-        [secondaryActions, secondaryActionsImplementation]
+        }),
+        [translate, parentReportAction, transaction, reportID, report],
     );
 
+    const applicableSecondaryActions = useMemo(() => secondaryActions.map((action) => secondaryActionsImplementation[action]), [secondaryActions, secondaryActionsImplementation]);
+
     return (
         <View style={[styles.pl0, styles.borderBottom]}>
             <HeaderWithBackButton
diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
index 826bb687676d..da1473d9e5d7 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
@@ -200,7 +200,9 @@ function MoneyRequestReportTransactionList({
             // to display prev/next arrows in RHP for navigation
             const sortedSiblingTransactionReportIDs = getThreadReportIDsForTransactions(reportActions, sortedTransactions);
             setActiveTransactionThreadIDs(sortedSiblingTransactionReportIDs).then(() => {
-                Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo, parentReportID: activeTransaction.reportID, parentReportActionID: iouAction?.reportActionID}));
+                Navigation.navigate(
+                    ROUTES.SEARCH_REPORT.getRoute({reportID: reportIDToNavigate, backTo, parentReportID: activeTransaction.reportID, parentReportActionID: iouAction?.reportActionID}),
+                );
             });
         },
         [reportActions, sortedTransactions],
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index abcaf3cceab9..ebafab5d19fd 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -185,7 +185,9 @@ function ReportActionsView({
         }
 
         return [...actions, createdAction];
-    }, [report, allReportActions, reportPreviewAction?.childMoneyRequestCount, transactionThreadReport, isReportTransactionThread]);
+        // We don't need to listen for changes in whole report and threadTransactionReport objects
+        // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
+    }, [report.reportID, reportPreviewAction?.childMoneyRequestCount, isReportTransactionThread]);
 
     // Get a sorted array of reportActions for both the current report and the transaction thread report associated with this report (if there is one)
     // so that we display transaction-level and report-level report actions in order in the one-transaction view

From 928d46e1d4d3cb7c328a9ca7416f6b094e6ba2ce Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Mon, 7 Jul 2025 14:10:50 +0200
Subject: [PATCH 10/18] fix lint

---
 .../MoneyRequestReportTransactionsNavigation.tsx                | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
index c81c81f8b659..3b9574b3647b 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
@@ -19,7 +19,7 @@ type MoneyRequestReportRHPNavigationButtonsProps = {
     backTo?: string;
 };
 
-function MoneyRequestReportTransactionsNavigation({currentReportID, parentReportID, backTo}: MoneyRequestReportRHPNavigationButtonsProps) {
+function MoneyRequestReportTransactionsNavigation({currentReportID, parentReportID}: MoneyRequestReportRHPNavigationButtonsProps) {
     const [reportIDsList = CONST.EMPTY_ARRAY] = useOnyx(ONYXKEYS.TRANSACTION_THREAD_NAVIGATION_REPORT_IDS, {
         canBeMissing: true,
     });

From aaec8a5714e63faa84e32bcafac6006e8dcddc16 Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Mon, 7 Jul 2025 14:18:09 +0200
Subject: [PATCH 11/18] adjust backTo default logic

---
 ...neyRequestReportTransactionsNavigation.tsx | 22 +++++++++++--------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
index 3b9574b3647b..9e8a3ff8be8a 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
@@ -19,7 +19,7 @@ type MoneyRequestReportRHPNavigationButtonsProps = {
     backTo?: string;
 };
 
-function MoneyRequestReportTransactionsNavigation({currentReportID, parentReportID}: MoneyRequestReportRHPNavigationButtonsProps) {
+function MoneyRequestReportTransactionsNavigation({currentReportID, parentReportID, backTo}: MoneyRequestReportRHPNavigationButtonsProps) {
     const [reportIDsList = CONST.EMPTY_ARRAY] = useOnyx(ONYXKEYS.TRANSACTION_THREAD_NAVIGATION_REPORT_IDS, {
         canBeMissing: true,
     });
@@ -66,18 +66,22 @@ function MoneyRequestReportTransactionsNavigation({currentReportID, parentReport
             isPrevButtonDisabled={!prevReportID}
             isNextButtonDisabled={!nextReportID}
             onNext={(e) => {
-                const backTo = Navigation.getActiveRoute();
                 e?.preventDefault();
-                Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: nextReportID, parentReportActionID: nextParentReportActionID, parentReportID, backTo}), {
-                    forceReplace: true,
-                });
+                Navigation.navigate(
+                    ROUTES.SEARCH_REPORT.getRoute({reportID: nextReportID, parentReportActionID: nextParentReportActionID, parentReportID, backTo: backTo ?? Navigation.getActiveRoute()}),
+                    {
+                        forceReplace: true,
+                    },
+                );
             }}
             onPrevious={(e) => {
-                const backTo = Navigation.getActiveRoute();
                 e?.preventDefault();
-                Navigation.navigate(ROUTES.SEARCH_REPORT.getRoute({reportID: prevReportID, parentReportActionID: prevParentReportActionID, parentReportID, backTo}), {
-                    forceReplace: true,
-                });
+                Navigation.navigate(
+                    ROUTES.SEARCH_REPORT.getRoute({reportID: prevReportID, parentReportActionID: prevParentReportActionID, parentReportID, backTo: backTo ?? Navigation.getActiveRoute()}),
+                    {
+                        forceReplace: true,
+                    },
+                );
             }}
         />
     );

From 89f7cdec8a0f3d5678382115f3ab84fb34eb16fc Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Tue, 8 Jul 2025 16:34:19 +0200
Subject: [PATCH 12/18] fix: add missing allReportAction to dependency array

---
 src/pages/home/report/ReportActionsView.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index c8d73db5414d..a64ff5616b87 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -187,7 +187,7 @@ function ReportActionsView({
         return [...actions, createdAction];
         // We don't need to listen for changes in whole report and threadTransactionReport objects
         // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
-    }, [report.reportID, reportPreviewAction?.childMoneyRequestCount, isReportTransactionThread]);
+    }, [allReportActions, report.reportID, reportPreviewAction?.childMoneyRequestCount, isReportTransactionThread]);
 
     // Get a sorted array of reportActions for both the current report and the transaction thread report associated with this report (if there is one)
     // so that we display transaction-level and report-level report actions in order in the one-transaction view

From 207d3d60f58001795a9390f48368d6f759a2ca40 Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Thu, 10 Jul 2025 13:38:43 +0200
Subject: [PATCH 13/18] refactor: fixes after PR comments

---
 src/components/MoneyRequestHeader.tsx                         | 4 +---
 .../MoneyRequestReportTransactionsNavigation.tsx              | 1 +
 src/pages/home/report/ReportActionsView.tsx                   | 1 +
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 91a3a82c71eb..666ae6598adc 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -176,7 +176,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         [theme.icon],
     );
 
-    const getStatusBarProps: () => MoneyRequestHeaderStatusBarProps | undefined = useCallback(() => {
+    const statusBarProps: MoneyRequestHeaderStatusBarProps | undefined = useMemo(() => {
         if (isOnHold) {
             return {icon: getStatusIcon(Expensicons.Stopwatch), description: translate('iou.expenseOnHold')};
         }
@@ -208,8 +208,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         }
     }, [isOnHold, isDuplicate, transaction, shouldShowBrokenConnectionViolation, parentReport, policy, hasPendingRTERViolation, translate, getStatusIcon]);
 
-    const statusBarProps = useMemo(() => getStatusBarProps(), [getStatusBarProps]);
-
     useEffect(() => {
         // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
         if (isLoadingHoldUseExplained || dismissedHoldUseExplanation || !isOnHold) {
diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
index 54c3d3fc5593..baae8ac2d671 100644
--- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
+++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionsNavigation.tsx
@@ -13,6 +13,7 @@ import getEmptyArray from '@src/types/utils/getEmptyArray';
 type MoneyRequestReportRHPNavigationButtonsProps = {
     currentReportID: string;
 
+    /** The ID of the parent report that contains the transaction threads */
     parentReportID?: string;
 
     /** The route to navigate back to when the user clicks on the back button */
diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 00819fc421db..432891ed5983 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -63,6 +63,7 @@ type ReportActionsViewProps = {
     /** If the report has older actions to load */
     hasOlderActions: boolean;
 
+    /** If this report is a transaction thread report */
     isReportTransactionThread?: boolean;
 };
 

From 0762b0a30d9554be5b3064a6f4cf84f472ce09a4 Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Fri, 11 Jul 2025 12:42:34 +0200
Subject: [PATCH 14/18] chore: add more graceful error handling

---
 src/components/MoneyRequestHeader.tsx | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 666ae6598adc..8e88e651f4e0 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -12,6 +12,7 @@ import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useTransactionViolations from '@hooks/useTransactionViolations';
 import {deleteMoneyRequest, deleteTrackExpense, initSplitExpense} from '@libs/actions/IOU';
+import Log from '@libs/Log';
 import Navigation from '@libs/Navigation/Navigation';
 import {getOriginalMessage, getReportActions, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils';
 import {getTransactionThreadPrimaryAction} from '@libs/ReportPrimaryActionUtils';
@@ -153,7 +154,15 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
     const handleDeleteModalConfirm = useCallback(() => {
         setIsDeleteModalVisible(false);
         if (!parentReportAction || !transaction) {
-            throw new Error('Data missing');
+            Log.hmmm('[MoneyRequestHeader] Unable to delete expense due to missing data', {
+                hasParentReportAction: !!parentReportAction,
+                parentReportID: report?.parentReportID,
+                hasTransaction: !!transaction,
+                transactionID: transaction?.transactionID,
+            });
+
+            onBackButtonPress();
+            return;
         }
         if (isTrackExpenseAction(parentReportAction)) {
             deleteTrackExpense(report?.parentReportID, transaction.transactionID, parentReportAction, true);

From 3a0bf79763841b1d83b75c4661e29f0672f39708 Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Fri, 11 Jul 2025 12:45:05 +0200
Subject: [PATCH 15/18] fix: apply necessary filter for transaction report

---
 src/pages/home/report/ReportActionsView.tsx | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/src/pages/home/report/ReportActionsView.tsx b/src/pages/home/report/ReportActionsView.tsx
index 432891ed5983..61f54deadb20 100755
--- a/src/pages/home/report/ReportActionsView.tsx
+++ b/src/pages/home/report/ReportActionsView.tsx
@@ -209,15 +209,13 @@ function ReportActionsView({
     const canPerformWriteAction = canUserPerformWriteAction(report);
     const visibleReportActions = useMemo(
         () =>
-            isReportTransactionThread
-                ? reportActions
-                : reportActions.filter(
-                      (reportAction) =>
-                          (isOffline || isDeletedParentAction(reportAction) || reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || reportAction.errors) &&
-                          shouldReportActionBeVisible(reportAction, reportAction.reportActionID, canPerformWriteAction) &&
-                          isIOUActionMatchingTransactionList(reportAction, reportTransactionIDs),
-                  ),
-        [isReportTransactionThread, reportActions, isOffline, canPerformWriteAction, reportTransactionIDs],
+            reportActions.filter(
+                (reportAction) =>
+                    (isOffline || isDeletedParentAction(reportAction) || reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || reportAction.errors) &&
+                    shouldReportActionBeVisible(reportAction, reportAction.reportActionID, canPerformWriteAction) &&
+                    isIOUActionMatchingTransactionList(reportAction, reportTransactionIDs),
+            ),
+        [reportActions, isOffline, canPerformWriteAction, reportTransactionIDs],
     );
 
     const newestReportAction = useMemo(() => reportActions?.at(0), [reportActions]);

From c32835c007f651e608d907da780579bcee396149 Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Fri, 11 Jul 2025 12:49:59 +0200
Subject: [PATCH 16/18] chore: remove redundant default value

---
 src/pages/home/ReportScreen.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index e862e7e0a08b..73c9ce54c92b 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -158,7 +158,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
     const [reportOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportIDFromRoute}`, {allowStaleData: true, canBeMissing: true});
     const [reportNameValuePairsOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportIDFromRoute}`, {allowStaleData: true, canBeMissing: true});
     const [reportMetadata = defaultReportMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${reportIDFromRoute}`, {canBeMissing: true, allowStaleData: true});
-    const [policies = getEmptyObject<NonNullable<OnyxCollection<OnyxTypes.Policy>>>()] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, initialValue: {}, canBeMissing: false});
+    const [policies = getEmptyObject<NonNullable<OnyxCollection<OnyxTypes.Policy>>>()] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {allowStaleData: true, canBeMissing: false});
     const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(reportOnyx?.parentReportID ?? parentReportIDFromRoute)}`, {
         canEvict: false,
         selector: (parentReportActions) => getParentReportAction(parentReportActions, reportOnyx?.parentReportActionID ?? parentReportActionIDFromRoute),

From d3d18de8c6638ed088d61a392374100bf1e1ded7 Mon Sep 17 00:00:00 2001
From: Tomasz Lesniakiewicz <tlesniakiewicz@gmail.com>
Date: Mon, 14 Jul 2025 14:12:23 +0200
Subject: [PATCH 17/18] revert using FlatList for transaction thread

---
 src/pages/home/report/ReportActionsList.tsx | 18 ++----------------
 1 file changed, 2 insertions(+), 16 deletions(-)

diff --git a/src/pages/home/report/ReportActionsList.tsx b/src/pages/home/report/ReportActionsList.tsx
index d2f4619df084..0996f57a5476 100644
--- a/src/pages/home/report/ReportActionsList.tsx
+++ b/src/pages/home/report/ReportActionsList.tsx
@@ -1,11 +1,10 @@
 import type {ListRenderItemInfo} from '@react-native/virtualized-lists/Lists/VirtualizedList';
 import {useIsFocused, useRoute} from '@react-navigation/native';
 import React, {memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
-import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
+import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
 import {DeviceEventEmitter, InteractionManager, View} from 'react-native';
 import type {OnyxEntry} from 'react-native-onyx';
 import {renderScrollComponent} from '@components/ActionSheetAwareScrollView';
-import FlatList from '@components/FlatList';
 import InvertedFlatList from '@components/InvertedFlatList';
 import {AUTOSCROLL_TO_TOP_THRESHOLD} from '@components/InvertedFlatList/BaseInvertedFlatList';
 import {usePersonalDetails} from '@components/OnyxProvider';
@@ -696,18 +695,6 @@ function ReportActionsList({
     // Parse Fullstory attributes on initial render
     useLayoutEffect(parseFSAttributes, []);
 
-    const ListComponent = isTransactionThread(parentReportAction) ? FlatList : InvertedFlatList;
-
-    const contentContainerStyle = useMemo(() => {
-        const baseStyles: StyleProp<ViewStyle> = [styles.chatContentScrollView];
-
-        if (isTransactionThread(parentReportAction)) {
-            baseStyles.push(styles.pb0, styles.pt4);
-        }
-
-        return baseStyles;
-    }, [parentReportAction, styles.chatContentScrollView, styles.pb0, styles.pt4]);
-
     return (
         <>
             <FloatingMessageCounter
@@ -720,7 +707,7 @@ function ReportActionsList({
                 nativeID={reportActionsListTestID}
                 fsClass={reportActionsListFSClass}
             >
-                <ListComponent
+                <InvertedFlatList
                     accessibilityLabel={translate('sidebarScreen.listOfChatMessages')}
                     ref={reportScrollManager.ref}
                     testID="report-actions-list"
@@ -728,7 +715,6 @@ function ReportActionsList({
                     data={visibleReportActions}
                     renderItem={renderItem}
                     renderScrollComponent={renderScrollComponent}
-                    contentContainerStyle={contentContainerStyle}
                     keyExtractor={keyExtractor}
                     initialNumToRender={initialNumToRender}
                     onEndReached={onEndReached}

From 6b86334940ca4f5a7b4fa99af069950d9f7bd435 Mon Sep 17 00:00:00 2001
From: Pedro Guerreiro <pedro.a.c.guerreiro@gmail.com>
Date: Mon, 14 Jul 2025 13:45:58 +0100
Subject: [PATCH 18/18] docs: explain implementation changes

---
 src/components/MoneyRequestHeader.tsx | 2 ++
 src/pages/home/ReportScreen.tsx       | 1 +
 2 files changed, 3 insertions(+)

diff --git a/src/components/MoneyRequestHeader.tsx b/src/components/MoneyRequestHeader.tsx
index 8e88e651f4e0..1cb3a04e030e 100644
--- a/src/components/MoneyRequestHeader.tsx
+++ b/src/components/MoneyRequestHeader.tsx
@@ -75,6 +75,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
     const route = useRoute();
     const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.parentReportID}`, {
         canBeMissing: false,
+        // We need to use a selector here to prevent the component from re-rendering on unused report properties changes
         selector: (onyxReport) =>
             onyxReport && {
                 reportID: onyxReport.reportID,
@@ -90,6 +91,7 @@ function MoneyRequestHeader({report, parentReportAction, policy, backTo, onBackB
         }`,
         {
             canBeMissing: true,
+            // We need to use a selector here to prevent the component from re-rendering on unused transaction properties changes
             selector: (onyxTransaction) =>
                 onyxTransaction && {
                     transactionID: onyxTransaction.transactionID,
diff --git a/src/pages/home/ReportScreen.tsx b/src/pages/home/ReportScreen.tsx
index 4ee0cfa111fa..fbf236eb9142 100644
--- a/src/pages/home/ReportScreen.tsx
+++ b/src/pages/home/ReportScreen.tsx
@@ -219,6 +219,7 @@ function ReportScreen({route, navigation}: ReportScreenProps) {
      */
     const report = useMemo(
         () =>
+            // This is required to get the transaction data from the parent report, to render the optimistic transaction thread
             parentReportIDFromRoute && parentReportActionIDFromRoute
                 ? ({
                       ...reportOnyx,
