diff --git a/src/components/Search/SearchFiltersChatsSelector.tsx b/src/components/Search/SearchFiltersChatsSelector.tsx index 57b27b234ebe6..f10b57172334f 100644 --- a/src/components/Search/SearchFiltersChatsSelector.tsx +++ b/src/components/Search/SearchFiltersChatsSelector.tsx @@ -73,7 +73,8 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen createOptionFromReport({...reportData, reportID: id}, personalDetails, currentUserAccountID, chatReport, privateIsArchived, reportAttributesDerived), ); const isReportArchived = !!privateIsArchived; - const alternateText = getAlternateText(report, {}, isReportArchived, currentUserEmail, {}, undefined, undefined, reportAttributesDerived); + const policy = allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${reportData?.policyID}`]; + const alternateText = getAlternateText(report, {}, isReportArchived, currentUserEmail, policy, {}, undefined, undefined, reportAttributesDerived); return {...report, alternateText}; }); diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index a9fdd6833b75a..502a158cbc6c2 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -431,6 +431,8 @@ function getAlternateText( {showChatPreviewLine = false, forcePolicyNamePreview = false}: PreviewConfig, isReportArchived: boolean | undefined, currentUserLogin: string, + // We'll make it required in the next PR. Ref: https://github.com/Expensify/App/issues/66415 + policy?: OnyxEntry, lastActorDetails: Partial | null = {}, visibleReportActionsData: VisibleReportActionsDerivedValue = {}, translate?: LocalizedTranslate, @@ -451,6 +453,7 @@ function getAlternateText( translate: translateFn, report, lastActorDetails, + policy, isReportArchived, chatReport, visibleReportActionsDataParam: visibleReportActionsData, @@ -1058,6 +1061,8 @@ function createOption( {showChatPreviewLine, forcePolicyNamePreview}, !!result.private_isArchived, currentUserLogin, + // TODO: Remove this in the next PR that will refactor prepareReportOptionsForDisplay. Ref: https://github.com/Expensify/App/issues/66415 + undefined, lastActorDetails, visibleReportActionsData, translateFn, @@ -2257,6 +2262,8 @@ function prepareReportOptionsForDisplay( {showChatPreviewLine, forcePolicyNamePreview}, !!option.private_isArchived, currentUserLogin, + // TODO: Remove this in the next PR that will refactor prepareReportOptionsForDisplay. Ref: https://github.com/Expensify/App/issues/66415 + undefined, null, visibleReportActionsData, undefined, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 13e0309bd02d4..4982732f96c34 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -1066,14 +1066,12 @@ Onyx.connect({ }); let allPolicies: OnyxCollection; -let hasPolicies: boolean; let policiesArray: Policy[] = []; Onyx.connect({ key: ONYXKEYS.COLLECTION.POLICY, waitForCollectionCallback: true, callback: (value) => { allPolicies = value; - hasPolicies = !isEmptyObject(value); policiesArray = Object.values(value ?? {}).filter((policy): policy is Policy => !!policy); }, }); @@ -1412,7 +1410,7 @@ function getPolicyName({report, returnEmptyIfNotFound = false, policy, policies, const noPolicyFound = returnEmptyIfNotFound ? '' : unavailableTranslation; const parentReport = report ? getRootParentReport({report, reports}) : undefined; - if ((!report?.policyName && !parentReport?.policyName && !hasPolicies && isEmptyObject(policies)) || isEmptyObject(report)) { + if ((!report?.policyName && !parentReport?.policyName && isEmptyObject(policies) && isEmptyObject(allPolicies)) || isEmptyObject(report)) { return noPolicyFound; } const finalPolicy = (() => { diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 36f7b46e19c63..55bb1bf230246 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -3147,8 +3147,8 @@ function getSortedTransactionData( const aIsUnreported = a.report?.type !== CONST.REPORT.TYPE.EXPENSE && a.report?.type !== CONST.REPORT.TYPE.INVOICE; const bIsUnreported = b.report?.type !== CONST.REPORT.TYPE.EXPENSE && b.report?.type !== CONST.REPORT.TYPE.INVOICE; - const aValue = !aIsUnreported ? getPolicyName({report: a.report}) : ''; - const bValue = !bIsUnreported ? getPolicyName({report: b.report}) : ''; + const aValue = !aIsUnreported ? getPolicyName({report: a.report, policy: a.policy}) : ''; + const bValue = !bIsUnreported ? getPolicyName({report: b.report, policy: b.policy}) : ''; return compareValues(aValue, bValue, sortOrder, sortBy, localeCompare); }); } diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index ce372c917fc60..04b67d8c90d14 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -1230,7 +1230,7 @@ function getWelcomeMessage( } else { welcomeMessage.messageHtml = translate( 'reportActionsView.beginningOfChatHistoryPolicyExpenseChat', - getPolicyName({report}), + getPolicyName({report, policy}), getDisplayNameForParticipant({accountID: report?.ownerAccountID, formatPhoneNumber: formatPhoneNumberPhoneUtils}), ); welcomeMessage.messageText = Parser.htmlToText(welcomeMessage.messageHtml); diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 159be3a58a7fa..95b38894382d6 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -4324,6 +4324,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4349,6 +4350,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4375,6 +4377,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4401,6 +4404,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4424,6 +4428,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4446,6 +4451,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4467,6 +4473,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4488,6 +4495,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4509,6 +4517,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4530,6 +4539,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4551,6 +4561,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4576,6 +4587,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4603,6 +4615,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4621,6 +4634,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4651,6 +4665,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4690,6 +4705,7 @@ describe('OptionsListUtils', () => { report, translate: translateLocal, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4775,6 +4791,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4805,6 +4822,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, @@ -4812,6 +4830,91 @@ describe('OptionsListUtils', () => { expect(lastMessage).toBe(translate(CONST.LOCALES.EN, 'iou.error.genericCreateFailureMessage')); }); }); + + describe('archived report with policy', () => { + it('should use the passed policy name for POLICY_DELETED archive reason', async () => { + const testPolicyID = 'archivePolicyTest'; + const policy: Policy = { + id: testPolicyID, + name: 'Test Workspace', + type: CONST.POLICY.TYPE.TEAM, + } as Policy; + const report: Report = { + ...createRandomReport(0, undefined), + policyID: testPolicyID, + type: CONST.REPORT.TYPE.CHAT, + }; + const closedAction = { + ...createRandomReportAction(1), + actionName: CONST.REPORT.ACTIONS.TYPE.CLOSED, + originalMessage: { + policyName: policy.name, + reason: CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED, + }, + } as ReportAction; + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, { + [closedAction.reportActionID]: closedAction, + }); + + const lastMessage = getLastMessageTextForReport({ + translate: translateLocal, + report, + lastActorDetails: null, + policy, + isReportArchived: true, + chatReport: undefined, + currentUserLogin: '', + }); + + expect(lastMessage).toBe( + translateLocal('reportArchiveReasons.policyDeleted', { + policyName: policy.name, + }), + ); + }); + + it('should use the passed policy name for REMOVED_FROM_POLICY archive reason', async () => { + const testPolicyID = 'archivePolicyTest2'; + const policy: Policy = { + id: testPolicyID, + name: 'My Workspace', + type: CONST.POLICY.TYPE.TEAM, + } as Policy; + const report: Report = { + ...createRandomReport(0, undefined), + policyID: testPolicyID, + type: CONST.REPORT.TYPE.CHAT, + }; + const closedAction = { + ...createRandomReportAction(1), + actionName: CONST.REPORT.ACTIONS.TYPE.CLOSED, + originalMessage: { + policyName: policy.name, + reason: CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY, + }, + } as ReportAction; + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID}`, { + [closedAction.reportActionID]: closedAction, + }); + + const lastMessage = getLastMessageTextForReport({ + translate: translateLocal, + report, + lastActorDetails: null, + policy, + isReportArchived: true, + chatReport: undefined, + currentUserLogin: '', + }); + + expect(lastMessage).toBe( + translateLocal('reportArchiveReasons.removedFromPolicy', { + displayName: 'Hidden', + policyName: policy.name, + }), + ); + }); + }); }); describe('getPersonalDetailSearchTerms', () => { @@ -6536,6 +6639,7 @@ describe('OptionsListUtils', () => { translate: jest.fn().mockReturnValue(''), report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport, currentUserLogin: CURRENT_USER_EMAIL, @@ -6555,6 +6659,7 @@ describe('OptionsListUtils', () => { translate: jest.fn().mockReturnValue(''), report, lastActorDetails: null, + policy: undefined, isReportArchived: false, chatReport: undefined, currentUserLogin: CURRENT_USER_EMAIL, diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index f69f594a7036d..0107607018e03 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -13428,6 +13428,45 @@ describe('ReportUtils', () => { }); expect(result).toBe('Report Fallback Name'); }); + + it('should return noPolicyFound when policy, policies, and allPolicies are all empty and report has no policyName', async () => { + // Clear all policies from Onyx so allPolicies is empty + await Onyx.clear(); + await waitForBatchedUpdates(); + + const report: Report = { + ...createRandomReport(1, undefined), + policyID: 'nonexistent', + policyName: undefined, + oldPolicyName: undefined, + }; + + const result = getPolicyName({report, returnEmptyIfNotFound: true}); + expect(result).toBe(''); + }); + + it('should not trigger early return when allPolicies has data even if policy and policies params are empty', async () => { + const onyxPolicy: Policy = { + ...createRandomPolicy(1), + id: 'guardTestPolicy', + name: 'Guard Test Policy', + }; + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${onyxPolicy.id}`, onyxPolicy); + await waitForBatchedUpdates(); + + const report: Report = { + ...createRandomReport(1, undefined), + policyID: 'guardTestPolicy', + policyName: undefined, + }; + + // No policy or policies passed, but allPolicies has the policy via Onyx + const result = getPolicyName({report}); + expect(result).toBe('Guard Test Policy'); + + // Cleanup + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${onyxPolicy.id}`, null); + }); }); describe('getBillableAndTaxTotal', () => {