From 85c830a2ef9e28eb76067d6adab463690df4667f Mon Sep 17 00:00:00 2001 From: Faizan Shoukat Abbasi Date: Sat, 21 Feb 2026 03:29:34 +0500 Subject: [PATCH] 74706: Chat - LHN rearranges and last message changes after clearing cache --- src/hooks/useSidebarOrderedReports.tsx | 4 +- src/libs/SidebarUtils.ts | 55 +++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/hooks/useSidebarOrderedReports.tsx b/src/hooks/useSidebarOrderedReports.tsx index 64ccd2d1deb34..47db40b57bce9 100644 --- a/src/hooks/useSidebarOrderedReports.tsx +++ b/src/hooks/useSidebarOrderedReports.tsx @@ -232,10 +232,10 @@ function SidebarOrderedReportsContextProvider({ const getOrderedReportIDs = useCallback( () => - SidebarUtils.sortReportsToDisplayInLHN(deepComparedReportsToDisplayInLHN ?? {}, priorityMode, localeCompare, deepComparedReportsDrafts, reportNameValuePairs, conciergeReportID), + SidebarUtils.sortReportsToDisplayInLHN(deepComparedReportsToDisplayInLHN ?? {}, priorityMode, localeCompare, deepComparedReportsDrafts, reportNameValuePairs, conciergeReportID, chatReports), // Rule disabled intentionally - reports should be sorted only when the reportsToDisplayInLHN changes // eslint-disable-next-line react-hooks/exhaustive-deps - [deepComparedReportsToDisplayInLHN, localeCompare, deepComparedReportsDrafts, conciergeReportID], + [deepComparedReportsToDisplayInLHN, localeCompare, deepComparedReportsDrafts, conciergeReportID, chatReports], ); const orderedReportIDs = useMemo(() => getOrderedReportIDs(), [getOrderedReportIDs]); diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 4a75fbc26f665..81189adeac6e3 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -372,6 +372,44 @@ function updateReportsToDisplayInLHN({ return displayedReportsCopy; } +/** + * Builds a map of parent report IDs to their child transaction thread reports' lastVisibleActionCreated. + * This is used to find the correct lastVisibleActionCreated for expense reports with transaction threads. + */ +function buildTransactionThreadMap(allReports: OnyxCollection): Record { + const transactionThreadMap: Record = {}; + + if (!allReports) { + return transactionThreadMap; + } + + for (const report of Object.values(allReports)) { + if (!report?.parentReportID || !report.lastVisibleActionCreated) { + continue; + } + + const parentReport = allReports[`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID}`]; + + // Only consider child reports of expense/IOU/invoice reports + if ( + !parentReport || + (parentReport.type !== CONST.REPORT.TYPE.IOU && parentReport.type !== CONST.REPORT.TYPE.EXPENSE && parentReport.type !== CONST.REPORT.TYPE.INVOICE) + ) { + continue; + } + + // Get the existing value or empty string + const existingValue = transactionThreadMap[report.parentReportID] ?? ''; + + // Keep the most recent lastVisibleActionCreated for each parent + if (report.lastVisibleActionCreated > existingValue) { + transactionThreadMap[report.parentReportID] = report.lastVisibleActionCreated; + } + } + + return transactionThreadMap; +} + /** * Categorizes reports into their respective LHN groups */ @@ -380,6 +418,7 @@ function categorizeReportsForLHN( reportsDrafts: OnyxCollection | undefined, conciergeReportID: string | undefined, reportNameValuePairs?: OnyxCollection, + allReports?: OnyxCollection, ) { const pinnedAndGBRReports: MiniReport[] = []; const errorReports: MiniReport[] = []; @@ -387,6 +426,9 @@ function categorizeReportsForLHN( const nonArchivedReports: MiniReport[] = []; const archivedReports: MiniReport[] = []; + // Build a map of transaction thread lastVisibleActionCreated for expense reports + const transactionThreadMap = allReports ? buildTransactionThreadMap(allReports) : {}; + // Pre-calculate report names and other properties to avoid repeated calculations const reportValues = Object.values(reportsToDisplay); const precomputedReports: Array<{ @@ -407,10 +449,18 @@ function categorizeReportsForLHN( const reportID = report.reportID; // eslint-disable-next-line @typescript-eslint/no-deprecated const displayName = getReportName(report, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, conciergeReportID); + + // Get the correct lastVisibleActionCreated considering transaction thread reports + let lastVisibleActionCreated = report.lastVisibleActionCreated ?? ''; + const transactionThreadLastAction = transactionThreadMap[reportID]; + if (transactionThreadLastAction && transactionThreadLastAction > lastVisibleActionCreated) { + lastVisibleActionCreated = transactionThreadLastAction; + } + const miniReport: MiniReport = { reportID, displayName, - lastVisibleActionCreated: report.lastVisibleActionCreated, + lastVisibleActionCreated, }; const isPinned = !!report.isPinned; @@ -543,6 +593,7 @@ function sortReportsToDisplayInLHN( reportsDrafts: OnyxCollection | undefined, reportNameValuePairs: OnyxCollection | undefined, conciergeReportID: string | undefined, + allReports?: OnyxCollection, ): string[] { Performance.markStart(CONST.TIMING.GET_ORDERED_REPORT_IDS); @@ -560,7 +611,7 @@ function sortReportsToDisplayInLHN( // - Sorted by reportDisplayName in GSD (focus) view mode // Step 1: Categorize reports - const categories = categorizeReportsForLHN(reportsToDisplay, reportsDrafts, conciergeReportID, reportNameValuePairs); + const categories = categorizeReportsForLHN(reportsToDisplay, reportsDrafts, conciergeReportID, reportNameValuePairs, allReports); // Step 2: Sort each category const sortedCategories = sortCategorizedReports(categories, isInDefaultMode, localeCompare);