diff --git a/src/components/OptionListContextProvider.tsx b/src/components/OptionListContextProvider.tsx index 216df0141e3e7..e1777958dfdea 100644 --- a/src/components/OptionListContextProvider.tsx +++ b/src/components/OptionListContextProvider.tsx @@ -123,7 +123,8 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { for (const reportKey of changedReportKeys) { const report = changedReportsEntries[reportKey]; const reportID = reportKey.replace(ONYXKEYS.COLLECTION.REPORT, ''); - const {reportOption} = processReport(report, personalDetails, currentUserAccountID, reportAttributes?.reports); + const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]; + const {reportOption} = processReport(report, personalDetails, privateIsArchived, currentUserAccountID, reportAttributes?.reports); if (reportOption) { updatedReportsMap.set(reportID, reportOption); @@ -137,7 +138,7 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { reports: Array.from(updatedReportsMap.values()), }; }); - }, [changedReportsEntries, personalDetails, currentUserAccountID, reportAttributes?.reports]); + }, [changedReportsEntries, personalDetails, currentUserAccountID, reportAttributes?.reports, privateIsArchivedMap]); useEffect(() => { if (!changedReportActions || !areOptionsInitialized.current) { @@ -157,7 +158,8 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { } const reportID = key.replace(ONYXKEYS.COLLECTION.REPORT_ACTIONS, ''); - const {reportOption} = processReport(updatedReportsMap.get(reportID)?.item, personalDetails, currentUserAccountID, reportAttributes?.reports); + const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]; + const {reportOption} = processReport(updatedReportsMap.get(reportID)?.item, personalDetails, privateIsArchived, currentUserAccountID, reportAttributes?.reports); if (reportOption) { updatedReportsMap.set(reportID, reportOption); @@ -169,7 +171,7 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { reports: Array.from(updatedReportsMap.values()), }; }); - }, [changedReportActions, personalDetails, currentUserAccountID, reportAttributes?.reports]); + }, [changedReportActions, personalDetails, currentUserAccountID, reportAttributes?.reports, privateIsArchivedMap]); /** * This effect is used to update the options list when personal details change. diff --git a/src/hooks/useFilteredOptions.ts b/src/hooks/useFilteredOptions.ts index 5d578f2ea581b..c18c4c0da5153 100644 --- a/src/hooks/useFilteredOptions.ts +++ b/src/hooks/useFilteredOptions.ts @@ -75,9 +75,9 @@ function useFilteredOptions(config: UseFilteredOptionsConfig = {}): UseFilteredO const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true}); const [allPersonalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); const reportAttributesDerived = useReportAttributes(); - const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const privateIsArchivedMap = usePrivateIsArchivedMap(); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const totalReports = allReports ? Object.keys(allReports).length : 0; diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 5a5bf56c67551..264b3ff0d6e4b 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -1261,6 +1261,7 @@ function isReportSelected(reportOption: SearchOptionData, selectedOptions: Array function processReport( report: OnyxEntry | null, personalDetails: OnyxEntry, + privateIsArchived: string | undefined, currentUserAccountID: number, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ): { @@ -1286,7 +1287,7 @@ function processReport( reportMapEntry, reportOption: { item: report, - ...createOption(accountIDs, personalDetails, report, currentUserAccountID, undefined, reportAttributesDerived), + ...createOption(accountIDs, personalDetails, report, currentUserAccountID, undefined, reportAttributesDerived, privateIsArchived), }, }; } @@ -1305,7 +1306,8 @@ function createOptionList( if (reports) { for (const report of Object.values(reports)) { - const {reportMapEntry, reportOption} = processReport(report, personalDetails, currentUserAccountID, reportAttributesDerived); + const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; + const {reportMapEntry, reportOption} = processReport(report, personalDetails, privateIsArchived, currentUserAccountID, reportAttributesDerived); if (reportMapEntry) { const [accountID, reportValue] = reportMapEntry; @@ -1424,7 +1426,8 @@ function createFilteredOptionList( // Step 5: Process the limited set of reports (performance optimization) const reportOptions: Array> = []; for (const report of limitedReports) { - const {reportMapEntry, reportOption} = processReport(report, personalDetails, currentUserAccountID, reportAttributesDerived); + const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]; + const {reportMapEntry, reportOption} = processReport(report, personalDetails, privateIsArchived, currentUserAccountID, reportAttributesDerived); if (reportMapEntry) { const [accountID, reportValue] = reportMapEntry; diff --git a/tests/unit/OptionListContextProviderTest.tsx b/tests/unit/OptionListContextProviderTest.tsx index 791238afb8656..1239397dcc637 100644 --- a/tests/unit/OptionListContextProviderTest.tsx +++ b/tests/unit/OptionListContextProviderTest.tsx @@ -5,7 +5,7 @@ import OptionListContextProvider, {useOptionsList} from '@components/OptionListC import useOnyx from '@hooks/useOnyx'; import usePrivateIsArchivedMap from '@hooks/usePrivateIsArchivedMap'; import type {OptionList, SearchOption} from '@libs/OptionsListUtils'; -import {createOptionFromReport, createOptionList} from '@libs/OptionsListUtils'; +import {createOptionFromReport, createOptionList, processReport} from '@libs/OptionsListUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Report} from '@src/types/onyx'; @@ -28,6 +28,7 @@ jest.mock('@components/OnyxListItemProvider', () => ({ })); const mockCreateOptionList = createOptionList as jest.MockedFunction; +const mockProcessReport = processReport as jest.MockedFunction; const mockCreateOptionFromReport = createOptionFromReport as jest.MockedFunction; const mockUseOnyx = useOnyx as jest.MockedFunction; const mockUsePersonalDetails = usePersonalDetails as jest.MockedFunction; @@ -122,6 +123,59 @@ describe('OptionListContextProvider', () => { expect(mockCreateOptionList).toHaveBeenCalledTimes(1); }); + it('calls processReport with privateIsArchived when reports change', () => { + const reportID = '1'; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; + const report = {reportID}; + + const {result, rerender} = renderHook(({shouldInitialize}) => useOptionsList({shouldInitialize}), { + initialProps: {shouldInitialize: false}, + wrapper, + }); + + act(() => { + result.current.initializeOptions(); + }); + + mockProcessReport.mockClear(); + + onyxState = { + ...onyxState, + [ONYXKEYS.COLLECTION.REPORT]: {[reportKey]: report}, + }; + onyxSourceValues = { + ...onyxSourceValues, + [ONYXKEYS.COLLECTION.REPORT]: {[reportKey]: report}, + }; + rerender({shouldInitialize: false}); + + expect(mockProcessReport).toHaveBeenCalled(); + }); + + it('calls processReport with privateIsArchived when report actions change', () => { + const reportID = '2'; + const reportActionsKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`; + + const {result, rerender} = renderHook(({shouldInitialize}) => useOptionsList({shouldInitialize}), { + initialProps: {shouldInitialize: false}, + wrapper, + }); + + act(() => { + result.current.initializeOptions(); + }); + + mockProcessReport.mockClear(); + + onyxSourceValues = { + ...onyxSourceValues, + [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: {[reportActionsKey]: {someAction: {}}}, + }; + rerender({shouldInitialize: false}); + + expect(mockProcessReport).toHaveBeenCalled(); + }); + it('passes privateIsArchived to createOptionFromReport when personal details change', () => { const reportID = '1'; const accountID = '12345'; diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 09324edb76f98..d171b3684999c 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -3663,6 +3663,113 @@ describe('OptionsListUtils', () => { expect(misterFantasticOption?.private_isArchived).toBe('2023-06-15 10:00:00'); expect(invisibleWomanOption?.private_isArchived).toBe('2023-07-20 15:30:00'); }); + + it('should set private_isArchived on report options when privateIsArchivedMap is provided', () => { + renderLocaleContextProvider(); + // Given a privateIsArchivedMap with archived reports + const privateIsArchivedMap: PrivateIsArchivedMap = { + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}3`]: '2023-06-15 10:00:00', + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}5`]: '2023-07-20 15:30:00', + }; + + // When we call createOptionList with this privateIsArchivedMap + const result = createOptionList(PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, privateIsArchivedMap, REPORTS); + + // Then the report options should have the correct private_isArchived values + const report3Option = result.reports.find((r) => r.item?.reportID === '3'); + const report5Option = result.reports.find((r) => r.item?.reportID === '5'); + const report1Option = result.reports.find((r) => r.item?.reportID === '1'); + + expect(report3Option?.private_isArchived).toBe('2023-06-15 10:00:00'); + expect(report5Option?.private_isArchived).toBe('2023-07-20 15:30:00'); + // Report 1 should not have private_isArchived since it's not in the map + expect(report1Option?.private_isArchived).toBeUndefined(); + }); + }); + + describe('createFilteredOptionList()', () => { + it('should set private_isArchived on report options when privateIsArchivedMap is provided', () => { + renderLocaleContextProvider(); + // Given a privateIsArchivedMap with archived reports + const privateIsArchivedMap: PrivateIsArchivedMap = { + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}3`]: '2023-06-15 10:00:00', + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}5`]: '2023-07-20 15:30:00', + }; + + // When we call createFilteredOptionList with this privateIsArchivedMap + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, CURRENT_USER_ACCOUNT_ID, undefined, privateIsArchivedMap); + + // Then the report options should have the correct private_isArchived values + const report3Option = result.reports.find((r) => r.item?.reportID === '3'); + const report5Option = result.reports.find((r) => r.item?.reportID === '5'); + const report1Option = result.reports.find((r) => r.item?.reportID === '1'); + + expect(report3Option?.private_isArchived).toBe('2023-06-15 10:00:00'); + expect(report5Option?.private_isArchived).toBe('2023-07-20 15:30:00'); + // Report 1 should not have private_isArchived since it's not in the map + expect(report1Option?.private_isArchived).toBeUndefined(); + }); + + it('should not set private_isArchived from map when privateIsArchivedMap is empty', () => { + renderLocaleContextProvider(); + // Given an empty privateIsArchivedMap + const emptyMap: PrivateIsArchivedMap = {}; + + // When we call createFilteredOptionList with an empty privateIsArchivedMap + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, CURRENT_USER_ACCOUNT_ID, undefined, emptyMap); + + // Then reports NOT in Onyx (like report 3, 5) should not have private_isArchived set + // Note: Report 10 gets private_isArchived from Onyx (set in beforeAll) + const report3Option = result.reports.find((r) => r.item?.reportID === '3'); + const report5Option = result.reports.find((r) => r.item?.reportID === '5'); + expect(report3Option?.private_isArchived).toBeUndefined(); + expect(report5Option?.private_isArchived).toBeUndefined(); + }); + + it('should correctly map multiple archived reports in privateIsArchivedMap', () => { + renderLocaleContextProvider(); + // Given a privateIsArchivedMap with multiple archived reports + const privateIsArchivedMap: PrivateIsArchivedMap = { + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}1`]: '2023-01-01 00:00:00', + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}3`]: '2023-06-15 10:00:00', + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}5`]: '2023-07-20 15:30:00', + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}7`]: '2023-12-31 23:59:59', + }; + + // When we call createFilteredOptionList with this privateIsArchivedMap + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, CURRENT_USER_ACCOUNT_ID, undefined, privateIsArchivedMap); + + // Then the report options should have the correct private_isArchived values + const report1Option = result.reports.find((r) => r.item?.reportID === '1'); + const report3Option = result.reports.find((r) => r.item?.reportID === '3'); + const report5Option = result.reports.find((r) => r.item?.reportID === '5'); + const report7Option = result.reports.find((r) => r.item?.reportID === '7'); + const report2Option = result.reports.find((r) => r.item?.reportID === '2'); + + expect(report1Option?.private_isArchived).toBe('2023-01-01 00:00:00'); + expect(report3Option?.private_isArchived).toBe('2023-06-15 10:00:00'); + expect(report5Option?.private_isArchived).toBe('2023-07-20 15:30:00'); + expect(report7Option?.private_isArchived).toBe('2023-12-31 23:59:59'); + // Report 2 should not have private_isArchived since it's not in the map + expect(report2Option?.private_isArchived).toBeUndefined(); + }); + + it('should respect maxRecentReports option while preserving archived status', () => { + renderLocaleContextProvider(); + // Given a privateIsArchivedMap and a small maxRecentReports limit + const privateIsArchivedMap: PrivateIsArchivedMap = { + [`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}7`]: '2023-12-31 23:59:59', // Report 7 has largest lastVisibleActionCreated + }; + + // When we call createFilteredOptionList with maxRecentReports limit + const result = createFilteredOptionList(PERSONAL_DETAILS, REPORTS, CURRENT_USER_ACCOUNT_ID, undefined, privateIsArchivedMap, { + maxRecentReports: 5, + }); + + // Then the report 7 (most recent) should still have private_isArchived set + const report7Option = result.reports.find((r) => r.item?.reportID === '7'); + expect(report7Option?.private_isArchived).toBe('2023-12-31 23:59:59'); + }); }); describe('filterSelfDMChat()', () => {