From 81edfb5fd4bf0e7b1f38ebdcec4dec946a640d46 Mon Sep 17 00:00:00 2001 From: VH Date: Sun, 26 Jul 2026 21:05:46 +0700 Subject: [PATCH 1/3] refactor(reportName): replace deprecatedGetReportName with getReportName across libs Migrate call sites off deprecatedGetReportName to the new getReportName signature, which takes the precomputed report name string directly instead of the full reportAttributes map. --- src/libs/ModifiedExpenseMessage.ts | 4 ++-- .../LocalNotification/BrowserNotifications.ts | 4 ++-- src/libs/OptionsListUtils/index.ts | 8 ++++---- src/libs/ReportUtils.ts | 14 ++++++++------ src/libs/SearchQueryUtils.ts | 5 +++-- src/libs/SearchUIUtils.ts | 6 +++--- src/libs/SidebarUtils.ts | 12 +++++++----- src/libs/actions/Task.ts | 4 ++-- 8 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index a0b6fd24c370..61e7692a7b89 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -33,7 +33,7 @@ import {getOriginalMessage, isModifiedExpenseAction} from './ReportActionsUtils' // The functions imported here are pure utility functions that don't create initialization-time dependencies. // ReportNameUtils imports helper functions from ReportUtils, and ReportUtils imports name generation functions from ReportNameUtils. // eslint-disable-next-line import/no-cycle -import {buildReportNameFromParticipantNames, deprecatedGetReportName, getPolicyExpenseChatName} from './ReportNameUtils'; +import {buildReportNameFromParticipantNames, getPolicyExpenseChatName, getReportName} from './ReportNameUtils'; import {getPolicyName, getRootParentReport, isPolicyExpenseChat, isSelfDM} from './ReportUtils'; import {getFormattedAttendees, getTagArrayFromName} from './TransactionUtils'; import {isInvalidMerchantValue} from './ValidationUtils'; @@ -196,7 +196,7 @@ function getMovedFromOrToReportMessage( } if (movedFromReport) { - const originReportName = deprecatedGetReportName(movedFromReport, reportAttributes); + const originReportName = getReportName(movedFromReport, reportAttributes?.[movedFromReport.reportID]?.reportName); return originReportName ? translate('iou.movedFromReport', originReportName) : translate('iou.movedFromReportNoName'); } } diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts index b53e061b4188..0840186c311e 100644 --- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts +++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts @@ -6,7 +6,7 @@ import Log from '@libs/Log'; import {getForReportAction} from '@libs/ModifiedExpenseMessage'; import NotificationPermission from '@libs/Notification/notificationPermission'; import {getTextFromHtml} from '@libs/ReportActionsUtils'; -import {deprecatedGetReportName} from '@libs/ReportNameUtils'; +import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; @@ -134,7 +134,7 @@ export default { } if (isRoomOrGroupChat) { - const roomName = deprecatedGetReportName(report, reportAttributes); + const roomName = getReportName(report, reportAttributes?.[report.reportID]?.reportName); title = roomName; body = `${plainTextPerson}: ${plainTextMessage}`; } else { diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 06939caa81fa..f304f67283f2 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -111,7 +111,7 @@ import { wasActionTakenByCurrentUser, withDEWRoutedActionsArray, } from '@libs/ReportActionsUtils'; -import {deprecatedGetReportName} from '@libs/ReportNameUtils'; +import {getReportName} from '@libs/ReportNameUtils'; import type {OptionData} from '@libs/ReportUtils'; import { canUserPerformWriteAction, @@ -1212,7 +1212,7 @@ function createOption({ }, ); - const computedReportName = deprecatedGetReportName(report, reportAttributesDerived); + const computedReportName = getReportName(report, report?.reportID ? reportAttributesDerived?.[report.reportID]?.reportName : undefined); reportName = showPersonalDetails ? getDisplayNameForParticipant({accountID: accountIDs.at(0), formatPhoneNumber: formatPhoneNumberPhoneUtils, translate: translateFn}) || @@ -1297,7 +1297,7 @@ function getReportOption( if (option.isSelfDM) { option.alternateText = translate('reportActionsView.yourSpace'); } else if (option.isInvoiceRoom) { - option.text = deprecatedGetReportName(report, reportAttributesDerived); + option.text = getReportName(report, report?.reportID ? reportAttributesDerived?.[report.reportID]?.reportName : undefined); option.alternateText = translate('workspace.common.invoices'); } else { option.text = getPolicyName({report, policy, unavailableTranslation: translate('workspace.common.unavailable')}); @@ -1355,7 +1355,7 @@ function getReportDisplayOption( if (option.isSelfDM) { option.alternateText = translate('reportActionsView.yourSpace'); } else if (option.isInvoiceRoom) { - option.text = deprecatedGetReportName(report, reportAttributesDerived); + option.text = getReportName(report, report?.reportID ? reportAttributesDerived?.[report.reportID]?.reportName : undefined); option.alternateText = translate('workspace.common.invoices'); } else if (unknownUserDetails) { option.text = unknownUserDetails.text ?? unknownUserDetails.login; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 9b3d8ca23dc0..7ef45dbd6805 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -241,7 +241,7 @@ import { // The functions imported here are pure utility functions that don't create initialization-time dependencies. // ReportNameUtils imports helper functions from ReportUtils, and ReportUtils imports name generation functions from ReportNameUtils. // eslint-disable-next-line import/no-cycle -import {deprecatedGetReportName, getGroupChatName, getInvoicePayerName, getInvoiceReportName} from './ReportNameUtils'; +import {getGroupChatName, getInvoicePayerName, getInvoiceReportName, getReportName} from './ReportNameUtils'; import {shouldRestrictUserBillableActions} from './SubscriptionUtils'; import {isTaskCompleted} from './TaskUtils'; import { @@ -5693,7 +5693,8 @@ function getReportPreviewMessageForCopy( const originalReportAction = params.originalReportAction ?? iouReportAction; const report = typeof reportOrID === 'string' ? getReport(reportOrID, deprecatedAllReports) : reportOrID; if (report) { - return deprecatedGetReportName(report, reportAttributes ?? reportAttributesDerivedValue) || (originalReportAction?.childReportName ?? ''); + const attributes = reportAttributes ?? reportAttributesDerivedValue; + return getReportName(report, attributes?.[report.reportID]?.reportName) || (originalReportAction?.childReportName ?? ''); } return originalReportAction?.childReportName ?? ''; } @@ -7188,7 +7189,7 @@ function getMovedTransactionMessage(translate: LocalizedTranslate, action: Repor const report = fromReport ?? toReport; - const reportName = Parser.htmlToText(deprecatedGetReportName(report, reportAttributes) ?? report?.reportName ?? ''); + const reportName = Parser.htmlToText(getReportName(report, report?.reportID ? reportAttributes?.[report.reportID]?.reportName : undefined)); const reportUrl = getReportURLForCurrentContext(report?.reportID); if (typeof fromReportID === 'undefined') { return reportName ? translate('iou.movedTransactionTo', reportUrl, reportName) : translate('iou.movedTransactionToAnotherReport'); @@ -7202,7 +7203,7 @@ function getUnreportedTransactionMessage(translate: LocalizedTranslate, action: const fromReport = deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; - const reportName = Parser.htmlToText(deprecatedGetReportName(fromReport, reportAttributes) ?? fromReport?.reportName ?? ''); + const reportName = Parser.htmlToText(getReportName(fromReport, fromReport?.reportID ? reportAttributes?.[fromReport.reportID]?.reportName : undefined)); let reportUrl = getReportURLForCurrentContext(fromReportID); @@ -13115,10 +13116,11 @@ function getChatListItemReportName(action: ReportAction & {reportName?: string}, } if (report?.reportID) { - return deprecatedGetReportName(getReport(report?.reportID, deprecatedAllReports), reportAttributesDerivedValue); + const fullReport = getReport(report.reportID, deprecatedAllReports); + return getReportName(fullReport, fullReport?.reportID ? reportAttributesDerivedValue?.[fullReport.reportID]?.reportName : undefined); } - return deprecatedGetReportName(report, reportAttributesDerivedValue); + return getReportName(report, report?.reportID ? reportAttributesDerivedValue?.[report.reportID]?.reportName : undefined); } /** diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 2a20f4ceadb8..6015be04d59d 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -65,7 +65,7 @@ import navigationRef from './Navigation/navigationRef'; import {isRecord} from './ObjectUtils'; import {getPersonalDetailByEmail, temporaryGetDisplayNameOrDefault} from './PersonalDetailsUtils'; import {getCleanedTagName, getValidConnectedIntegration} from './PolicyUtils'; -import {deprecatedGetReportName} from './ReportNameUtils'; +import {getReportName} from './ReportNameUtils'; import {parse as parseSearchQuery} from './SearchParser/searchParser'; import StringUtils from './StringUtils'; import {hashText} from './UserUtils'; @@ -1707,7 +1707,8 @@ function getFilterDisplayValue({ return getBankAccountSearchLabel(bankAccount); } if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) { - return deprecatedGetReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filterValue}`], reportAttributes) || filterValue; + const filterReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filterValue}`]; + return getReportName(filterReport, filterReport?.reportID ? reportAttributes?.[filterReport.reportID]?.reportName : undefined) || filterValue; } if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT) { // Added 2 here as this is the maximum number of decimals an amount can have. So, we can run a search with 2 decimals here. diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index e1e6c25d7569..c885f29d3551 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -141,7 +141,7 @@ import { isResolvedActionableWhisper, isWhisperActionTargetedToOthers, } from './ReportActionsUtils'; -import {deprecatedGetReportName} from './ReportNameUtils'; +import {getReportName} from './ReportNameUtils'; import {isExportAction} from './ReportPrimaryActionUtils'; import { canDeleteMoneyRequestReport, @@ -2608,7 +2608,7 @@ function getTaskSections( if (parentReport && personalDetails) { const policy = data[`${ONYXKEYS.COLLECTION.POLICY}${parentReport.policyID}`]; const isParentReportArchived = isArchivedReport(reportNameValuePairs?.[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${parentReport?.reportID}`]); - const parentReportName = deprecatedGetReportName(parentReport, reportAttributesDerivedValue); + const parentReportName = getReportName(parentReport, parentReport?.reportID ? reportAttributesDerivedValue?.[parentReport.reportID]?.reportName : undefined); const icons = getIcons(parentReport, formatPhoneNumber, translate, personalDetails, null, '', -1, policy, undefined, isParentReportArchived); const parentReportIcon = icons?.at(0); @@ -2780,7 +2780,7 @@ function getReportActionsSections( ...reportAction, reportID, from, - reportName: deprecatedGetReportName(report, reportAttributesDerivedValue), + reportName: getReportName(report, report?.reportID ? reportAttributesDerivedValue?.[report.reportID]?.reportName : undefined), formattedFrom: from?.displayName ?? from?.login ?? '', date: reportAction.created, keyForList: reportAction.reportActionID, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 2ce0c0284d63..a9e335e63133 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -152,7 +152,7 @@ import { isTagModificationAction, isTaskAction, } from './ReportActionsUtils'; -import {deprecatedGetReportName} from './ReportNameUtils'; +import {getReportName} from './ReportNameUtils'; import { canUserPerformWriteAction as canUserPerformWriteActionUtil, excludeParticipantsForDisplay, @@ -573,7 +573,7 @@ function categorizeReportsForLHN( } const reportID = report.reportID; - const displayName = deprecatedGetReportName(report, reportAttributes); + const displayName = getReportName(report, reportAttributes?.[report.reportID]?.reportName); const miniReport: MiniReport = { reportID, displayName, @@ -1064,7 +1064,9 @@ function getOptionData({ : translate('workspace.invite.removed'); const users = translate(targetAccountIDsLength > 1 ? 'common.members' : 'common.member')?.toLocaleLowerCase(); result.alternateText = formatReportLastMessageText(`${actorDisplayName ?? lastActorDisplayName}: ${verb} ${targetAccountIDsLength} ${users}`); - const roomName = deprecatedGetReportName(lastActionReport ?? undefined, reportAttributesDerived) || lastActionOriginalMessage?.roomName; + const roomName = + getReportName(lastActionReport ?? undefined, lastActionReport?.reportID ? reportAttributesDerived?.[lastActionReport.reportID]?.reportName : undefined) || + lastActionOriginalMessage?.roomName; if (roomName) { const preposition = lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.INVITE_TO_ROOM || lastAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.INVITE_TO_ROOM @@ -1382,7 +1384,7 @@ function getOptionData({ result.phoneNumber = personalDetail?.phoneNumber ?? ''; } - const reportName = deprecatedGetReportName(report, reportAttributesDerived); + const reportName = getReportName(report, report?.reportID ? reportAttributesDerived?.[report.reportID]?.reportName : undefined); result.text = reportName; result.subtitle = subtitle; @@ -1513,7 +1515,7 @@ function getRoomWelcomeMessage( ): WelcomeMessage { const welcomeMessage: WelcomeMessage = {}; const workspaceName = getPolicyName({report}); - const reportName = deprecatedGetReportName(report ?? undefined, reportAttributes); + const reportName = getReportName(report ?? undefined, report?.reportID ? reportAttributes?.[report.reportID]?.reportName : undefined); if (report?.description) { welcomeMessage.messageHtml = getReportDescription(report); diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 452a22da784d..4a1a1a2b0cd6 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -14,7 +14,7 @@ import {getDBTimeWithSkew} from '@libs/NetworkState'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; -import {deprecatedGetReportName} from '@libs/ReportNameUtils'; +import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {buildOptimisticSnapshotData} from '@libs/SearchQueryUtils'; import playSound, {SOUNDS} from '@libs/Sound'; @@ -1170,7 +1170,7 @@ function getShareDestination( } return { icons: ReportUtils.getIcons(report, formatPhoneNumber, translate, personalDetails, FallbackAvatar), - displayName: deprecatedGetReportName(report, reportAttributes), + displayName: getReportName(report, report?.reportID ? reportAttributes?.[report.reportID]?.reportName : undefined), subtitle, displayNamesWithTooltips, shouldUseFullTitleToDisplay: ReportUtils.shouldUseFullTitleToDisplay(report), From b4781d2d260cdf6d0f3b608acb22c0353c55372d Mon Sep 17 00:00:00 2001 From: VH Date: Mon, 27 Jul 2026 23:10:11 +0700 Subject: [PATCH 2/3] Let sub pr 2 do this migration --- src/libs/SidebarUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index a9e335e63133..4b61c673280d 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -152,7 +152,7 @@ import { isTagModificationAction, isTaskAction, } from './ReportActionsUtils'; -import {getReportName} from './ReportNameUtils'; +import {deprecatedGetReportName, getReportName} from './ReportNameUtils'; import { canUserPerformWriteAction as canUserPerformWriteActionUtil, excludeParticipantsForDisplay, @@ -1515,7 +1515,7 @@ function getRoomWelcomeMessage( ): WelcomeMessage { const welcomeMessage: WelcomeMessage = {}; const workspaceName = getPolicyName({report}); - const reportName = getReportName(report ?? undefined, report?.reportID ? reportAttributes?.[report.reportID]?.reportName : undefined); + const reportName = deprecatedGetReportName(report ?? undefined, reportAttributes); if (report?.description) { welcomeMessage.messageHtml = getReportDescription(report); From b20f401f9522412f88d96f5e49261e0ed2ba5970 Mon Sep 17 00:00:00 2001 From: VH Date: Tue, 4 Aug 2026 11:34:09 +0700 Subject: [PATCH 3/3] refactor(reportAttributes): pass report name instead of attributes map - Replace `reportAttributes` params with a single report-name arg in getMovedTransactionMessage, getUnreportedTransactionMessage, getShareDestination and getMovedFromOrToReportMessage - Add getMovedTransactionReportID/getUnreportedTransactionReportID so components can subscribe via useDerivedReportNameByReportID instead of the whole report attributes map - Fall back to module-level reportAttributesDerivedValue for imperative callers (SidebarUtils, OptionsListUtils, ReportNameUtils, ContextMenu) - Drop reportAttributes from ContextMenuActionPayload and getReportPreviewMessageForCopy --- .../MovedTransactionAction.tsx | 8 +-- .../UnreportedTransactionAction.tsx | 8 +-- src/libs/ModifiedExpenseMessage.ts | 13 +++- src/libs/OptionsListUtils/index.ts | 4 +- src/libs/ReportNameUtils.ts | 4 +- src/libs/ReportUtils.ts | 66 +++++++++++++++---- src/libs/SidebarUtils.ts | 4 +- src/libs/actions/Task.ts | 4 +- .../BaseReportActionContextMenu.tsx | 1 - .../report/ContextMenu/ContextMenuActions.tsx | 8 +-- src/pages/tasks/DynamicNewTaskPage.tsx | 6 +- tests/unit/ReportUtilsTest.ts | 7 +- 12 files changed, 89 insertions(+), 44 deletions(-) diff --git a/src/components/ReportActionItem/MovedTransactionAction.tsx b/src/components/ReportActionItem/MovedTransactionAction.tsx index 5e4a28ae305f..c8257bf2dbdb 100644 --- a/src/components/ReportActionItem/MovedTransactionAction.tsx +++ b/src/components/ReportActionItem/MovedTransactionAction.tsx @@ -2,12 +2,12 @@ import RenderHTML from '@components/RenderHTML'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useReportAttributes from '@hooks/useReportAttributes'; +import {useDerivedReportNameByReportID} from '@hooks/useReportAttributes'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Parser from '@libs/Parser'; import {getOriginalMessage, hasReasoning} from '@libs/ReportActionsUtils'; -import {getMovedTransactionMessage} from '@libs/ReportUtils'; +import {getMovedTransactionMessage, getMovedTransactionReportID} from '@libs/ReportUtils'; import ReportActionItemBasicMessage from '@pages/inbox/report/ReportActionItemBasicMessage'; import ReportActionItemMessageWithExplain from '@pages/inbox/report/ReportActionItemMessageWithExplain'; @@ -35,11 +35,11 @@ function MovedTransactionAction({action, originalReport}: MovedTransactionAction const [fromReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`); const [childReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(action.childReportID)}`); - const reportAttributes = useReportAttributes(); const isPendingDelete = fromReport?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; - const message = getMovedTransactionMessage(translate, action, reportAttributes); + const derivedReportName = useDerivedReportNameByReportID(getMovedTransactionReportID(action)); + const message = getMovedTransactionMessage(translate, action, derivedReportName); if (hasReasoning(action)) { return ( diff --git a/src/components/ReportActionItem/UnreportedTransactionAction.tsx b/src/components/ReportActionItem/UnreportedTransactionAction.tsx index 066e78010f1d..e9d39dd5ece2 100644 --- a/src/components/ReportActionItem/UnreportedTransactionAction.tsx +++ b/src/components/ReportActionItem/UnreportedTransactionAction.tsx @@ -2,12 +2,12 @@ import RenderHTML from '@components/RenderHTML'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; -import useReportAttributes from '@hooks/useReportAttributes'; +import {useDerivedReportNameByReportID} from '@hooks/useReportAttributes'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Parser from '@libs/Parser'; import {getOriginalMessage, hasReasoning} from '@libs/ReportActionsUtils'; -import {getUnreportedTransactionMessage} from '@libs/ReportUtils'; +import {getUnreportedTransactionMessage, getUnreportedTransactionReportID} from '@libs/ReportUtils'; import ReportActionItemBasicMessage from '@pages/inbox/report/ReportActionItemBasicMessage'; import ReportActionItemMessageWithExplain from '@pages/inbox/report/ReportActionItemMessageWithExplain'; @@ -35,10 +35,10 @@ function UnreportedTransactionAction({action, originalReport}: UnreportedTransac const {translate} = useLocalize(); const [fromReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`); const [childReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(action.childReportID)}`); - const reportAttributes = useReportAttributes(); const isPendingDelete = fromReport?.pendingFields?.preview === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; - const unreportedTransactionMessage = getUnreportedTransactionMessage(translate, action, reportAttributes); + const derivedReportName = useDerivedReportNameByReportID(getUnreportedTransactionReportID(action)); + const unreportedTransactionMessage = getUnreportedTransactionMessage(translate, action, derivedReportName); if (hasReasoning(action)) { return ( diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 61e7692a7b89..25838a1a8784 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -189,14 +189,14 @@ function getMovedFromOrToReportMessage( movedToReport: OnyxEntry | undefined, currentUserLogin: string, policy: OnyxEntry, - reportAttributes?: ReportAttributesDerivedValue['reports'], + movedFromReportName?: string, ): string | undefined { if (movedToReport) { return getForExpenseMovedFromSelfDM(translate, movedToReport, currentUserLogin, policy); } if (movedFromReport) { - const originReportName = getReportName(movedFromReport, reportAttributes?.[movedFromReport.reportID]?.reportName); + const originReportName = getReportName(movedFromReport, movedFromReportName); return originReportName ? translate('iou.movedFromReport', originReportName) : translate('iou.movedFromReportNoName'); } } @@ -295,7 +295,14 @@ function getForReportAction({ return ''; } - const movedFromOrToReportMessage = getMovedFromOrToReportMessage(translate, movedFromReport, movedToReport, currentUserLogin, policy, reportAttributes); + const movedFromOrToReportMessage = getMovedFromOrToReportMessage( + translate, + movedFromReport, + movedToReport, + currentUserLogin, + policy, + movedFromReport ? reportAttributes?.[movedFromReport.reportID]?.reportName : undefined, + ); if (movedFromOrToReportMessage) { return movedFromOrToReportMessage; } diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index f304f67283f2..ad1ca249c534 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -809,7 +809,7 @@ function getLastMessageTextForReport({ const properSchemaForModifiedExpenseMessage = Parser.htmlToText(properSchemaForModifiedExpenseMessageWithHTML); lastMessageTextFromReport = formatReportLastMessageText(properSchemaForModifiedExpenseMessage, true); } else if (isMovedTransactionAction(lastReportAction)) { - lastMessageTextFromReport = Parser.htmlToText(getMovedTransactionMessage(translate, lastReportAction, reportAttributesDerived)); + lastMessageTextFromReport = Parser.htmlToText(getMovedTransactionMessage(translate, lastReportAction)); } else if (isTaskAction(lastReportAction)) { lastMessageTextFromReport = formatReportLastMessageText(getTaskReportActionMessage(translate, lastReportAction).text); } else if (isCreatedTaskReportAction(lastReportAction)) { @@ -914,7 +914,7 @@ function getLastMessageTextForReport({ } else if (isMovedAction(lastReportAction)) { lastMessageTextFromReport = Parser.htmlToText(getMovedActionMessage(translate, lastReportAction, report)); } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION)) { - lastMessageTextFromReport = Parser.htmlToText(getUnreportedTransactionMessage(translate, lastReportAction, reportAttributesDerived)); + lastMessageTextFromReport = Parser.htmlToText(getUnreportedTransactionMessage(translate, lastReportAction)); } else if (isActionableMentionWhisper(lastReportAction)) { const targetAccountIDs = getOriginalMessage(lastReportAction)?.inviteeAccountIDs; lastMessageTextFromReport = Parser.htmlToText(getActionableMentionWhisperMessage(translate, lastReportAction, getPersonalDetailsListByIDs(targetAccountIDs, personalDetails))); diff --git a/src/libs/ReportNameUtils.ts b/src/libs/ReportNameUtils.ts index 1d751575fda5..5a6bdfa84aa4 100644 --- a/src/libs/ReportNameUtils.ts +++ b/src/libs/ReportNameUtils.ts @@ -584,11 +584,11 @@ function computeReportNameBasedOnReportAction( } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION)) { - return Parser.htmlToText(getUnreportedTransactionMessage(translate, parentReportAction, reportAttributes)); + return Parser.htmlToText(getUnreportedTransactionMessage(translate, parentReportAction)); } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - return Parser.htmlToText(getMovedTransactionMessage(translate, parentReportAction, reportAttributes)); + return Parser.htmlToText(getMovedTransactionMessage(translate, parentReportAction)); } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_MAX_EXPENSE_AMOUNT)) { diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 7ef45dbd6805..8c55b37ad04e 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5686,15 +5686,12 @@ type GetReportPreviewMessageBaseParams = { * derived from report action content. Report action text is in English only, so this never needs * `translateLocal`. */ -function getReportPreviewMessageForCopy( - params: Pick & {reportAttributes?: ReportAttributesDerivedValue['reports']}, -): string { - const {reportOrID, iouReportAction = null, reportAttributes} = params; +function getReportPreviewMessageForCopy(params: Pick): string { + const {reportOrID, iouReportAction = null} = params; const originalReportAction = params.originalReportAction ?? iouReportAction; const report = typeof reportOrID === 'string' ? getReport(reportOrID, deprecatedAllReports) : reportOrID; if (report) { - const attributes = reportAttributes ?? reportAttributesDerivedValue; - return getReportName(report, attributes?.[report.reportID]?.reportName) || (originalReportAction?.childReportName ?? ''); + return getReportName(report, reportAttributesDerivedValue?.[report.reportID]?.reportName) || (originalReportAction?.childReportName ?? ''); } return originalReportAction?.childReportName ?? ''; } @@ -7180,16 +7177,38 @@ function getDeletedTransactionMessage(translate: LocalizedTranslate, action: Rep return message; } -function getMovedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, reportAttributes?: ReportAttributesDerivedValue['reports']) { +/** + * Resolves the report a moved transaction message points at, along with the raw `fromReportID` + * that tells "moved to" apart from "moved from". + */ +function getMovedTransactionReportContext(action: ReportAction) { const movedTransactionOriginalMessage = getOriginalMessage(action) ?? {}; const {toReportID, fromReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; const toReport = deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${toReportID}`]; const fromReport = deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; - const report = fromReport ?? toReport; + return {report: fromReport ?? toReport, fromReportID}; +} + +/** + * ID of the report whose name a moved transaction message renders. Callers use this to look up that + * single report's derived name, so they never have to hold the whole report attributes map. + */ +function getMovedTransactionReportID(action: ReportAction): string | undefined { + return getMovedTransactionReportContext(action).report?.reportID; +} + +/** + * `derivedReportName` only matters to callers that render this message: passing it (sourced via + * `useDerivedReportNameByReportID`, keyed by {@link getMovedTransactionReportID}) keeps the name reactive + * without subscribing to the whole report attributes map. Imperative callers can omit it and get the + * module-level derived value, which is already current whenever they run. + */ +function getMovedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, derivedReportName?: string) { + const {report, fromReportID} = getMovedTransactionReportContext(action); - const reportName = Parser.htmlToText(getReportName(report, report?.reportID ? reportAttributes?.[report.reportID]?.reportName : undefined)); + const reportName = Parser.htmlToText(getReportName(report, derivedReportName ?? (report?.reportID ? reportAttributesDerivedValue?.[report.reportID]?.reportName : undefined))); const reportUrl = getReportURLForCurrentContext(report?.reportID); if (typeof fromReportID === 'undefined') { return reportName ? translate('iou.movedTransactionTo', reportUrl, reportName) : translate('iou.movedTransactionToAnotherReport'); @@ -7197,13 +7216,34 @@ function getMovedTransactionMessage(translate: LocalizedTranslate, action: Repor return reportName ? translate('iou.movedTransactionFrom', reportUrl, reportName) : translate('iou.movedTransactionFromAnotherReport'); } -function getUnreportedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, reportAttributes?: ReportAttributesDerivedValue['reports']) { +/** + * Resolves the report an unreported transaction message points at, along with the raw `fromReportID`, + * which stays meaningful even when no report exists for it (e.g. `UNREPORTED_REPORT_ID`). + */ +function getUnreportedTransactionReportContext(action: ReportAction) { const movedTransactionOriginalMessage = getOriginalMessage(action) ?? {}; const {fromReportID} = movedTransactionOriginalMessage as OriginalMessageMovedTransaction; - const fromReport = deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`]; + return {report: deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${fromReportID}`], fromReportID}; +} - const reportName = Parser.htmlToText(getReportName(fromReport, fromReport?.reportID ? reportAttributes?.[fromReport.reportID]?.reportName : undefined)); +/** + * ID of the report whose name an unreported transaction message renders. See {@link getMovedTransactionReportID}. + */ +function getUnreportedTransactionReportID(action: ReportAction): string | undefined { + return getUnreportedTransactionReportContext(action).report?.reportID; +} + +/** + * `derivedReportName` behaves as described on {@link getMovedTransactionMessage}, keyed by + * {@link getUnreportedTransactionReportID}. + */ +function getUnreportedTransactionMessage(translate: LocalizedTranslate, action: ReportAction, derivedReportName?: string) { + const {report: fromReport, fromReportID} = getUnreportedTransactionReportContext(action); + + const reportName = Parser.htmlToText( + getReportName(fromReport, derivedReportName ?? (fromReport?.reportID ? reportAttributesDerivedValue?.[fromReport.reportID]?.reportName : undefined)), + ); let reportUrl = getReportURLForCurrentContext(fromReportID); @@ -14147,7 +14187,9 @@ export { getPolicyChangeMessage, getPolicyChangeLogCopyMessage, getMovedTransactionMessage, + getMovedTransactionReportID, getUnreportedTransactionMessage, + getUnreportedTransactionReportID, navigateToLinkedReportAction, buildOptimisticUnreportedTransactionAction, isBusinessInvoiceRoom, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index 4b61c673280d..a2fd63028a63 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1214,7 +1214,7 @@ function getOptionData({ } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_EMPLOYEE) { result.alternateText = getPolicyChangeLogDeleteMemberMessage(translate, lastAction); } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION)) { - result.alternateText = Parser.htmlToText(getUnreportedTransactionMessage(translate, lastAction, reportAttributesDerived)); + result.alternateText = Parser.htmlToText(getUnreportedTransactionMessage(translate, lastAction)); } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_CUSTOM_UNIT_RATE) { result.alternateText = getReportActionMessageText(lastAction) ?? ''; } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_INTEGRATION) { @@ -1292,7 +1292,7 @@ function getOptionData({ } else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_OWNERSHIP) { result.alternateText = Parser.htmlToText(getUpdatedOwnershipMessage(translate, lastAction, policy)); } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - result.alternateText = Parser.htmlToText(getMovedTransactionMessage(translate, lastAction, reportAttributesDerived)); + result.alternateText = Parser.htmlToText(getMovedTransactionMessage(translate, lastAction)); } else if (isActionOfType(lastAction, CONST.REPORT.ACTIONS.TYPE.SETTLEMENT_ACCOUNT_LOCKED)) { result.alternateText = Parser.htmlToText(getSettlementAccountLockedMessage(translate, lastAction)); } else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && lastActorDisplayName && lastMessageTextFromReport) { diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 4a1a1a2b0cd6..7a36c3ebf807 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1143,7 +1143,7 @@ function getShareDestination( policy: OnyxEntry, conciergeReportID: string | undefined, translate: LocalizedTranslate, - reportAttributes?: OnyxTypes.ReportAttributesDerivedValue['reports'], + reportName?: string, ): ShareDestination { const isOneOnOneChat = ReportUtils.isOneOnOneChat(report); @@ -1170,7 +1170,7 @@ function getShareDestination( } return { icons: ReportUtils.getIcons(report, formatPhoneNumber, translate, personalDetails, FallbackAvatar), - displayName: getReportName(report, report?.reportID ? reportAttributes?.[report.reportID]?.reportName : undefined), + displayName: getReportName(report, reportName), subtitle, displayNamesWithTooltips, shouldUseFullTitleToDisplay: ReportUtils.shouldUseFullTitleToDisplay(report), diff --git a/src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx b/src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx index f02a125ee5bc..39d345767275 100755 --- a/src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx +++ b/src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx @@ -447,7 +447,6 @@ function BaseReportActionContextMenu({ isOffline, conciergeReportID, delegateAccountID, - reportAttributes, originalReportOfUnapprovedTransaction, memberChangeLogRoomReportName, }; diff --git a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx index 67e4ef6861ee..ba0046b66816 100644 --- a/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx +++ b/src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx @@ -227,7 +227,6 @@ import type { ReportAction, ReportActionReactions, ReportActions, - ReportAttributesDerivedValue, Report as ReportType, Transaction, TransactionViolations, @@ -355,7 +354,6 @@ type ContextMenuActionPayload = { conciergeReportID: string | undefined; originalReportOfUnapprovedTransaction?: OnyxEntry; delegateAccountID: number | undefined; - reportAttributes: ReportAttributesDerivedValue['reports'] | undefined; memberChangeLogRoomReportName: string | undefined; }; @@ -936,7 +934,6 @@ const ContextMenuActions: ContextMenuAction[] = [ harvestReportOriginalID, currentUserPersonalDetails, bankAccountList, - reportAttributes, originalReportOfUnapprovedTransaction, personalDetails, memberChangeLogRoomReportName, @@ -958,7 +955,6 @@ const ContextMenuActions: ContextMenuAction[] = [ const displayMessage = getReportPreviewMessageForCopy({ reportOrID: iouReportID, iouReportAction: reportAction, - reportAttributes, }); Clipboard.setString(displayMessage); } else if (isTaskActionReportActionsUtils(reportAction)) { @@ -1114,7 +1110,7 @@ const ContextMenuActions: ContextMenuAction[] = [ } else if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_OWNERSHIP) { setClipboardMessage(Parser.htmlToText(getUpdatedOwnershipMessage(translate, reportAction, policy) ?? '')); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION)) { - setClipboardMessage(getUnreportedTransactionMessage(translate, reportAction, reportAttributes)); + setClipboardMessage(getUnreportedTransactionMessage(translate, reportAction)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.MARKED_REIMBURSED)) { Clipboard.setString(getMarkedReimbursedMessage(translate, reportAction)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.REIMBURSED)) { @@ -1299,7 +1295,7 @@ const ContextMenuActions: ContextMenuAction[] = [ ) { setClipboardMessage(getChangedApproverActionMessage(translate, reportAction)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.MOVED_TRANSACTION)) { - setClipboardMessage(getMovedTransactionMessage(translate, reportAction, reportAttributes)); + setClipboardMessage(getMovedTransactionMessage(translate, reportAction)); } else if (isMovedAction(reportAction)) { setClipboardMessage(getMovedActionMessage(translate, reportAction, originalReport)); } else if (isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_CARD_FRAUD_ALERT)) { diff --git a/src/pages/tasks/DynamicNewTaskPage.tsx b/src/pages/tasks/DynamicNewTaskPage.tsx index d8df54a1db06..1626681e7ea9 100644 --- a/src/pages/tasks/DynamicNewTaskPage.tsx +++ b/src/pages/tasks/DynamicNewTaskPage.tsx @@ -14,7 +14,7 @@ import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; import usePressLoading from '@hooks/usePressLoading'; -import useReportAttributes from '@hooks/useReportAttributes'; +import {useDerivedReportNameByReportID} from '@hooks/useReportAttributes'; import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -40,7 +40,7 @@ function DynamicNewTaskPage() { const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const reportAttributes = useReportAttributes(); + const shareDestinationReportName = useDerivedReportNameByReportID(task?.shareDestination); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const [taskCreatorAndAssigneeDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, { selector: personalDetailsListSelector([currentUserPersonalDetails.accountID, task?.assigneeAccountID]), @@ -56,7 +56,7 @@ function DynamicNewTaskPage() { translate, ); const shareDestination = task?.shareDestination - ? getShareDestination(parentReport, personalDetails, localeCompare, formatPhoneNumber, policy, conciergeReportID, translate, reportAttributes) + ? getShareDestination(parentReport, personalDetails, localeCompare, formatPhoneNumber, policy, conciergeReportID, translate, shareDestinationReportName) : undefined; const ancestors = useAncestors(parentReport); const taskKey = `${task?.assignee}|${task?.assigneeAccountID}|${task?.description}|${task?.parentReportID}|${task?.shareDestination}|${task?.title}`; diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 38d9a110de96..d5fd9c9ba919 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -16178,7 +16178,7 @@ describe('ReportUtils', () => { expect(result).toBe('Expense Report 2025-01-15'); }); - it('getReportPreviewMessageForCopy should use the report name from the reportAttributes param', async () => { + it('getReportPreviewMessageForCopy should use the report name from the report attributes derived value', async () => { const report = LHNTestUtils.getFakeReport(); report.reportName = 'Stale Report Name'; const reportAction: ReportAction = { @@ -16196,13 +16196,14 @@ describe('ReportUtils', () => { reportErrors: {}, }, }; + await Onyx.set(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {reports: reportAttributes, locale: CONST.LOCALES.DEFAULT}); + await waitForBatchedUpdates(); - // When called with reportAttributes that provide a report name, it should be preferred over the report's own name + // When the derived report attributes provide a report name, it should be preferred over the report's own name const result = getReportPreviewMessageForCopy({ reportOrID: report, iouReportAction: reportAction, originalReportAction: reportAction, - reportAttributes, }); expect(result).toBe('Computed Report Name'); });