From 6025f3611c8abd249dc7dbd2c82eb26b3159ece5 Mon Sep 17 00:00:00 2001 From: dmkt9 Date: Wed, 18 Jun 2025 08:35:48 +0700 Subject: [PATCH 1/5] Fix - Header link takes 2 steps back to expense report when reply is created in transaction thread --- src/libs/ReportUtils.ts | 6 ++++-- src/pages/home/HeaderView.tsx | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index db07ccf634ff..7a8f89a38977 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -154,6 +154,7 @@ import { getReportActionHtml, getReportActionMessage as getReportActionMessageReportUtils, getReportActionMessageText, + getReportActions, getReportActionText, getRetractedMessage, getTravelUpdateMessage, @@ -8631,9 +8632,9 @@ function navigateToLinkedReportAction(ancestor: Ancestor, isInNarrowPaneModal: b const parentReportAction = getReportAction(ancestor.report.parentReportID, ancestor.report.parentReportActionID); let newAncestor = ancestor; - // If `parentReport` is an IOU or Expense report, navigate directly to `parentReport`, + // If `parentReport` is an money report with one transaction, navigate directly to `parentReport`, // preventing redundant navigation when threading back to the parent chat thread - if (parentReport && parentReportAction && (isIOUReport(parentReport) || isExpenseReport(parentReport))) { + if (parentReport && parentReportAction && !!getOneTransactionThreadReportID(parentReport, getReportOrDraftReport(parentReport.chatReportID), getReportActions(parentReport))) { newAncestor = { ...ancestor, report: parentReport, @@ -11252,6 +11253,7 @@ export { hasReportBeenReopened, getMoneyReportPreviewName, getNextApproverAccountID, + isOneTransactionReport, }; export type { diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index b5a844dea00e..fbab0661c2de 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -34,6 +34,7 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import Parser from '@libs/Parser'; +import {getOneTransactionThreadReportID, getReportActions} from '@libs/ReportActionsUtils'; import { canJoinChat, canUserPerformWriteAction, @@ -46,6 +47,7 @@ import { getPolicyName, getReportDescription, getReportName, + getReportOrDraftReport, hasReportNameError, isAdminRoom, isArchivedReport, @@ -54,10 +56,8 @@ import { isChatUsedForOnboarding as isChatUsedForOnboardingReportUtils, isCurrentUserSubmitter, isDeprecatedGroupDM, - isExpenseReport, isExpenseRequest, isGroupChat as isGroupChatReportUtils, - isIOUReport, isOpenTaskReport, isPolicyExpenseChat as isPolicyExpenseChatReportUtils, isSelfDM as isSelfDMReportUtils, @@ -140,7 +140,10 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, const isTaskReport = isTaskReportReportUtils(report); const [parentOfParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReport?.parentReportID}`, {canBeMissing: true}); const reportHeaderData = - ((!isTaskReport && !isChatThread) || (parentOfParentReport && (isIOUReport(parentOfParentReport) || isExpenseReport(parentOfParentReport)))) && report?.parentReportID + ((!isTaskReport && !isChatThread) || + (parentOfParentReport && + !!getOneTransactionThreadReportID(parentOfParentReport, getReportOrDraftReport(parentOfParentReport?.chatReportID), getReportActions(parentOfParentReport)))) && + report?.parentReportID ? parentReport : report; // Use sorted display names for the title for group chats on native small screen widths From cc92c846331aab8f7176aeb4c7e0bfa77977369c Mon Sep 17 00:00:00 2001 From: dmkt9 Date: Wed, 18 Jun 2025 09:43:00 +0700 Subject: [PATCH 2/5] update for filter visible report actions --- src/libs/ReportUtils.ts | 18 ++++++++++++------ src/pages/home/HeaderView.tsx | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7a8f89a38977..f7355648e960 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8634,12 +8634,18 @@ function navigateToLinkedReportAction(ancestor: Ancestor, isInNarrowPaneModal: b let newAncestor = ancestor; // If `parentReport` is an money report with one transaction, navigate directly to `parentReport`, // preventing redundant navigation when threading back to the parent chat thread - if (parentReport && parentReportAction && !!getOneTransactionThreadReportID(parentReport, getReportOrDraftReport(parentReport.chatReportID), getReportActions(parentReport))) { - newAncestor = { - ...ancestor, - report: parentReport, - reportAction: parentReportAction, - }; + if (parentReport && parentReportAction) { + const allParentReportActions = getReportActions(parentReport); + const visibleParentReportActions = Object.values(allParentReportActions ?? {}).filter( + (action) => !isMessageDeleted(action) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isOffline), + ); + if (getOneTransactionThreadReportID(parentReport, getReportOrDraftReport(parentReport.chatReportID), visibleParentReportActions)) { + newAncestor = { + ...ancestor, + report: parentReport, + reportAction: parentReportAction, + }; + } } if (isInNarrowPaneModal) { diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index fbab0661c2de..36aa4505ae90 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -25,6 +25,7 @@ import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import usePolicy from '@hooks/usePolicy'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useSubscriptionPlan from '@hooks/useSubscriptionPlan'; @@ -34,7 +35,7 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import Parser from '@libs/Parser'; -import {getOneTransactionThreadReportID, getReportActions} from '@libs/ReportActionsUtils'; +import {getOneTransactionThreadReportID, getReportActions, isMessageDeleted} from '@libs/ReportActionsUtils'; import { canJoinChat, canUserPerformWriteAction, @@ -139,10 +140,19 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, const isPolicyExpenseChat = isPolicyExpenseChatReportUtils(report); const isTaskReport = isTaskReportReportUtils(report); const [parentOfParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReport?.parentReportID}`, {canBeMissing: true}); + const {isOffline} = useNetwork(); + const visibleParentOfParentReportActions = useMemo(() => { + if (!parentOfParentReport) { + return; + } + + const allReportActions = getReportActions(parentOfParentReport); + return Object.values(allReportActions ?? {}).filter((action) => !isMessageDeleted(action) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isOffline)); + }, [parentOfParentReport, isOffline]); const reportHeaderData = ((!isTaskReport && !isChatThread) || (parentOfParentReport && - !!getOneTransactionThreadReportID(parentOfParentReport, getReportOrDraftReport(parentOfParentReport?.chatReportID), getReportActions(parentOfParentReport)))) && + !!getOneTransactionThreadReportID(parentOfParentReport, getReportOrDraftReport(parentOfParentReport?.chatReportID), visibleParentOfParentReportActions))) && report?.parentReportID ? parentReport : report; From 245475da9f300ae441beb3e2931f733b3fd16608 Mon Sep 17 00:00:00 2001 From: dmkt9 Date: Wed, 18 Jun 2025 10:00:17 +0700 Subject: [PATCH 3/5] update --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f7355648e960..b204430b5286 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -11259,7 +11259,6 @@ export { hasReportBeenReopened, getMoneyReportPreviewName, getNextApproverAccountID, - isOneTransactionReport, }; export type { From e522b419f8b0c6fff92711c5e97c63c348bf8256 Mon Sep 17 00:00:00 2001 From: dmkt9 Date: Fri, 27 Jun 2025 12:55:11 +0700 Subject: [PATCH 4/5] refactor code --- src/libs/ReportActionsUtils.ts | 5 ++++- src/libs/ReportUtils.ts | 22 ++++++++++------------ src/pages/home/HeaderView.tsx | 19 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/libs/ReportActionsUtils.ts b/src/libs/ReportActionsUtils.ts index b8a18ad084d4..0016fdec2f9f 100644 --- a/src/libs/ReportActionsUtils.ts +++ b/src/libs/ReportActionsUtils.ts @@ -1249,6 +1249,8 @@ const isIOUActionMatchingTransactionList = ( /** * Gets the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions. * Returns a reportID if there is exactly one transaction thread for the report, and null otherwise. + * + * @param ignoreAllDeletedReportActions Filter out all deleted reportActions, even if they have visible childActions. */ function getOneTransactionThreadReportID( report: OnyxEntry, @@ -1256,6 +1258,7 @@ function getOneTransactionThreadReportID( reportActions: OnyxEntry | ReportAction[], isOffline: boolean | undefined = undefined, reportTransactionIDs?: string[], + ignoreAllDeletedReportActions = false, ): string | undefined { // If the report is not an IOU, Expense report, or Invoice, it shouldn't be treated as one-transaction report. if (report?.type !== CONST.REPORT.TYPE.IOU && report?.type !== CONST.REPORT.TYPE.EXPENSE && report?.type !== CONST.REPORT.TYPE.INVOICE) { @@ -1294,7 +1297,7 @@ function getOneTransactionThreadReportID( // - the action is pending deletion and the user is offline (!!originalMessage?.IOUTransactionID || // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (isMessageDeleted(action) && action.childVisibleActionCount) || + (ignoreAllDeletedReportActions ? !isMessageDeleted(action) : isMessageDeleted(action) && action.childVisibleActionCount) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && (isOffline ?? isNetworkOffline))) ) { iouRequestActions.push(action); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ce82849b11b7..384d7caca67d 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -8872,18 +8872,16 @@ function navigateToLinkedReportAction(ancestor: Ancestor, isInNarrowPaneModal: b let newAncestor = ancestor; // If `parentReport` is an money report with one transaction, navigate directly to `parentReport`, // preventing redundant navigation when threading back to the parent chat thread - if (parentReport && parentReportAction) { - const allParentReportActions = getReportActions(parentReport); - const visibleParentReportActions = Object.values(allParentReportActions ?? {}).filter( - (action) => !isMessageDeleted(action) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isOffline), - ); - if (getOneTransactionThreadReportID(parentReport, getReportOrDraftReport(parentReport.chatReportID), visibleParentReportActions)) { - newAncestor = { - ...ancestor, - report: parentReport, - reportAction: parentReportAction, - }; - } + if ( + parentReport && + parentReportAction && + getOneTransactionThreadReportID(parentReport, getReportOrDraftReport(parentReport.chatReportID), getReportActions(parentReport), isOffline, undefined, true) + ) { + newAncestor = { + ...ancestor, + report: parentReport, + reportAction: parentReportAction, + }; } if (isInNarrowPaneModal) { diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index b0f30ff79c53..dc3d376a64a7 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -36,7 +36,7 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import Parser from '@libs/Parser'; -import {getOneTransactionThreadReportID, getReportActions, isMessageDeleted} from '@libs/ReportActionsUtils'; +import {getOneTransactionThreadReportID, getReportActions} from '@libs/ReportActionsUtils'; import { canJoinChat, canUserPerformWriteAction, @@ -142,18 +142,17 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, const isTaskReport = isTaskReportReportUtils(report); const [parentOfParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReport?.parentReportID}`, {canBeMissing: true}); const {isOffline} = useNetwork(); - const visibleParentOfParentReportActions = useMemo(() => { - if (!parentOfParentReport) { - return; - } - - const allReportActions = getReportActions(parentOfParentReport); - return Object.values(allReportActions ?? {}).filter((action) => !isMessageDeleted(action) || (action.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isOffline)); - }, [parentOfParentReport, isOffline]); const reportHeaderData = ((!isTaskReport && !isChatThread) || (parentOfParentReport && - !!getOneTransactionThreadReportID(parentOfParentReport, getReportOrDraftReport(parentOfParentReport?.chatReportID), visibleParentOfParentReportActions))) && + !!getOneTransactionThreadReportID( + parentOfParentReport, + getReportOrDraftReport(parentOfParentReport?.chatReportID), + getReportActions(parentOfParentReport), + isOffline, + undefined, + true, + ))) && report?.parentReportID ? parentReport : report; From 187c7b3ff9a31965911b8b367aa254655cd84726 Mon Sep 17 00:00:00 2001 From: dmkt9 Date: Wed, 23 Jul 2025 23:50:06 +0700 Subject: [PATCH 5/5] updated to fix parent report return behavior --- src/libs/ReportUtils.ts | 29 +++++------------------------ src/pages/home/HeaderView.tsx | 20 +------------------- 2 files changed, 6 insertions(+), 43 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f497c0270a82..466566eece77 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -162,7 +162,6 @@ import { getReportActionHtml, getReportActionMessage as getReportActionMessageReportUtils, getReportActionMessageText, - getReportActions, getReportActionText, getRetractedMessage, getTravelUpdateMessage, @@ -8808,29 +8807,11 @@ function isMoneyRequestReportPendingDeletion(reportOrID: OnyxEntry | str } function navigateToLinkedReportAction(ancestor: Ancestor, isInNarrowPaneModal: boolean, canUserPerformWrite: boolean | undefined, isOffline: boolean) { - const parentReport = getReportOrDraftReport(ancestor.report.parentReportID); - const parentReportAction = getReportAction(ancestor.report.parentReportID, ancestor.report.parentReportActionID); - - let newAncestor = ancestor; - // If `parentReport` is an money report with one transaction, navigate directly to `parentReport`, - // preventing redundant navigation when threading back to the parent chat thread - if ( - parentReport && - parentReportAction && - getOneTransactionThreadReportID(parentReport, getReportOrDraftReport(parentReport.chatReportID), getReportActions(parentReport), isOffline, undefined, true) - ) { - newAncestor = { - ...ancestor, - report: parentReport, - reportAction: parentReportAction, - }; - } - if (isInNarrowPaneModal) { Navigation.navigate( ROUTES.SEARCH_REPORT.getRoute({ - reportID: newAncestor.report.reportID, - reportActionID: newAncestor.reportAction.reportActionID, + reportID: ancestor.report.reportID, + reportActionID: ancestor.reportAction.reportActionID, backTo: SCREENS.SEARCH.REPORT_RHP, }), ); @@ -8838,13 +8819,13 @@ function navigateToLinkedReportAction(ancestor: Ancestor, isInNarrowPaneModal: b } // Pop the thread report screen before navigating to the chat report. - Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(newAncestor.report.reportID)); + Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID)); - const isVisibleAction = shouldReportActionBeVisible(newAncestor.reportAction, newAncestor.reportAction.reportActionID, canUserPerformWrite); + const isVisibleAction = shouldReportActionBeVisible(ancestor.reportAction, ancestor.reportAction.reportActionID, canUserPerformWrite); if (isVisibleAction && !isOffline) { // Pop the chat report screen before navigating to the linked report action. - Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(newAncestor.report.reportID, newAncestor.reportAction.reportActionID)); + Navigation.goBack(ROUTES.REPORT_WITH_ID.getRoute(ancestor.report.reportID, ancestor.reportAction.reportActionID)); } } diff --git a/src/pages/home/HeaderView.tsx b/src/pages/home/HeaderView.tsx index 36414aa19de0..d6779d8153dc 100644 --- a/src/pages/home/HeaderView.tsx +++ b/src/pages/home/HeaderView.tsx @@ -26,7 +26,6 @@ import useHasTeam2025Pricing from '@hooks/useHasTeam2025Pricing'; import useLoadingBarVisibility from '@hooks/useLoadingBarVisibility'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useNetwork from '@hooks/useNetwork'; import usePolicy from '@hooks/usePolicy'; import useReportIsArchived from '@hooks/useReportIsArchived'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -37,7 +36,6 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; import Parser from '@libs/Parser'; -import {getOneTransactionThreadReportID, getReportActions} from '@libs/ReportActionsUtils'; import { canJoinChat, canUserPerformWriteAction, @@ -50,7 +48,6 @@ import { getPolicyName, getReportDescription, getReportName, - getReportOrDraftReport, hasReportNameError, isAdminRoom, isArchivedReport, @@ -143,22 +140,7 @@ function HeaderView({report, parentReportAction, onNavigationMenuButtonClicked, const isChatRoom = isChatRoomReportUtils(report); const isPolicyExpenseChat = isPolicyExpenseChatReportUtils(report); const isTaskReport = isTaskReportReportUtils(report); - const [parentOfParentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${parentReport?.parentReportID}`, {canBeMissing: true}); - const {isOffline} = useNetwork(); - const reportHeaderData = - ((!isTaskReport && !isChatThread) || - (parentOfParentReport && - !!getOneTransactionThreadReportID( - parentOfParentReport, - getReportOrDraftReport(parentOfParentReport?.chatReportID), - getReportActions(parentOfParentReport), - isOffline, - undefined, - true, - ))) && - report?.parentReportID - ? parentReport - : report; + const reportHeaderData = !isTaskReport && !isChatThread && report?.parentReportID ? parentReport : report; // Use sorted display names for the title for group chats on native small screen widths const title = getReportName(reportHeaderData, policy, parentReportAction, personalDetails, invoiceReceiverPolicy); const subtitle = getChatRoomSubtitle(reportHeaderData);