From 61b7e0edbbf97800846b0b400c91e2b2c78765a5 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Sat, 7 Mar 2026 10:55:19 +0400 Subject: [PATCH 01/17] refactor: Remove deprecated report name functions in ReportUtils --- .../MoneyRequestReportPreviewContent.tsx | 7 ++----- src/libs/ModifiedExpenseMessage.ts | 7 +++---- .../Notification/LocalNotification/BrowserNotifications.ts | 5 ++--- src/libs/SearchQueryUtils.ts | 5 ++--- src/libs/actions/Task.ts | 5 ++--- tests/ui/MoneyRequestReportPreview.test.tsx | 5 ++--- 6 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx index d03273ae7049a..1c1e6013e781b 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContent.tsx @@ -48,7 +48,7 @@ import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportU import Navigation from '@libs/Navigation/Navigation'; import {getConnectedIntegration, hasDynamicExternalWorkflow} from '@libs/PolicyUtils'; import {hasPendingDEWSubmit} from '@libs/ReportActionsUtils'; -import {getInvoicePayerName} from '@libs/ReportNameUtils'; +import {getInvoicePayerName, getReportName} from '@libs/ReportNameUtils'; import getReportPreviewAction from '@libs/ReportPreviewActionUtils'; import { areAllRequestsBeingSmartScanned as areAllRequestsBeingSmartScannedReportUtils, @@ -58,7 +58,6 @@ import { getMoneyRequestSpendBreakdown, getNonHeldAndFullAmount, getPolicyName, - getReportName, getReportStatusColorStyle, getReportStatusTranslation, getTransactionsWithReceipts, @@ -883,9 +882,7 @@ function MoneyRequestReportPreviewContent({ style={[styles.headerText]} testID="MoneyRequestReportPreview-reportName" > - {/* This will be fixed as follow up https://github.com/Expensify/App/pull/75357 */} - {/* eslint-disable-next-line @typescript-eslint/no-deprecated */} - {getReportName({report: iouReport, reportAttributes}) || action.childReportName} + {getReportName(iouReport, reportAttributes) || action.childReportName} diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index a3b72f7998afe..6f36870a623e2 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -24,9 +24,9 @@ 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, getPolicyExpenseChatName} from './ReportNameUtils'; +import {buildReportNameFromParticipantNames, getPolicyExpenseChatName, getReportName} from './ReportNameUtils'; // eslint-disable-next-line import/no-cycle -import {getPolicyName, getReportName, getRootParentReport, isPolicyExpenseChat, isSelfDM} from './ReportUtils'; +import {getPolicyName, getRootParentReport, isPolicyExpenseChat, isSelfDM} from './ReportUtils'; import {getFormattedAttendees, getTagArrayFromName} from './TransactionUtils'; import {isInvalidMerchantValue} from './ValidationUtils'; @@ -201,8 +201,7 @@ function getMovedFromOrToReportMessage( } if (movedFromReport) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - const originReportName = getReportName({report: movedFromReport}); + const originReportName = getReportName(movedFromReport); return translate('iou.movedFromReport', originReportName ?? ''); } } diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts index 3df24270115a4..3b4144fe0a1ea 100644 --- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts +++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts @@ -5,6 +5,7 @@ import EXPENSIFY_ICON_URL from '@assets/images/expensify-logo-round-clearspace.p import * as AppUpdate from '@libs/actions/AppUpdate'; import {getForReportAction} from '@libs/ModifiedExpenseMessage'; import {getTextFromHtml} from '@libs/ReportActionsUtils'; +import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; import type {Report, ReportAction} from '@src/types/onyx'; @@ -113,9 +114,7 @@ export default { } if (isRoomOrGroupChat) { - // Will be fixed in https://github.com/Expensify/App/issues/76852 - // eslint-disable-next-line @typescript-eslint/no-deprecated - const roomName = ReportUtils.getReportName({report}); + const roomName = getReportName(report); title = roomName; body = `${plainTextPerson}: ${plainTextMessage}`; } else { diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index c8971df55ea69..e09d7da9fb79d 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -44,7 +44,7 @@ import navigationRef from './Navigation/navigationRef'; import type {SearchFullscreenNavigatorParamList} from './Navigation/types'; import {getDisplayNameOrDefault, getPersonalDetailByEmail} from './PersonalDetailsUtils'; import {getCleanedTagName, getTagNamesFromTagsLists} from './PolicyUtils'; -import {getReportName} from './ReportUtils'; +import {getReportName} from './ReportNameUtils'; import {parse as parseSearchQuery} from './SearchParser/searchParser'; import StringUtils from './StringUtils'; import {hashText} from './UserUtils'; @@ -1129,8 +1129,7 @@ function getFilterDisplayValue( return getCardDescription(cardList?.[cardID], translate) || filterValue; } if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - return getReportName({report: reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filterValue}`]}) || filterValue; + return getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filterValue}`]) || filterValue; } if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT) { const frontendAmount = convertToFrontendAmountAsInteger(Number(filterValue)); diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 451286240055e..9da25604a0495 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -14,6 +14,7 @@ import NetworkConnection from '@libs/NetworkConnection'; import * as OptionsListUtils from '@libs/OptionsListUtils'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; +import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import {buildOptimisticSnapshotData} from '@libs/SearchQueryUtils'; import playSound, {SOUNDS} from '@libs/Sound'; @@ -1047,9 +1048,7 @@ function getShareDestination( } return { icons: ReportUtils.getIcons(report, LocalePhoneNumber.formatPhoneNumber, personalDetails, Expensicons.FallbackAvatar), - // Will be fixed in https://github.com/Expensify/App/issues/76852 - // eslint-disable-next-line @typescript-eslint/no-deprecated - displayName: ReportUtils.getReportName({report}), + displayName: getReportName(report), subtitle, displayNamesWithTooltips, shouldUseFullTitleToDisplay: ReportUtils.shouldUseFullTitleToDisplay(report), diff --git a/tests/ui/MoneyRequestReportPreview.test.tsx b/tests/ui/MoneyRequestReportPreview.test.tsx index 8f097130c52e8..af246966f7f7d 100644 --- a/tests/ui/MoneyRequestReportPreview.test.tsx +++ b/tests/ui/MoneyRequestReportPreview.test.tsx @@ -15,6 +15,7 @@ import DateUtils from '@libs/DateUtils'; import {getFormattedCreated, isManagedCardTransaction} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import * as ReportActionUtils from '@src/libs/ReportActionsUtils'; +import * as ReportNameUtils from '@src/libs/ReportNameUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; @@ -153,9 +154,7 @@ describe('MoneyRequestReportPreview', () => { }); await waitForBatchedUpdatesWithAct(); - // This will be fixed as follow up https://github.com/Expensify/App/pull/75357 - // eslint-disable-next-line @typescript-eslint/no-deprecated - expect(screen.getByText(ReportUtils.getReportName({report: mockIOUReport}))).toBeOnTheScreen(); + expect(screen.getByText(ReportNameUtils.getReportName(mockIOUReport))).toBeOnTheScreen(); for (const transaction of arrayOfTransactions) { const {transactionDisplayAmount, transactionHeaderText} = getTransactionDisplayAmountAndHeaderText(transaction); From 4f089301b24eea05e9b839d208c22f528ca37d1e Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Sat, 7 Mar 2026 12:07:19 +0400 Subject: [PATCH 02/17] refactor: pass report attributes to getReportName calls --- .../SearchPageHeaderInput.tsx | 6 +- .../SearchRouter/buildSubstitutionsMap.ts | 16 ++- src/libs/ModifiedExpenseMessage.ts | 13 +- .../LocalNotification/BrowserNotifications.ts | 12 +- .../Notification/LocalNotification/index.ts | 6 +- .../Notification/LocalNotification/types.ts | 4 +- src/libs/SearchQueryUtils.ts | 60 +++++++-- tests/unit/Search/SearchQueryUtilsTest.ts | 120 ++++++++++-------- 8 files changed, 153 insertions(+), 84 deletions(-) diff --git a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx index 06f5a83f9c14b..b5810fa743310 100644 --- a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx +++ b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx @@ -24,6 +24,7 @@ import useFeedKeysWithAssignedCards from '@hooks/useFeedKeysWithAssignedCards'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -63,6 +64,7 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo const personalDetails = usePersonalDetails(); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); + const reportAttributes = useReportAttributes(); const taxRates = useMemo(() => getAllTaxRates(policies), [policies]); const [personalAndWorkspaceCards] = useOnyx(ONYXKEYS.DERIVED.PERSONAL_AND_WORKSPACE_CARD_LIST); const [allFeeds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER); @@ -82,6 +84,7 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo autoCompleteWithSpace: true, translate, feedKeysWithCards, + reportAttributes, }); const [searchContext] = useOnyx(ONYXKEYS.SEARCH_CONTEXT); @@ -139,9 +142,10 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo policies, currentUserAccountID, translate, + reportAttributes, ); setAutocompleteSubstitutions(substitutionsMap); - }, [allFeeds, personalAndWorkspaceCards, originalInputQuery, personalDetails, reports, taxRates, policies, currentUserAccountID, translate]); + }, [allFeeds, personalAndWorkspaceCards, originalInputQuery, personalDetails, reportAttributes, reports, taxRates, policies, currentUserAccountID, translate]); useEffect(() => { if (searchRouterListVisible) { diff --git a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts index 06b7bd51c55a9..47a72df7c347a 100644 --- a/src/components/Search/SearchRouter/buildSubstitutionsMap.ts +++ b/src/components/Search/SearchRouter/buildSubstitutionsMap.ts @@ -4,7 +4,7 @@ import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Se import {parse} from '@libs/SearchParser/autocompleteParser'; import {getFilterDisplayValue} from '@libs/SearchQueryUtils'; import CONST from '@src/CONST'; -import type {CardFeeds, CardList, PersonalDetailsList, Policy, Report} from '@src/types/onyx'; +import type {CardFeeds, CardList, PersonalDetailsList, Policy, Report, ReportAttributesDerivedValue} from '@src/types/onyx'; import type {SubstitutionMap} from './getQueryWithSubstitutions'; const getSubstitutionsKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`; @@ -35,6 +35,7 @@ function buildSubstitutionsMap( policies: OnyxCollection, currentUserAccountID: number, translate: LocalizedTranslate, + reportAttributes?: ReportAttributesDerivedValue['reports'], ): SubstitutionMap { const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]}; @@ -73,7 +74,18 @@ function buildSubstitutionsMap( filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.PAYER || filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.ATTENDEE ) { - const displayValue = getFilterDisplayValue(filterKey, filterValue, personalDetails, reports, cardList, cardFeeds, policies, currentUserAccountID, translate); + const displayValue = getFilterDisplayValue({ + filterName: filterKey, + filterValue, + personalDetails, + reports, + cardList, + cardFeeds, + policies, + currentUserAccountID, + translate, + reportAttributes, + }); // If displayValue === filterValue, then it means there is nothing to substitute, so we don't add any key to map if (displayValue !== filterValue) { diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 6f36870a623e2..15c4c9079bfff 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -6,7 +6,7 @@ import type {LocalizedTranslate} from '@components/LocaleContextProvider'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {Policy, PolicyTagLists, Report, ReportAction} from '@src/types/onyx'; +import type {Policy, PolicyTagLists, Report, ReportAction, ReportAttributesDerivedValue} from '@src/types/onyx'; import type {PersonalRulesModifiedFields, PolicyRulesModifiedFields} from '@src/types/onyx/OriginalMessage'; import ObjectUtils from '@src/types/utils/ObjectUtils'; import {getDecodedCategoryName, isCategoryMissing} from './CategoryUtils'; @@ -195,13 +195,14 @@ function getMovedFromOrToReportMessage( movedFromReport: OnyxEntry | undefined, movedToReport: OnyxEntry | undefined, currentUserLogin: string, + reportAttributes?: ReportAttributesDerivedValue['reports'], ): string | undefined { if (movedToReport) { return getForExpenseMovedFromSelfDM(translate, movedToReport, currentUserLogin); } if (movedFromReport) { - const originReportName = getReportName(movedFromReport); + const originReportName = getReportName(movedFromReport, reportAttributes); return translate('iou.movedFromReport', originReportName ?? ''); } } @@ -279,6 +280,7 @@ function getForReportAction({ movedToReport, policyForMovingExpensesID, currentUserLogin: currentUserLoginParam, + reportAttributes, }: { reportAction: OnyxEntry; policyID: string | undefined; @@ -286,6 +288,7 @@ function getForReportAction({ movedToReport?: OnyxEntry; policyForMovingExpensesID?: string; currentUserLogin?: string; + reportAttributes?: ReportAttributesDerivedValue['reports']; }): string { // Temporary fallback to storedCurrentUserLogin since currentUserLogin can be empty string. // Remove once all callers pass currentUserLogin explicitly and the migration to getForReportActionTemp is complete. @@ -298,7 +301,7 @@ function getForReportAction({ } // eslint-disable-next-line @typescript-eslint/no-deprecated - const movedFromOrToReportMessage = getMovedFromOrToReportMessage(translateLocal, movedFromReport, movedToReport, currentUserLogin); + const movedFromOrToReportMessage = getMovedFromOrToReportMessage(translateLocal, movedFromReport, movedToReport, currentUserLogin, reportAttributes); if (movedFromOrToReportMessage) { return movedFromOrToReportMessage; } @@ -600,6 +603,7 @@ function getForReportActionTemp({ movedToReport, policyTags, currentUserLogin: currentUserLoginParam, + reportAttributes, }: { translate: LocalizedTranslate; reportAction: OnyxEntry; @@ -608,6 +612,7 @@ function getForReportActionTemp({ movedToReport?: OnyxEntry; policyTags: OnyxEntry; currentUserLogin: string; + reportAttributes?: ReportAttributesDerivedValue['reports']; }): string { // Temporary fallback to storedCurrentUserLogin since currentUserLogin can be empty string. // Remove once all callers pass currentUserLogin explicitly and the migration to getForReportActionTemp is complete. @@ -619,7 +624,7 @@ function getForReportActionTemp({ return ''; } - const movedFromOrToReportMessage = getMovedFromOrToReportMessage(translate, movedFromReport, movedToReport, currentUserLogin); + const movedFromOrToReportMessage = getMovedFromOrToReportMessage(translate, movedFromReport, movedToReport, currentUserLogin, reportAttributes); if (movedFromOrToReportMessage) { return movedFromOrToReportMessage; } diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts index 3b4144fe0a1ea..49bee1e31089e 100644 --- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts +++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts @@ -8,7 +8,7 @@ import {getTextFromHtml} from '@libs/ReportActionsUtils'; import {getReportName} from '@libs/ReportNameUtils'; import * as ReportUtils from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import type {Report, ReportAction} from '@src/types/onyx'; +import type {Report, ReportAction, ReportAttributesDerivedValue} from '@src/types/onyx'; import SafeString from '@src/utils/SafeString'; import type {LocalNotificationClickHandler, LocalNotificationData, LocalNotificationModifiedExpensePushParams} from './types'; @@ -95,7 +95,13 @@ export default { * * @param usesIcon true if notification uses right circular icon */ - pushReportCommentNotification(report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler, usesIcon = false) { + pushReportCommentNotification( + report: Report, + reportAction: ReportAction, + onClick: LocalNotificationClickHandler, + usesIcon = false, + reportAttributes?: ReportAttributesDerivedValue['reports'], + ) { let title; let body; const icon = usesIcon ? EXPENSIFY_ICON_URL : ''; @@ -114,7 +120,7 @@ export default { } if (isRoomOrGroupChat) { - const roomName = getReportName(report); + const roomName = getReportName(report, reportAttributes); title = roomName; body = `${plainTextPerson}: ${plainTextMessage}`; } else { diff --git a/src/libs/Notification/LocalNotification/index.ts b/src/libs/Notification/LocalNotification/index.ts index 24b07db283622..855d820b5e088 100644 --- a/src/libs/Notification/LocalNotification/index.ts +++ b/src/libs/Notification/LocalNotification/index.ts @@ -1,9 +1,9 @@ -import type {Report, ReportAction} from '@src/types/onyx'; +import type {Report, ReportAction, ReportAttributesDerivedValue} from '@src/types/onyx'; import BrowserNotifications from './BrowserNotifications'; import type {LocalNotificationClickHandler, LocalNotificationModifiedExpenseParams, LocalNotificationModule} from './types'; -function showCommentNotification(report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler) { - BrowserNotifications.pushReportCommentNotification(report, reportAction, onClick, true); +function showCommentNotification(report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler, reportAttributes?: ReportAttributesDerivedValue['reports']) { + BrowserNotifications.pushReportCommentNotification(report, reportAction, onClick, true, reportAttributes); } function showUpdateAvailableNotification() { diff --git a/src/libs/Notification/LocalNotification/types.ts b/src/libs/Notification/LocalNotification/types.ts index 0ed62ffda9c05..bc95add01b81f 100644 --- a/src/libs/Notification/LocalNotification/types.ts +++ b/src/libs/Notification/LocalNotification/types.ts @@ -1,6 +1,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import type ClearReportNotifications from '@libs/Notification/clearReportNotifications/types'; -import type {Report, ReportAction} from '@src/types/onyx'; +import type {Report, ReportAction, ReportAttributesDerivedValue} from '@src/types/onyx'; type LocalNotificationClickHandler = () => void; @@ -9,7 +9,7 @@ type LocalNotificationData = { }; type LocalNotificationModule = { - showCommentNotification: (report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler) => void; + showCommentNotification: (report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler, reportAttributes?: ReportAttributesDerivedValue['reports']) => void; showUpdateAvailableNotification: () => void; showModifiedExpenseNotification: (params: LocalNotificationModifiedExpenseParams) => void; clearReportNotifications: ClearReportNotifications; diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index e09d7da9fb79d..5bddb7c258bca 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -1096,21 +1096,36 @@ function getPolicyNameWithFallback(policyID: string, policies: OnyxCollection; + cardList: OnyxTypes.CardList | undefined; + cardFeeds: OnyxCollection; + policies: OnyxCollection; + currentUserAccountID: number; + translate: LocalizedTranslate; + feedKeysWithCards?: FeedKeysWithAssignedCards; + reportAttributes?: OnyxTypes.ReportAttributesDerivedValue['reports']; +}; + /** * Returns the human-readable "pretty" string for a specified filter value. */ -function getFilterDisplayValue( - filterName: string, - filterValue: string, - personalDetails: OnyxTypes.PersonalDetailsList | undefined, - reports: OnyxCollection, - cardList: OnyxTypes.CardList | undefined, - cardFeeds: OnyxCollection, - policies: OnyxCollection, - currentUserAccountID: number, - translate: LocalizedTranslate, - feedKeysWithCards?: FeedKeysWithAssignedCards, -) { +function getFilterDisplayValue({ + filterName, + filterValue, + personalDetails, + reports, + cardList, + cardFeeds, + policies, + currentUserAccountID, + translate, + feedKeysWithCards, + reportAttributes, +}: GetFilterDisplayValueParams) { if ( filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TO || @@ -1129,7 +1144,7 @@ function getFilterDisplayValue( return getCardDescription(cardList?.[cardID], translate) || filterValue; } if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.IN) { - return getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filterValue}`]) || filterValue; + return getReportName(reports?.[`${ONYXKEYS.COLLECTION.REPORT}${filterValue}`], reportAttributes) || filterValue; } if (filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.AMOUNT || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.TOTAL || filterName === CONST.SEARCH.SYNTAX_FILTER_KEYS.PURCHASE_AMOUNT) { const frontendAmount = convertToFrontendAmountAsInteger(Number(filterValue)); @@ -1170,6 +1185,7 @@ function getDisplayQueryFiltersForKey( currentUserAccountID: number, translate: LocalizedTranslate, feedKeysWithCards?: FeedKeysWithAssignedCards, + reportAttributes?: OnyxTypes.ReportAttributesDerivedValue['reports'], ) { if (key === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) { const taxRateIDs = queryFilter.map((filter) => filter.value.toString()); @@ -1224,7 +1240,19 @@ function getDisplayQueryFiltersForKey( return queryFilter.map((filter) => ({ operator: filter.operator, - value: getFilterDisplayValue(key, getUserFriendlyValue(filter.value.toString()), personalDetails, reports, cardList, cardFeeds, policies, currentUserAccountID, translate), + value: getFilterDisplayValue({ + filterName: key, + filterValue: getUserFriendlyValue(filter.value.toString()), + personalDetails, + reports, + cardList, + cardFeeds, + policies, + currentUserAccountID, + translate, + feedKeysWithCards, + reportAttributes, + }), })); } @@ -1302,6 +1330,7 @@ function buildUserReadableQueryString({ autoCompleteWithSpace = false, translate, feedKeysWithCards, + reportAttributes, }: { queryJSON: SearchQueryJSON; PersonalDetails: OnyxTypes.PersonalDetailsList | undefined; @@ -1314,6 +1343,7 @@ function buildUserReadableQueryString({ autoCompleteWithSpace: boolean; translate: LocalizedTranslate; feedKeysWithCards?: FeedKeysWithAssignedCards; + reportAttributes?: OnyxTypes.ReportAttributesDerivedValue['reports']; }) { const {type, status, groupBy, columns, policyID, rawFilterList, flatFilters: filters = [], limit} = queryJSON; @@ -1358,6 +1388,7 @@ function buildUserReadableQueryString({ currentUserAccountID, translate, feedKeysWithCards, + reportAttributes, ); if (!displayQueryFilters.length) { @@ -1407,6 +1438,7 @@ function buildUserReadableQueryString({ currentUserAccountID, translate, feedKeysWithCards, + reportAttributes, ); if (!displayQueryFilters.length) { diff --git a/tests/unit/Search/SearchQueryUtilsTest.ts b/tests/unit/Search/SearchQueryUtilsTest.ts index 444323137e27a..80c09b9568997 100644 --- a/tests/unit/Search/SearchQueryUtilsTest.ts +++ b/tests/unit/Search/SearchQueryUtilsTest.ts @@ -1079,17 +1079,17 @@ describe('SearchQueryUtils', () => { }, }; - const result = getFilterDisplayValue( - CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, - '99999', + const result = getFilterDisplayValue({ + filterName: CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, + filterValue: '99999', personalDetails, - mockReports, - mockCardList, - mockCardFeeds, - mockPolicies, + reports: mockReports, + cardList: mockCardList, + cardFeeds: mockCardFeeds, + policies: mockPolicies, currentUserAccountID, - translateLocal, - ); + translate: translateLocal, + }); expect(result).toBe('+15551234567'); expect(result).not.toContain('@expensify.sms'); @@ -1104,17 +1104,17 @@ describe('SearchQueryUtils', () => { }, }; - const result = getFilterDisplayValue( - CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, - '78901', + const result = getFilterDisplayValue({ + filterName: CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, + filterValue: '78901', personalDetails, - mockReports, - mockCardList, - mockCardFeeds, - mockPolicies, + reports: mockReports, + cardList: mockCardList, + cardFeeds: mockCardFeeds, + policies: mockPolicies, currentUserAccountID, - translateLocal, - ); + translate: translateLocal, + }); expect(result).toBe('Jane Doe'); }); @@ -1128,17 +1128,17 @@ describe('SearchQueryUtils', () => { }, }; - const result = getFilterDisplayValue( - CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, - '12345', + const result = getFilterDisplayValue({ + filterName: CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, + filterValue: '12345', personalDetails, - mockReports, - mockCardList, - mockCardFeeds, - mockPolicies, + reports: mockReports, + cardList: mockCardList, + cardFeeds: mockCardFeeds, + policies: mockPolicies, currentUserAccountID, - translateLocal, - ); + translate: translateLocal, + }); expect(result).toBe(CONST.SEARCH.ME); }); @@ -1146,17 +1146,17 @@ describe('SearchQueryUtils', () => { it('should return fallback value when personal details not found', () => { const personalDetails = {}; - const result = getFilterDisplayValue( - CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, - '88888', + const result = getFilterDisplayValue({ + filterName: CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, + filterValue: '88888', personalDetails, - mockReports, - mockCardList, - mockCardFeeds, - mockPolicies, + reports: mockReports, + cardList: mockCardList, + cardFeeds: mockCardFeeds, + policies: mockPolicies, currentUserAccountID, - translateLocal, - ); + translate: translateLocal, + }); expect(result).toBe('88888'); }); @@ -1170,17 +1170,17 @@ describe('SearchQueryUtils', () => { }, }; - const result = getFilterDisplayValue( - CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, - '77777', + const result = getFilterDisplayValue({ + filterName: CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM, + filterValue: '77777', personalDetails, - mockReports, - mockCardList, - mockCardFeeds, - mockPolicies, + reports: mockReports, + cardList: mockCardList, + cardFeeds: mockCardFeeds, + policies: mockPolicies, currentUserAccountID, - translateLocal, - ); + translate: translateLocal, + }); expect(result).toBe('Custom Name'); expect(result).not.toContain('@expensify.sms'); @@ -1195,17 +1195,17 @@ describe('SearchQueryUtils', () => { }, }; - const result = getFilterDisplayValue( - CONST.SEARCH.SYNTAX_FILTER_KEYS.TO, - '66666', + const result = getFilterDisplayValue({ + filterName: CONST.SEARCH.SYNTAX_FILTER_KEYS.TO, + filterValue: '66666', personalDetails, - mockReports, - mockCardList, - mockCardFeeds, - mockPolicies, + reports: mockReports, + cardList: mockCardList, + cardFeeds: mockCardFeeds, + policies: mockPolicies, currentUserAccountID, - translateLocal, - ); + translate: translateLocal, + }); expect(result).toBe('+15551112222'); expect(result).not.toContain('@expensify.sms'); @@ -1228,7 +1228,17 @@ describe('SearchQueryUtils', () => { ]; for (const filterKey of filterKeys) { - const result = getFilterDisplayValue(filterKey, '55555', personalDetails, mockReports, mockCardList, mockCardFeeds, mockPolicies, currentUserAccountID, translateLocal); + const result = getFilterDisplayValue({ + filterName: filterKey, + filterValue: '55555', + personalDetails, + reports: mockReports, + cardList: mockCardList, + cardFeeds: mockCardFeeds, + policies: mockPolicies, + currentUserAccountID, + translate: translateLocal, + }); expect(result).toBe('+15553334444'); expect(result).not.toContain('@expensify.sms'); From 8cec2a81d4ee714452110db1adceaf67dd192988 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Sat, 7 Mar 2026 13:41:40 +0400 Subject: [PATCH 03/17] refactor: add reportAttributes to notification functions --- src/libs/Notification/LocalNotification/index.ts | 4 ++-- src/libs/Notification/LocalNotification/types.ts | 1 + src/libs/actions/Report/index.ts | 13 ++++++++++--- src/libs/actions/User.ts | 10 ++++++++-- tests/actions/ReportTest.ts | 2 +- tests/ui/MoneyRequestReportPreview.test.tsx | 4 ++-- 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/libs/Notification/LocalNotification/index.ts b/src/libs/Notification/LocalNotification/index.ts index 855d820b5e088..45f59a2b56a5e 100644 --- a/src/libs/Notification/LocalNotification/index.ts +++ b/src/libs/Notification/LocalNotification/index.ts @@ -10,8 +10,8 @@ function showUpdateAvailableNotification() { BrowserNotifications.pushUpdateAvailableNotification(); } -function showModifiedExpenseNotification({report, reportAction, movedFromReport, movedToReport, currentUserLogin, onClick}: LocalNotificationModifiedExpenseParams) { - BrowserNotifications.pushModifiedExpenseNotification({report, reportAction, movedFromReport, movedToReport, onClick, usesIcon: true, currentUserLogin}); +function showModifiedExpenseNotification({report, reportAction, movedFromReport, movedToReport, currentUserLogin, onClick, reportAttributes}: LocalNotificationModifiedExpenseParams) { + BrowserNotifications.pushModifiedExpenseNotification({report, reportAction, movedFromReport, movedToReport, onClick, usesIcon: true, currentUserLogin, reportAttributes}); } function clearReportNotifications(reportID: string | undefined) { diff --git a/src/libs/Notification/LocalNotification/types.ts b/src/libs/Notification/LocalNotification/types.ts index bc95add01b81f..f1e068e8e39db 100644 --- a/src/libs/Notification/LocalNotification/types.ts +++ b/src/libs/Notification/LocalNotification/types.ts @@ -22,6 +22,7 @@ type LocalNotificationModifiedExpenseParams = { movedFromReport?: OnyxEntry; movedToReport?: OnyxEntry; currentUserLogin: string; + reportAttributes?: ReportAttributesDerivedValue['reports']; }; type LocalNotificationModifiedExpensePushParams = LocalNotificationModifiedExpenseParams & { diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index da59bcf2d1806..16b4572d59164 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -208,6 +208,7 @@ import type { Report, ReportAction, ReportActionReactions, + ReportAttributesDerivedValue, ReportNextStepDeprecated, ReportUserIsTyping, Transaction, @@ -3961,7 +3962,13 @@ function shouldShowReportActionNotification(reportID: string, currentUserAccount return true; } -function showReportActionNotification(reportID: string, reportAction: ReportAction, currentUserAccountID: number, currentUserLogin: string) { +function showReportActionNotification( + reportID: string, + reportAction: ReportAction, + currentUserAccountID: number, + currentUserLogin: string, + reportAttributes?: ReportAttributesDerivedValue['reports'], +) { if (!shouldShowReportActionNotification(reportID, currentUserAccountID, reportAction)) { return; } @@ -3980,9 +3987,9 @@ function showReportActionNotification(reportID: string, reportAction: ReportActi if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE) { const movedFromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(reportAction, CONST.REPORT.MOVE_TYPE.FROM)}`]; const movedToReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(reportAction, CONST.REPORT.MOVE_TYPE.TO)}`]; - LocalNotification.showModifiedExpenseNotification({report, reportAction, onClick, movedFromReport, movedToReport, currentUserLogin}); + LocalNotification.showModifiedExpenseNotification({report, reportAction, onClick, movedFromReport, movedToReport, currentUserLogin, reportAttributes}); } else { - LocalNotification.showCommentNotification(report, reportAction, onClick); + LocalNotification.showCommentNotification(report, reportAction, onClick, reportAttributes); } notifyNewAction(reportID, undefined, reportAction.actorAccountID === currentUserAccountID); diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 18fa9b2bc343f..90943baf41f90 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -49,7 +49,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ExpenseRuleForm, MerchantRuleForm} from '@src/types/form'; -import type {AppReview, BlockedFromConcierge, CustomStatusDraft, ExpenseRule, LoginList, Policy} from '@src/types/onyx'; +import type {AppReview, BlockedFromConcierge, CustomStatusDraft, ExpenseRule, LoginList, Policy, ReportAttributesDerivedValue} from '@src/types/onyx'; import type Login from '@src/types/onyx/Login'; import type {Errors} from '@src/types/onyx/OnyxCommon'; import type {AnyOnyxServerUpdate, OnyxServerUpdate, OnyxUpdateEvent} from '@src/types/onyx/OnyxUpdatesFromServer'; @@ -81,6 +81,12 @@ Onyx.connect({ callback: (value) => (allPolicies = value), }); +let reportAttributes: ReportAttributesDerivedValue | undefined; +Onyx.connect({ + key: ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, + callback: (value) => (reportAttributes = value), +}); + type DomainOnyxUpdate = | OnyxUpdate<`${typeof ONYXKEYS.COLLECTION.DOMAIN}${string}`> | OnyxUpdate<`${typeof ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${string}`> @@ -713,7 +719,7 @@ function triggerNotifications(onyxUpdates: Array { }) .then(() => { // Ensure we show a notification for this new report action - expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN); + expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); }); }); diff --git a/tests/ui/MoneyRequestReportPreview.test.tsx b/tests/ui/MoneyRequestReportPreview.test.tsx index af246966f7f7d..2765c419ef1c1 100644 --- a/tests/ui/MoneyRequestReportPreview.test.tsx +++ b/tests/ui/MoneyRequestReportPreview.test.tsx @@ -15,7 +15,7 @@ import DateUtils from '@libs/DateUtils'; import {getFormattedCreated, isManagedCardTransaction} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import * as ReportActionUtils from '@src/libs/ReportActionsUtils'; -import * as ReportNameUtils from '@src/libs/ReportNameUtils'; +import {getReportName} from '@src/libs/ReportNameUtils'; import * as ReportUtils from '@src/libs/ReportUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report, Transaction, TransactionViolation, TransactionViolations} from '@src/types/onyx'; @@ -154,7 +154,7 @@ describe('MoneyRequestReportPreview', () => { }); await waitForBatchedUpdatesWithAct(); - expect(screen.getByText(ReportNameUtils.getReportName(mockIOUReport))).toBeOnTheScreen(); + expect(screen.getByText(getReportName(mockIOUReport))).toBeOnTheScreen(); for (const transaction of arrayOfTransactions) { const {transactionDisplayAmount, transactionHeaderText} = getTransactionDisplayAmountAndHeaderText(transaction); From 44f626279bb244856f164b2b816a33c19cde08a8 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:07:05 +0400 Subject: [PATCH 04/17] refactor: pass reportAttributes to getShareDestination function --- src/libs/actions/Task.ts | 3 ++- src/pages/tasks/NewTaskPage.tsx | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts index 9da25604a0495..f54682cfd0d5e 100644 --- a/src/libs/actions/Task.ts +++ b/src/libs/actions/Task.ts @@ -1021,6 +1021,7 @@ function getShareDestination( reports: OnyxCollection, personalDetails: OnyxEntry, localeCompare: LocaleContextProps['localeCompare'], + reportAttributes?: OnyxTypes.ReportAttributesDerivedValue['reports'], ): ShareDestination { const report = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; @@ -1048,7 +1049,7 @@ function getShareDestination( } return { icons: ReportUtils.getIcons(report, LocalePhoneNumber.formatPhoneNumber, personalDetails, Expensicons.FallbackAvatar), - displayName: getReportName(report), + displayName: getReportName(report, reportAttributes), subtitle, displayNamesWithTooltips, shouldUseFullTitleToDisplay: ReportUtils.shouldUseFullTitleToDisplay(report), diff --git a/src/pages/tasks/NewTaskPage.tsx b/src/pages/tasks/NewTaskPage.tsx index 62d6e7510abdb..3a880fb6b9888 100644 --- a/src/pages/tasks/NewTaskPage.tsx +++ b/src/pages/tasks/NewTaskPage.tsx @@ -13,6 +13,7 @@ import useAncestors from '@hooks/useAncestors'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings'; import useThemeStyles from '@hooks/useThemeStyles'; import blurActiveElement from '@libs/Accessibility/blurActiveElement'; @@ -35,6 +36,7 @@ function NewTaskPage({route}: NewTaskPageProps) { const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); const [quickAction] = useOnyx(ONYXKEYS.NVP_QUICK_ACTION_GLOBAL_CREATE); + const reportAttributes = useReportAttributes(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const styles = useThemeStyles(); const {translate, formatPhoneNumber, localeCompare} = useLocalize(); @@ -45,7 +47,7 @@ function NewTaskPage({route}: NewTaskPageProps) { localeCompare, formatPhoneNumber, ); - const shareDestination = task?.shareDestination ? getShareDestination(task.shareDestination, reports, personalDetails, localeCompare) : undefined; + const shareDestination = task?.shareDestination ? getShareDestination(task.shareDestination, reports, personalDetails, localeCompare, reportAttributes) : undefined; const parentReport = task?.shareDestination ? reports?.[`${ONYXKEYS.COLLECTION.REPORT}${task.shareDestination}`] : undefined; const ancestors = useAncestors(parentReport); const taskKey = `${task?.assignee}|${task?.assigneeAccountID}|${task?.description}|${task?.parentReportID}|${task?.shareDestination}|${task?.title}`; From 677c770c600516e02111ab44f57b87a785878388 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:35:35 +0400 Subject: [PATCH 05/17] refactor: integrate reportAttributes into SearchAutocompleteList, useSearchTypeMenu, BrowserNotifications, and SavedSearchList components --- src/components/Search/SearchAutocompleteList.tsx | 3 +++ src/hooks/useSearchTypeMenu.tsx | 3 +++ .../LocalNotification/BrowserNotifications.ts | 12 +++++++++++- src/pages/Search/SavedSearchList.tsx | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/components/Search/SearchAutocompleteList.tsx b/src/components/Search/SearchAutocompleteList.tsx index 615f27e05d4e2..76ce22641f892 100644 --- a/src/components/Search/SearchAutocompleteList.tsx +++ b/src/components/Search/SearchAutocompleteList.tsx @@ -18,6 +18,7 @@ import useFeedKeysWithAssignedCards from '@hooks/useFeedKeysWithAssignedCards'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import FS from '@libs/Fullstory'; @@ -149,6 +150,7 @@ function SearchAutocompleteList({ const [betas] = useOnyx(ONYXKEYS.BETAS); const feedKeysWithCards = useFeedKeysWithAssignedCards(); + const reportAttributes = useReportAttributes(); const [draftComments] = useOnyx(ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT); const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING); const [recentSearches, recentSearchesMetadata] = useOnyx(ONYXKEYS.RECENT_SEARCHES); @@ -315,6 +317,7 @@ function SearchAutocompleteList({ autoCompleteWithSpace: false, translate, feedKeysWithCards, + reportAttributes, }) : query, singleIcon: expensifyIcons.History, diff --git a/src/hooks/useSearchTypeMenu.tsx b/src/hooks/useSearchTypeMenu.tsx index 6682a0936a87b..7f3274a5017e7 100644 --- a/src/hooks/useSearchTypeMenu.tsx +++ b/src/hooks/useSearchTypeMenu.tsx @@ -23,6 +23,7 @@ import useFeedKeysWithAssignedCards from './useFeedKeysWithAssignedCards'; import {useMemoizedLazyExpensifyIcons} from './useLazyAsset'; import useLocalize from './useLocalize'; import useOnyx from './useOnyx'; +import useReportAttributes from './useReportAttributes'; import useSearchTypeMenuSections from './useSearchTypeMenuSections'; import useSingleExecution from './useSingleExecution'; import useTheme from './useTheme'; @@ -69,6 +70,7 @@ export default function useSearchTypeMenu(queryJSON: SearchQueryJSON) { const [allFeeds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER); const feedKeysWithCards = useFeedKeysWithAssignedCards(); + const reportAttributes = useReportAttributes(); const flattenedMenuItems = useMemo(() => typeMenuSections.flatMap((section) => section.menuItems), [typeMenuSections]); // this is a performance fix, rendering popover menu takes a lot of time and we don't need this component initially, that's why we postpone rendering it until everything else is rendered @@ -115,6 +117,7 @@ export default function useSearchTypeMenu(queryJSON: SearchQueryJSON) { autoCompleteWithSpace: false, translate, feedKeysWithCards, + reportAttributes, }); } diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts index 49bee1e31089e..dfd908284f04d 100644 --- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts +++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts @@ -135,7 +135,16 @@ export default { push(title, body, icon, data, onClick); }, - pushModifiedExpenseNotification({report, reportAction, movedFromReport, movedToReport, onClick, usesIcon = false, currentUserLogin}: LocalNotificationModifiedExpensePushParams) { + pushModifiedExpenseNotification({ + report, + reportAction, + movedFromReport, + movedToReport, + onClick, + usesIcon = false, + currentUserLogin, + reportAttributes, + }: LocalNotificationModifiedExpensePushParams) { const title = reportAction.person?.map((f) => f.text).join(', ') ?? ''; const body = getForReportAction({ reportAction, @@ -143,6 +152,7 @@ export default { movedFromReport, movedToReport, currentUserLogin, + reportAttributes, }); const icon = usesIcon ? EXPENSIFY_ICON_URL : ''; const data = { diff --git a/src/pages/Search/SavedSearchList.tsx b/src/pages/Search/SavedSearchList.tsx index 8080a6f59d892..256893b61198b 100644 --- a/src/pages/Search/SavedSearchList.tsx +++ b/src/pages/Search/SavedSearchList.tsx @@ -10,6 +10,7 @@ import useFeedKeysWithAssignedCards from '@hooks/useFeedKeysWithAssignedCards'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import useThemeStyles from '@hooks/useThemeStyles'; import {setSearchContext} from '@libs/actions/Search'; import {mergeCardListWithWorkspaceFeeds} from '@libs/CardUtils'; @@ -43,6 +44,7 @@ function SavedSearchList({hash}: SavedSearchListProps) { const [allFeeds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER); const feedKeysWithCards = useFeedKeysWithAssignedCards(); const [currentUserAccountID = -1] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector}); + const reportAttributes = useReportAttributes(); const {showDeleteModal} = useDeleteSavedSearch(); const { @@ -74,6 +76,7 @@ function SavedSearchList({hash}: SavedSearchListProps) { autoCompleteWithSpace: false, translate, feedKeysWithCards, + reportAttributes, }); } From 6ef9cfc1722f220a90825fc201705c62fcc1ad84 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:08:18 +0400 Subject: [PATCH 06/17] refactor: pass reportAttributes to user event subscription --- .../AppNavigator/AuthScreensInitHandler.tsx | 15 +++++++++++---- src/libs/actions/User.ts | 18 ++++++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx index cc03376cf8202..16dec97d7de8d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx @@ -2,6 +2,7 @@ import {hasSeenTourSelector} from '@selectors/Onboarding'; import {useEffect, useRef} from 'react'; import {useInitialURLActions, useInitialURLState} from '@components/InitialURLContextProvider'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import {init, isClientTheLeader} from '@libs/ActiveClientManager'; import Log from '@libs/Log'; import getCurrentUrl from '@libs/Navigation/currentUrl'; @@ -22,14 +23,15 @@ import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {ReportAttributesDerivedValue} from '@src/types/onyx'; -function initializePusher(currentUserAccountID?: number) { +function initializePusher(currentUserAccountID?: number, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) { return Pusher.init({ appKey: CONFIG.PUSHER.APP_KEY, cluster: CONFIG.PUSHER.CLUSTER, authEndpoint: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/AuthenticatePusher?`, }).then(() => { - User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID); + User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID, getReportAttributes ?? (() => undefined)); }); } @@ -62,6 +64,11 @@ function AuthScreensInitHandler() { lastUpdateIDAppliedToClientRef.current = lastUpdateIDAppliedToClient; isLoadingAppRef.current = isLoadingApp; + const reportAttributes = useReportAttributes(); + // We use a ref so the Pusher callback (registered once on mount) always reads the latest value without re-subscribing. + const reportAttributesRef = useRef(reportAttributes); + reportAttributesRef.current = reportAttributes; + const handleNetworkReconnect = () => { if (isLoadingAppRef.current) { App.openApp(); @@ -76,7 +83,7 @@ function AuthScreensInitHandler() { return; } // This means sign in in RHP was successful, so we can subscribe to user events - initializePusher(session?.accountID); + initializePusher(session?.accountID, () => reportAttributesRef.current); }, [session?.accountID]); useEffect(() => { @@ -99,7 +106,7 @@ function AuthScreensInitHandler() { parentSpan: getSpan(CONST.TELEMETRY.SPAN_BOOTSPLASH.ROOT), }); PusherConnectionManager.init(); - initializePusher(session?.accountID).finally(() => { + initializePusher(session?.accountID, () => reportAttributesRef.current).finally(() => { endSpan(CONST.TELEMETRY.SPAN_NAVIGATION.PUSHER_INIT); }); diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index b6ee42a7488cd..2c00f7fc007be 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -81,12 +81,6 @@ Onyx.connect({ callback: (value) => (allPolicies = value), }); -let reportAttributes: ReportAttributesDerivedValue | undefined; -Onyx.connect({ - key: ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, - callback: (value) => (reportAttributes = value), -}); - type DomainOnyxUpdate = | OnyxUpdate<`${typeof ONYXKEYS.COLLECTION.DOMAIN}${string}`> | OnyxUpdate<`${typeof ONYXKEYS.COLLECTION.DOMAIN_PENDING_ACTIONS}${string}`> @@ -627,7 +621,11 @@ function isBlockedFromConcierge(blockedFromConciergeNVP: OnyxEntry(onyxUpdates: Array>, currentUserAccountIDParam: number) { +function triggerNotifications( + onyxUpdates: Array>, + currentUserAccountIDParam: number, + reportAttributes?: ReportAttributesDerivedValue['reports'], +) { for (const update of onyxUpdates) { if (!update.shouldNotify && !update.shouldShowPushNotification) { continue; @@ -639,7 +637,7 @@ function triggerNotifications(onyxUpdates: Array ReportAttributesDerivedValue['reports'] | undefined) { // If we don't have the user's accountID yet (because the app isn't fully setup yet) we can't subscribe so return early if (!currentUserAccountIDParam) { return; @@ -921,7 +919,7 @@ function subscribeToUserEvents(currentUserAccountIDParam: number) { } const onyxUpdatePromise = Onyx.update(pushJSON).then(() => { - triggerNotifications(pushJSON, currentUserAccountIDParam); + triggerNotifications(pushJSON, currentUserAccountIDParam, getReportAttributes()); }); // Return a promise when Onyx is done updating so that the OnyxUpdatesManager can properly apply all From c65fd6446502ae9392569c39ed4fed125295da8f Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:13:21 +0400 Subject: [PATCH 07/17] fix: handle optional getReportAttributes in user event subscription --- src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx | 2 +- src/libs/actions/User.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx index 16dec97d7de8d..496a6dbc4c933 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx @@ -31,7 +31,7 @@ function initializePusher(currentUserAccountID?: number, getReportAttributes?: ( cluster: CONFIG.PUSHER.CLUSTER, authEndpoint: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/AuthenticatePusher?`, }).then(() => { - User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID, getReportAttributes ?? (() => undefined)); + User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID, getReportAttributes); }); } diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 2c00f7fc007be..422d6ea00eee6 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -864,7 +864,7 @@ function initializePusherPingPong() { * Handles the newest events from Pusher where a single mega multipleEvents contains * an array of singular events all in one event */ -function subscribeToUserEvents(currentUserAccountIDParam: number, getReportAttributes: () => ReportAttributesDerivedValue['reports'] | undefined) { +function subscribeToUserEvents(currentUserAccountIDParam: number, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) { // If we don't have the user's accountID yet (because the app isn't fully setup yet) we can't subscribe so return early if (!currentUserAccountIDParam) { return; @@ -919,7 +919,7 @@ function subscribeToUserEvents(currentUserAccountIDParam: number, getReportAttri } const onyxUpdatePromise = Onyx.update(pushJSON).then(() => { - triggerNotifications(pushJSON, currentUserAccountIDParam, getReportAttributes()); + triggerNotifications(pushJSON, currentUserAccountIDParam, getReportAttributes?.()); }); // Return a promise when Onyx is done updating so that the OnyxUpdatesManager can properly apply all From 3c08df4806f27dae98e4053bdb42866918d21277 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:39:05 +0400 Subject: [PATCH 08/17] Refactor showCommentNotification signature to remove unnecessary parameter --- src/libs/Notification/LocalNotification/types.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libs/Notification/LocalNotification/types.ts b/src/libs/Notification/LocalNotification/types.ts index f4b59c96c72de..f1e068e8e39db 100644 --- a/src/libs/Notification/LocalNotification/types.ts +++ b/src/libs/Notification/LocalNotification/types.ts @@ -9,13 +9,7 @@ type LocalNotificationData = { }; type LocalNotificationModule = { - showCommentNotification: ( - report: Report, - reportAction: ReportAction, - onClick: LocalNotificationClickHandler, - conciergeReportID: string | undefined, - reportAttributes?: ReportAttributesDerivedValue['reports'], - ) => void; + showCommentNotification: (report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler, reportAttributes?: ReportAttributesDerivedValue['reports']) => void; showUpdateAvailableNotification: () => void; showModifiedExpenseNotification: (params: LocalNotificationModifiedExpenseParams) => void; clearReportNotifications: ClearReportNotifications; From 54b08ff34c5c2d4b61ec09c8ace8d60a07096848 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Sat, 14 Mar 2026 09:52:38 +0400 Subject: [PATCH 09/17] Remove unnecessary parameter from showCommentNotification call --- src/libs/actions/Report/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index 6f590972d746d..582926dd25486 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -4011,7 +4011,7 @@ function showReportActionNotification( const movedToReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(reportAction, CONST.REPORT.MOVE_TYPE.TO)}`]; LocalNotification.showModifiedExpenseNotification({report, reportAction, onClick, movedFromReport, movedToReport, currentUserLogin, reportAttributes}); } else { - LocalNotification.showCommentNotification(report, reportAction, onClick, conciergeReportID, reportAttributes); + LocalNotification.showCommentNotification(report, reportAction, onClick, reportAttributes); } notifyNewAction(reportID, undefined, reportAction.actorAccountID === currentUserAccountID); From 9670e1cfcffc750d8e6abac3f5285509654fdb29 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Sat, 14 Mar 2026 10:30:38 +0400 Subject: [PATCH 10/17] Refactor: Remove conciergeReportID parameter from multiple functions and update related calls --- .../AppNavigator/AuthScreensInitHandler.tsx | 15 ++++-------- src/libs/actions/Report/index.ts | 1 - src/libs/actions/User.ts | 7 +++--- tests/actions/ReportTest.ts | 24 ++++++++----------- tests/ui/AuthScreensInitHandlerTest.tsx | 8 +++---- 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx index b418d92285015..18c7ff268bb9d 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreensInitHandler.tsx @@ -24,15 +24,14 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {ReportAttributesDerivedValue} from '@src/types/onyx'; -import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -function initializePusher(conciergeReportID: string | undefined, currentUserAccountID?: number, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) { +function initializePusher(currentUserAccountID?: number, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) { return Pusher.init({ appKey: CONFIG.PUSHER.APP_KEY, cluster: CONFIG.PUSHER.CLUSTER, authEndpoint: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/AuthenticatePusher?`, }).then(() => { - User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID, conciergeReportID, getReportAttributes); + User.subscribeToUserEvents(currentUserAccountID ?? CONST.DEFAULT_NUMBER_ID, getReportAttributes); }); } @@ -60,7 +59,6 @@ function AuthScreensInitHandler() { const [lastUpdateIDAppliedToClient] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP); - const [conciergeReportID, conciergeReportIDMetadata] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const lastUpdateIDAppliedToClientRef = useRef(lastUpdateIDAppliedToClient); const isLoadingAppRef = useRef(isLoadingApp); @@ -85,12 +83,9 @@ function AuthScreensInitHandler() { if (!Navigation.isActiveRoute(ROUTES.SIGN_IN_MODAL)) { return; } - if (isLoadingOnyxValue(conciergeReportIDMetadata)) { - return; - } // This means sign in in RHP was successful, so we can subscribe to user events - initializePusher(conciergeReportID, session?.accountID, () => reportAttributesRef.current); - }, [session?.accountID, conciergeReportID, conciergeReportIDMetadata]); + initializePusher(session?.accountID, () => reportAttributesRef.current); + }, [session?.accountID]); useEffect(() => { const isLoggingInAsNewUser = !!session?.email && SessionUtils.isLoggingInAsNewUser(currentUrl, session.email); @@ -113,7 +108,7 @@ function AuthScreensInitHandler() { }); PusherConnectionManager.init(); - initializePusher(conciergeReportID, session?.accountID, () => reportAttributesRef.current).finally(() => { + initializePusher(session?.accountID, () => reportAttributesRef.current).finally(() => { endSpan(CONST.TELEMETRY.SPAN_NAVIGATION.PUSHER_INIT); }); diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index f96357d9946b4..fef3ffc48b47a 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -3997,7 +3997,6 @@ function showReportActionNotification( reportAction: ReportAction, currentUserAccountID: number, currentUserLogin: string, - conciergeReportID: string | undefined, reportAttributes?: ReportAttributesDerivedValue['reports'], ) { if (!shouldShowReportActionNotification(reportID, currentUserAccountID, reportAction)) { diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index 20c1c2f67ebcc..e208170140178 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -617,7 +617,6 @@ function isBlockedFromConcierge(blockedFromConciergeNVP: OnyxEntry( onyxUpdates: Array>, currentUserAccountIDParam: number, - conciergeReportID: string | undefined, reportAttributes?: ReportAttributesDerivedValue['reports'], ) { for (const update of onyxUpdates) { @@ -631,7 +630,7 @@ function triggerNotifications( for (const action of reportActions) { if (action) { // They aren't connected to a UI anywhere, it's OK to use currentEmail - showReportActionNotification(reportID, action, currentUserAccountIDParam, currentEmail, conciergeReportID, reportAttributes); + showReportActionNotification(reportID, action, currentUserAccountIDParam, currentEmail, reportAttributes); } } } @@ -858,7 +857,7 @@ function initializePusherPingPong() { * Handles the newest events from Pusher where a single mega multipleEvents contains * an array of singular events all in one event */ -function subscribeToUserEvents(currentUserAccountIDParam: number, conciergeReportID: string | undefined, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) { +function subscribeToUserEvents(currentUserAccountIDParam: number, getReportAttributes?: () => ReportAttributesDerivedValue['reports'] | undefined) { // If we don't have the user's accountID yet (because the app isn't fully setup yet) we can't subscribe so return early if (!currentUserAccountIDParam) { return; @@ -913,7 +912,7 @@ function subscribeToUserEvents(currentUserAccountIDParam: number, conciergeRepor } const onyxUpdatePromise = Onyx.update(pushJSON).then(() => { - triggerNotifications(pushJSON, currentUserAccountIDParam, conciergeReportID, getReportAttributes?.()); + triggerNotifications(pushJSON, currentUserAccountIDParam, getReportAttributes?.()); }); // Return a promise when Onyx is done updating so that the OnyxUpdatesManager can properly apply all diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 0c9e44f991c72..b3f89fefb191a 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -847,7 +847,6 @@ describe('actions/Report', () => { const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'test@user.com'; const REPORT_ID = '1'; - const CONCIERGE_REPORT_ID = '42'; const REPORT_ACTION = { actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, }; @@ -855,7 +854,7 @@ describe('actions/Report', () => { return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) .then(waitForBatchedUpdates) .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID, CONCIERGE_REPORT_ID); + User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { @@ -872,7 +871,7 @@ describe('actions/Report', () => { return SequentialQueue.getCurrentRequest().then(waitForBatchedUpdates); }) .then(() => { - expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, CONCIERGE_REPORT_ID); + expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); }); }); @@ -880,7 +879,6 @@ describe('actions/Report', () => { const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'test@user.com'; const REPORT_ID = '1'; - const CONCIERGE_REPORT_ID = '99'; const REPORT_ACTION = { actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, }; @@ -888,7 +886,7 @@ describe('actions/Report', () => { return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) .then(waitForBatchedUpdates) .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID, CONCIERGE_REPORT_ID); + User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { @@ -905,7 +903,7 @@ describe('actions/Report', () => { return SequentialQueue.getCurrentRequest().then(waitForBatchedUpdates); }) .then(() => { - expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, CONCIERGE_REPORT_ID); + expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); }); }); @@ -914,7 +912,6 @@ describe('actions/Report', () => { const TEST_USER_LOGIN = 'test@user.com'; const REPORT_ID_1 = '1'; const REPORT_ID_2 = '2'; - const CONCIERGE_REPORT_ID = '55'; const REPORT_ACTION_1 = { actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, }; @@ -925,7 +922,7 @@ describe('actions/Report', () => { return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) .then(waitForBatchedUpdates) .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID, CONCIERGE_REPORT_ID); + User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { @@ -951,8 +948,8 @@ describe('actions/Report', () => { }) .then(() => { expect(Report.showReportActionNotification).toHaveBeenCalledTimes(2); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID_1, REPORT_ACTION_1, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, CONCIERGE_REPORT_ID); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID_2, REPORT_ACTION_2, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, CONCIERGE_REPORT_ID); + expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID_1, REPORT_ACTION_1, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); + expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID_2, REPORT_ACTION_2, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); }); }); @@ -960,7 +957,6 @@ describe('actions/Report', () => { const TEST_USER_ACCOUNT_ID = 1; const TEST_USER_LOGIN = 'test@user.com'; const REPORT_ID = '1'; - const CONCIERGE_REPORT_ID = '77'; const REPORT_ACTION_1 = { actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, }; @@ -971,7 +967,7 @@ describe('actions/Report', () => { return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) .then(waitForBatchedUpdates) .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID, CONCIERGE_REPORT_ID); + User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); return waitForBatchedUpdates(); }) .then(() => { @@ -990,8 +986,8 @@ describe('actions/Report', () => { }) .then(() => { expect(Report.showReportActionNotification).toHaveBeenCalledTimes(2); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID, REPORT_ACTION_1, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, CONCIERGE_REPORT_ID); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID, REPORT_ACTION_2, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, CONCIERGE_REPORT_ID); + expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID, REPORT_ACTION_1, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); + expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID, REPORT_ACTION_2, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); }); }); diff --git a/tests/ui/AuthScreensInitHandlerTest.tsx b/tests/ui/AuthScreensInitHandlerTest.tsx index dd1797b795e3c..85fcebede13cc 100644 --- a/tests/ui/AuthScreensInitHandlerTest.tsx +++ b/tests/ui/AuthScreensInitHandlerTest.tsx @@ -161,7 +161,7 @@ describe('AuthScreensInitHandler', () => { await waitForBatchedUpdatesWithAct(); expect(mockedPusherInit).toHaveBeenCalled(); - expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, conciergeReportID); + expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); }); it('calls initializePusher when SIGN_IN_MODAL is active and conciergeReportID is loaded', async () => { @@ -177,7 +177,7 @@ describe('AuthScreensInitHandler', () => { await waitForBatchedUpdatesWithAct(); // subscribeToUserEvents should be called with conciergeReportID from both mount and sign-in modal effects - expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, conciergeReportID); + expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); }); it('does not call initializePusher from sign-in modal effect when conciergeReportID is still loading', async () => { @@ -190,7 +190,7 @@ describe('AuthScreensInitHandler', () => { await waitForBatchedUpdatesWithAct(); // The mount effect calls subscribeToUserEvents with undefined conciergeReportID - expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, undefined); + expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); }); it('passes undefined conciergeReportID when not set', async () => { @@ -200,7 +200,7 @@ describe('AuthScreensInitHandler', () => { renderAuthScreensInitHandler(); await waitForBatchedUpdatesWithAct(); - expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, undefined); + expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); }); it('signs out when logging in as new user during transition', async () => { From 5c616b8b61dd7690e3391c544a08b6f32cc5b412 Mon Sep 17 00:00:00 2001 From: Mohammad Jafarinejad <71210799+mohammadjafarinejad@users.noreply.github.com> Date: Fri, 20 Mar 2026 00:48:09 +0400 Subject: [PATCH 11/17] fix: Remove 'concierge-report-id' parameter from buildSubstitutionsMap in tests --- tests/unit/Search/buildSubstitutionsMapTest.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/Search/buildSubstitutionsMapTest.ts b/tests/unit/Search/buildSubstitutionsMapTest.ts index bf818eca9de90..f3241fd17484f 100644 --- a/tests/unit/Search/buildSubstitutionsMapTest.ts +++ b/tests/unit/Search/buildSubstitutionsMapTest.ts @@ -80,14 +80,14 @@ describe('buildSubstitutionsMap should return correct substitutions map', () => test('when there were no substitutions', () => { const userQuery = 'foo bar'; - const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, {}, cardFeedsMock, {}, 12345, translateLocal, 'concierge-report-id'); + const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, {}, cardFeedsMock, {}, 12345, translateLocal, {}); expect(result).toStrictEqual({}); }); test('when query has a single substitution', () => { const userQuery = 'foo from:12345'; - const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, {}, cardFeedsMock, {}, 11111, translateLocal, 'concierge-report-id'); + const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, {}, cardFeedsMock, {}, 11111, translateLocal, {}); expect(result).toStrictEqual({ 'from:John Doe': '12345', @@ -97,7 +97,7 @@ describe('buildSubstitutionsMap should return correct substitutions map', () => test('when query has multiple substitutions of different types', () => { const userQuery = 'from:78901,12345 to:nonExistingGuy@mail.com cardID:11223344 in:rep123 taxRate:id_TAX_1 groupBy:cards feed:"1234_oauth.americanexpressfdx.com 1001"'; - const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, cardListMock, cardFeedsMock, {}, 11111, translateLocal, 'concierge-report-id'); + const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, cardListMock, cardFeedsMock, {}, 11111, translateLocal, {}); expect(result).toStrictEqual({ 'from:Jane Doe': '78901', @@ -112,7 +112,7 @@ describe('buildSubstitutionsMap should return correct substitutions map', () => test('when query has a substitution for the current user', () => { const userQuery = 'from:12345'; - const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, cardListMock, cardFeedsMock, {}, 12345, translateLocal, 'concierge-report-id'); + const result = buildSubstitutionsMap(userQuery, personalDetailsMock, reportsMock, taxRatesMock, cardListMock, cardFeedsMock, {}, 12345, translateLocal, {}); expect(result).toStrictEqual({ 'from:me': '12345', From 71121b9ec6cb6c71867e364320b6185627f380a1 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Fri, 20 Mar 2026 18:15:38 +0100 Subject: [PATCH 12/17] fixes after merge from main --- .../LocalNotification/BrowserNotifications.ts | 2 -- src/libs/Notification/LocalNotification/index.ts | 4 ++-- src/libs/Notification/LocalNotification/types.ts | 1 - src/libs/actions/Report/index.ts | 2 +- tests/unit/showReportActionNotificationTest.ts | 14 +++++++------- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/libs/Notification/LocalNotification/BrowserNotifications.ts b/src/libs/Notification/LocalNotification/BrowserNotifications.ts index d072d560b015c..35d6780a522b7 100644 --- a/src/libs/Notification/LocalNotification/BrowserNotifications.ts +++ b/src/libs/Notification/LocalNotification/BrowserNotifications.ts @@ -147,7 +147,6 @@ export default { policyTags, policy, currentUserLogin, - conciergeReportID, reportAttributes, }: LocalNotificationModifiedExpensePushParams) { const title = reportAction.person?.map((f) => f.text).join(', ') ?? ''; @@ -160,7 +159,6 @@ export default { movedToReport, policyTags, currentUserLogin, - conciergeReportID, reportAttributes, }); // Strip HTML tags for plain text notification body diff --git a/src/libs/Notification/LocalNotification/index.ts b/src/libs/Notification/LocalNotification/index.ts index ad177c780bc18..ad915ebecce6c 100644 --- a/src/libs/Notification/LocalNotification/index.ts +++ b/src/libs/Notification/LocalNotification/index.ts @@ -35,7 +35,7 @@ function showUpdateAvailableNotification() { BrowserNotifications.pushUpdateAvailableNotification(); } -function showModifiedExpenseNotification({report, reportAction, movedFromReport, movedToReport, onClick, currentUserLogin, conciergeReportID}: LocalNotificationModifiedExpenseParams) { +function showModifiedExpenseNotification({report, reportAction, movedFromReport, movedToReport, onClick, currentUserLogin, reportAttributes}: LocalNotificationModifiedExpenseParams) { const policyID = report.policyID; const policyTags = policyID ? allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] : undefined; const policy = policyID ? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`] : undefined; @@ -49,7 +49,7 @@ function showModifiedExpenseNotification({report, reportAction, movedFromReport, policyTags, policy, currentUserLogin, - conciergeReportID, + reportAttributes, }); } diff --git a/src/libs/Notification/LocalNotification/types.ts b/src/libs/Notification/LocalNotification/types.ts index 1666d828c1604..321550b06a88e 100644 --- a/src/libs/Notification/LocalNotification/types.ts +++ b/src/libs/Notification/LocalNotification/types.ts @@ -22,7 +22,6 @@ type LocalNotificationModifiedExpenseParams = { movedFromReport?: OnyxEntry; movedToReport?: OnyxEntry; currentUserLogin: string; - conciergeReportID: string | undefined; reportAttributes?: ReportAttributesDerivedValue['reports']; }; diff --git a/src/libs/actions/Report/index.ts b/src/libs/actions/Report/index.ts index b66bdf4f01c1b..9e9bfe0c08ea6 100644 --- a/src/libs/actions/Report/index.ts +++ b/src/libs/actions/Report/index.ts @@ -4119,7 +4119,7 @@ function showReportActionNotification( if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.MODIFIED_EXPENSE) { const movedFromReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(reportAction, CONST.REPORT.MOVE_TYPE.FROM)}`]; const movedToReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${getMovedReportID(reportAction, CONST.REPORT.MOVE_TYPE.TO)}`]; - LocalNotification.showModifiedExpenseNotification({report, reportAction, onClick, movedFromReport, movedToReport, currentUserLogin, conciergeReportID, reportAttributes}); + LocalNotification.showModifiedExpenseNotification({report, reportAction, onClick, movedFromReport, movedToReport, currentUserLogin, reportAttributes}); } else { LocalNotification.showCommentNotification(report, reportAction, onClick, reportAttributes); } diff --git a/tests/unit/showReportActionNotificationTest.ts b/tests/unit/showReportActionNotificationTest.ts index aed1f1de98e21..a7b88a480c953 100644 --- a/tests/unit/showReportActionNotificationTest.ts +++ b/tests/unit/showReportActionNotificationTest.ts @@ -46,7 +46,7 @@ const CURRENT_USER_ACCOUNT_ID = 1; const CURRENT_USER_LOGIN = 'test@user.com'; const REPORT_ID = '100'; const OTHER_USER_ACCOUNT_ID = 2; -const CONCIERGE_REPORT_ID = '42'; +const REPORT_ATTRIBUTES = {someReportKey: {reportName: 'Test Report'}} as Record; describe('showReportActionNotification', () => { beforeAll(() => { @@ -76,7 +76,7 @@ describe('showReportActionNotification', () => { await waitForBatchedUpdates(); } - it('passes conciergeReportID to showModifiedExpenseNotification for MODIFIED_EXPENSE actions', async () => { + it('passes reportAttributes to showModifiedExpenseNotification for MODIFIED_EXPENSE actions', async () => { await setupReport(); const reportAction = { @@ -93,17 +93,17 @@ describe('showReportActionNotification', () => { reportAction as Parameters[1], CURRENT_USER_ACCOUNT_ID, CURRENT_USER_LOGIN, - CONCIERGE_REPORT_ID, + REPORT_ATTRIBUTES as Parameters[4], ); await waitForBatchedUpdates(); expect(mockShowModifiedExpenseNotification).toHaveBeenCalledTimes(1); const callArgs = mockShowModifiedExpenseNotification.mock.calls.at(0)?.at(0) as Record; - expect(callArgs.conciergeReportID).toBe(CONCIERGE_REPORT_ID); + expect(callArgs.reportAttributes).toBe(REPORT_ATTRIBUTES); expect(mockShowCommentNotification).not.toHaveBeenCalled(); }); - it('passes undefined conciergeReportID to showModifiedExpenseNotification when not provided', async () => { + it('passes undefined reportAttributes to showModifiedExpenseNotification when not provided', async () => { await setupReport(); const reportAction = { @@ -120,7 +120,7 @@ describe('showReportActionNotification', () => { expect(mockShowModifiedExpenseNotification).toHaveBeenCalledTimes(1); const callArgs = mockShowModifiedExpenseNotification.mock.calls.at(0)?.at(0) as Record; - expect(callArgs.conciergeReportID).toBeUndefined(); + expect(callArgs.reportAttributes).toBeUndefined(); expect(mockShowCommentNotification).not.toHaveBeenCalled(); }); @@ -141,7 +141,7 @@ describe('showReportActionNotification', () => { reportAction as Parameters[1], CURRENT_USER_ACCOUNT_ID, CURRENT_USER_LOGIN, - CONCIERGE_REPORT_ID, + REPORT_ATTRIBUTES as Parameters[4], ); await waitForBatchedUpdates(); From 5f92362835e107754c0d982be853da5f60eff0a2 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Fri, 20 Mar 2026 18:28:15 +0100 Subject: [PATCH 13/17] tests fixes after merge from main --- src/libs/ModifiedExpenseMessage.ts | 4 +- tests/actions/ReportTest.ts | 148 ------------------------ tests/ui/AuthScreensInitHandlerTest.tsx | 44 +------ 3 files changed, 2 insertions(+), 194 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index 2a3427181968a..fe6f4891b7c2e 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -165,8 +165,6 @@ function getMovedFromOrToReportMessage( movedFromReport: OnyxEntry | undefined, movedToReport: OnyxEntry | undefined, currentUserLogin: string, - // TODO: This will be required eventually. Refactor issue: https://github.com/Expensify/App/issues/66411 - conciergeReportID?: string, reportAttributes?: ReportAttributesDerivedValue['reports'], ): string | undefined { if (movedToReport) { @@ -274,7 +272,7 @@ function getForReportAction({ return ''; } - const movedFromOrToReportMessage = getMovedFromOrToReportMessage(translate, movedFromReport, movedToReport, currentUserLogin, conciergeReportID, reportAttributes); + const movedFromOrToReportMessage = getMovedFromOrToReportMessage(translate, movedFromReport, movedToReport, currentUserLogin, reportAttributes); if (movedFromOrToReportMessage) { return movedFromOrToReportMessage; } diff --git a/tests/actions/ReportTest.ts b/tests/actions/ReportTest.ts index 50c39ab4bfd8f..2793dae83b75e 100644 --- a/tests/actions/ReportTest.ts +++ b/tests/actions/ReportTest.ts @@ -873,154 +873,6 @@ describe('actions/Report', () => { }); }); - it('should pass conciergeReportID through to showReportActionNotification when provided', () => { - const TEST_USER_ACCOUNT_ID = 1; - const TEST_USER_LOGIN = 'test@user.com'; - const REPORT_ID = '1'; - const REPORT_ACTION = { - actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, - }; - - return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) - .then(waitForBatchedUpdates) - .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); - return waitForBatchedUpdates(); - }) - .then(() => { - PusherHelper.emitOnyxUpdate([ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, - value: { - 1: REPORT_ACTION, - }, - shouldNotify: true, - }, - ]); - return SequentialQueue.getCurrentRequest().then(waitForBatchedUpdates); - }) - .then(() => { - expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); - }); - }); - - it('should pass conciergeReportID through when using shouldShowPushNotification flag', () => { - const TEST_USER_ACCOUNT_ID = 1; - const TEST_USER_LOGIN = 'test@user.com'; - const REPORT_ID = '1'; - const REPORT_ACTION = { - actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, - }; - - return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) - .then(waitForBatchedUpdates) - .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); - return waitForBatchedUpdates(); - }) - .then(() => { - PusherHelper.emitOnyxUpdate([ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, - value: { - 1: REPORT_ACTION, - }, - shouldShowPushNotification: true, - }, - ]); - return SequentialQueue.getCurrentRequest().then(waitForBatchedUpdates); - }) - .then(() => { - expect(Report.showReportActionNotification).toBeCalledWith(REPORT_ID, REPORT_ACTION, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); - }); - }); - - it('should pass conciergeReportID for multiple report actions across different reports', () => { - const TEST_USER_ACCOUNT_ID = 1; - const TEST_USER_LOGIN = 'test@user.com'; - const REPORT_ID_1 = '1'; - const REPORT_ID_2 = '2'; - const REPORT_ACTION_1 = { - actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, - }; - const REPORT_ACTION_2 = { - actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, - }; - - return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) - .then(waitForBatchedUpdates) - .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); - return waitForBatchedUpdates(); - }) - .then(() => { - PusherHelper.emitOnyxUpdate([ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID_1}`, - value: { - 1: REPORT_ACTION_1, - }, - shouldNotify: true, - }, - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID_2}`, - value: { - 2: REPORT_ACTION_2, - }, - shouldNotify: true, - }, - ]); - return SequentialQueue.getCurrentRequest().then(waitForBatchedUpdates); - }) - .then(() => { - expect(Report.showReportActionNotification).toHaveBeenCalledTimes(2); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID_1, REPORT_ACTION_1, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID_2, REPORT_ACTION_2, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); - }); - }); - - it('should pass conciergeReportID for multiple actions within a single report update', () => { - const TEST_USER_ACCOUNT_ID = 1; - const TEST_USER_LOGIN = 'test@user.com'; - const REPORT_ID = '1'; - const REPORT_ACTION_1 = { - actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, - }; - const REPORT_ACTION_2 = { - actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT, - }; - - return TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID) - .then(waitForBatchedUpdates) - .then(() => { - User.subscribeToUserEvents(TEST_USER_ACCOUNT_ID); - return waitForBatchedUpdates(); - }) - .then(() => { - PusherHelper.emitOnyxUpdate([ - { - onyxMethod: Onyx.METHOD.MERGE, - key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${REPORT_ID}`, - value: { - 1: REPORT_ACTION_1, - 2: REPORT_ACTION_2, - }, - shouldNotify: true, - }, - ]); - return SequentialQueue.getCurrentRequest().then(waitForBatchedUpdates); - }) - .then(() => { - expect(Report.showReportActionNotification).toHaveBeenCalledTimes(2); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID, REPORT_ACTION_1, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); - expect(Report.showReportActionNotification).toHaveBeenCalledWith(REPORT_ID, REPORT_ACTION_2, TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN, undefined); - }); - }); - it('should properly toggle reactions on a message', () => { global.fetch = TestHelper.getGlobalFetchMock(); diff --git a/tests/ui/AuthScreensInitHandlerTest.tsx b/tests/ui/AuthScreensInitHandlerTest.tsx index 85fcebede13cc..fc36dd159a879 100644 --- a/tests/ui/AuthScreensInitHandlerTest.tsx +++ b/tests/ui/AuthScreensInitHandlerTest.tsx @@ -150,11 +150,8 @@ describe('AuthScreensInitHandler', () => { await waitForBatchedUpdates(); }); - it('passes conciergeReportID to subscribeToUserEvents on mount', async () => { - const conciergeReportID = '12345'; - + it('calls subscribeToUserEvents with a getter function on mount', async () => { await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); - await Onyx.merge(ONYXKEYS.CONCIERGE_REPORT_ID, conciergeReportID); await waitForBatchedUpdates(); renderAuthScreensInitHandler(); @@ -164,45 +161,6 @@ describe('AuthScreensInitHandler', () => { expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); }); - it('calls initializePusher when SIGN_IN_MODAL is active and conciergeReportID is loaded', async () => { - mockedIsActiveRoute.mockReturnValue(true); - - const conciergeReportID = '67890'; - - await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); - await Onyx.merge(ONYXKEYS.CONCIERGE_REPORT_ID, conciergeReportID); - await waitForBatchedUpdates(); - - renderAuthScreensInitHandler(); - await waitForBatchedUpdatesWithAct(); - - // subscribeToUserEvents should be called with conciergeReportID from both mount and sign-in modal effects - expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); - }); - - it('does not call initializePusher from sign-in modal effect when conciergeReportID is still loading', async () => { - mockedIsActiveRoute.mockReturnValue(true); - - await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); - await waitForBatchedUpdates(); - - renderAuthScreensInitHandler(); - await waitForBatchedUpdatesWithAct(); - - // The mount effect calls subscribeToUserEvents with undefined conciergeReportID - expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); - }); - - it('passes undefined conciergeReportID when not set', async () => { - await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); - await waitForBatchedUpdates(); - - renderAuthScreensInitHandler(); - await waitForBatchedUpdatesWithAct(); - - expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); - }); - it('signs out when logging in as new user during transition', async () => { mockedGetCurrentUrl.mockReturnValue(`https://new.expensify.com/${ROUTES.TRANSITION_BETWEEN_APPS}`); mockedIsLoggingInAsNewUser.mockReturnValue(true); From ab309a5eceeeb600c19227b6592b2acd65e8ba9b Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Fri, 20 Mar 2026 18:37:10 +0100 Subject: [PATCH 14/17] lint fixes --- src/libs/ModifiedExpenseMessage.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libs/ModifiedExpenseMessage.ts b/src/libs/ModifiedExpenseMessage.ts index fe6f4891b7c2e..63a7c061aa7db 100644 --- a/src/libs/ModifiedExpenseMessage.ts +++ b/src/libs/ModifiedExpenseMessage.ts @@ -251,7 +251,6 @@ function getForReportAction({ movedToReport, policyTags, currentUserLogin, - conciergeReportID, reportAttributes, }: { translate: LocalizedTranslate; @@ -264,8 +263,6 @@ function getForReportAction({ // See https://github.com/Expensify/App/pull/75562 policyTags?: OnyxEntry; currentUserLogin: string; - // TODO: This will be required eventually. Refactor issue: https://github.com/Expensify/App/issues/66411 - conciergeReportID?: string; reportAttributes?: ReportAttributesDerivedValue['reports']; }): string { if (!isModifiedExpenseAction(reportAction)) { From c9664ac3e629c8bff610b4f32d197e9285f61c5f Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Fri, 20 Mar 2026 18:43:01 +0100 Subject: [PATCH 15/17] lint fixes --- .../Search/SearchPageHeader/SearchPageHeaderInput.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx index 0c829bb18b8d3..09fad2c2c76c8 100644 --- a/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx +++ b/src/components/Search/SearchPageHeader/SearchPageHeaderInput.tsx @@ -72,7 +72,6 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo const feedKeysWithCards = useFeedKeysWithAssignedCards(); const {inputQuery: originalInputQuery} = queryJSON; const [currentUserAccountID = -1] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector}); - const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); const [betas] = useOnyx(ONYXKEYS.BETAS); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); @@ -149,7 +148,7 @@ function SearchPageHeaderInput({queryJSON, searchRouterListVisible, hideSearchRo reportAttributes, ); setAutocompleteSubstitutions(substitutionsMap); - }, [allFeeds, personalAndWorkspaceCards, originalInputQuery, personalDetails, reports, taxRates, policies, currentUserAccountID, translate, conciergeReportID, reportAttributes]); + }, [allFeeds, personalAndWorkspaceCards, originalInputQuery, personalDetails, reports, taxRates, policies, currentUserAccountID, translate, reportAttributes]); useEffect(() => { if (searchRouterListVisible) { From 4dc7758cc05b4fa942804756abe6cbd703400302 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Fri, 20 Mar 2026 19:07:02 +0100 Subject: [PATCH 16/17] lint fixes --- tests/ui/AuthScreensInitHandlerTest.tsx | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/ui/AuthScreensInitHandlerTest.tsx b/tests/ui/AuthScreensInitHandlerTest.tsx index fc36dd159a879..3a9145ae63ad8 100644 --- a/tests/ui/AuthScreensInitHandlerTest.tsx +++ b/tests/ui/AuthScreensInitHandlerTest.tsx @@ -161,6 +161,46 @@ describe('AuthScreensInitHandler', () => { expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); }); + it('calls subscribeToUserEvents from sign-in modal effect when SIGN_IN_MODAL is active', async () => { + mockedIsActiveRoute.mockReturnValue(true); + + await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); + await waitForBatchedUpdates(); + + renderAuthScreensInitHandler(); + await waitForBatchedUpdatesWithAct(); + + // Both mount effect AND sign-in modal effect fire → 2 calls + expect(subscribeToUserEvents).toHaveBeenCalledTimes(2); + expect(subscribeToUserEvents).toHaveBeenCalledWith(TEST_ACCOUNT_ID, expect.any(Function)); + }); + + it('getter passed to subscribeToUserEvents returns report attributes when available', async () => { + const mockReports = {'1': {reportName: 'Test Report'}} as Record; + + await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); + await Onyx.merge(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {reports: mockReports}); + await waitForBatchedUpdates(); + + renderAuthScreensInitHandler(); + await waitForBatchedUpdatesWithAct(); + + const getter = (subscribeToUserEvents as jest.Mock).mock.calls.at(0)?.[1] as () => unknown; + expect(getter()).toEqual(mockReports); + }); + + it('getter passed to subscribeToUserEvents returns undefined when report attributes not yet loaded', async () => { + await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); + // Intentionally do not set ONYXKEYS.DERIVED.REPORT_ATTRIBUTES + await waitForBatchedUpdates(); + + renderAuthScreensInitHandler(); + await waitForBatchedUpdatesWithAct(); + + const getter = (subscribeToUserEvents as jest.Mock).mock.calls.at(0)?.[1] as () => unknown; + expect(getter()).toBeUndefined(); + }); + it('signs out when logging in as new user during transition', async () => { mockedGetCurrentUrl.mockReturnValue(`https://new.expensify.com/${ROUTES.TRANSITION_BETWEEN_APPS}`); mockedIsLoggingInAsNewUser.mockReturnValue(true); From 81457a0bd6838658cf0e82a04d4d0ad746247203 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Fri, 20 Mar 2026 19:27:57 +0100 Subject: [PATCH 17/17] lint and typecheck fixes --- tests/ui/AuthScreensInitHandlerTest.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/ui/AuthScreensInitHandlerTest.tsx b/tests/ui/AuthScreensInitHandlerTest.tsx index 3a9145ae63ad8..9d91971869274 100644 --- a/tests/ui/AuthScreensInitHandlerTest.tsx +++ b/tests/ui/AuthScreensInitHandlerTest.tsx @@ -14,6 +14,7 @@ import {signOutAndRedirectToSignIn} from '@userActions/Session'; import {subscribeToUserEvents} from '@userActions/User'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {ReportAttributesDerivedValue} from '@src/types/onyx'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; @@ -176,7 +177,7 @@ describe('AuthScreensInitHandler', () => { }); it('getter passed to subscribeToUserEvents returns report attributes when available', async () => { - const mockReports = {'1': {reportName: 'Test Report'}} as Record; + const mockReports = {testReport: {reportName: 'Test Report'}} as unknown as ReportAttributesDerivedValue['reports']; await Onyx.merge(ONYXKEYS.SESSION, {accountID: TEST_ACCOUNT_ID, email: 'test@test.com'}); await Onyx.merge(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {reports: mockReports}); @@ -185,7 +186,9 @@ describe('AuthScreensInitHandler', () => { renderAuthScreensInitHandler(); await waitForBatchedUpdatesWithAct(); - const getter = (subscribeToUserEvents as jest.Mock).mock.calls.at(0)?.[1] as () => unknown; + const mockCalls = (subscribeToUserEvents as jest.Mock).mock.calls; + const firstCallArgs = mockCalls.at(0) as unknown[]; + const getter = firstCallArgs.at(1) as () => unknown; expect(getter()).toEqual(mockReports); }); @@ -197,7 +200,9 @@ describe('AuthScreensInitHandler', () => { renderAuthScreensInitHandler(); await waitForBatchedUpdatesWithAct(); - const getter = (subscribeToUserEvents as jest.Mock).mock.calls.at(0)?.[1] as () => unknown; + const mockCalls = (subscribeToUserEvents as jest.Mock).mock.calls; + const firstCallArgs = mockCalls.at(0) as unknown[]; + const getter = firstCallArgs.at(1) as () => unknown; expect(getter()).toBeUndefined(); });