diff --git a/src/components/AvatarCropModal/AvatarCropModal.tsx b/src/components/AvatarCropModal/AvatarCropModal.tsx index 033279932f39..ac77934485a9 100644 --- a/src/components/AvatarCropModal/AvatarCropModal.tsx +++ b/src/components/AvatarCropModal/AvatarCropModal.tsx @@ -7,7 +7,6 @@ import ImageSize from 'react-native-image-size'; import {interpolate, useSharedValue} from 'react-native-reanimated'; import {scheduleOnUI} from 'react-native-worklets'; import ActivityIndicator from '@components/ActivityIndicator'; -import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Button from '@components/Button'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; @@ -24,6 +23,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import cropOrRotateImage from '@libs/cropOrRotateImage'; import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import CONST from '@src/CONST'; import type IconAsset from '@src/types/utils/IconAsset'; import ImageCropView from './ImageCropView'; diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 2d81005e4feb..96c2c9c6b7f9 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -2,7 +2,6 @@ import React, {useMemo} from 'react'; import {Keyboard, StyleSheet, View} from 'react-native'; import type {SvgProps} from 'react-native-svg'; import ActivityIndicator from '@components/ActivityIndicator'; -import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import Avatar from '@components/Avatar'; import AvatarWithDisplayName from '@components/AvatarWithDisplayName'; import Header from '@components/Header'; @@ -21,6 +20,7 @@ import useThemeStyles from '@hooks/useThemeStyles'; import useThrottledButtonState from '@hooks/useThrottledButtonState'; import getButtonState from '@libs/getButtonState'; import Navigation from '@libs/Navigation/Navigation'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import variables from '@styles/variables'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; diff --git a/src/components/LHNOptionsList/LHNOptionsList.tsx b/src/components/LHNOptionsList/LHNOptionsList.tsx index 20f8ed8a0c0a..fd90762c894a 100644 --- a/src/components/LHNOptionsList/LHNOptionsList.tsx +++ b/src/components/LHNOptionsList/LHNOptionsList.tsx @@ -9,7 +9,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import type {BlockingViewProps} from '@components/BlockingViews/BlockingView'; import BlockingView from '@components/BlockingViews/BlockingView'; import Icon from '@components/Icon'; -import * as Expensicons from '@components/Icon/Expensicons'; import {ScrollOffsetContext} from '@components/ScrollOffsetContextProvider'; import TextBlock from '@components/TextBlock'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; @@ -49,7 +48,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio const flashListRef = useRef>(null); const route = useRoute(); const isScreenFocused = useIsFocused(); - const expensifyIcons = useMemoizedLazyExpensifyIcons(['MagnifyingGlass']); + const expensifyIcons = useMemoizedLazyExpensifyIcons(['MagnifyingGlass', 'Plus']); const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); const reportAttributes = useReportAttributes(); @@ -132,7 +131,7 @@ function LHNOptionsList({style, contentContainerStyles, data, onSelectRow, optio text={translate('common.emptyLHN.subtitleText2')} /> { if (!changedReportActions || !areOptionsInitialized.current) { @@ -158,8 +159,10 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { } const reportID = key.replace(ONYXKEYS.COLLECTION.REPORT_ACTIONS, ''); + const reportItem = updatedReportsMap.get(reportID)?.item; const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`]; - const {reportOption} = processReport(updatedReportsMap.get(reportID)?.item, personalDetails, privateIsArchived, currentUserAccountID, reportAttributes?.reports); + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportItem?.chatReportID}`]; + const {reportOption} = processReport(reportItem, personalDetails, privateIsArchived, currentUserAccountID, chatReport, reportAttributes?.reports); if (reportOption) { updatedReportsMap.set(reportID, reportOption); @@ -171,7 +174,7 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { reports: Array.from(updatedReportsMap.values()), }; }); - }, [changedReportActions, personalDetails, currentUserAccountID, reportAttributes?.reports, privateIsArchivedMap]); + }, [changedReportActions, personalDetails, currentUserAccountID, reports, reportAttributes?.reports, privateIsArchivedMap]); /** * This effect is used to update the options list when personal details change. @@ -229,7 +232,10 @@ function OptionsListContextProvider({children}: OptionsListProviderProps) { } const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]; - const newReportOption = createOptionFromReport(report, personalDetails, currentUserAccountID, privateIsArchived, reportAttributes?.reports, {showPersonalDetails: true}); + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`]; + const newReportOption = createOptionFromReport(report, personalDetails, currentUserAccountID, chatReport, privateIsArchived, reportAttributes?.reports, { + showPersonalDetails: true, + }); const replaceIndex = options.reports.findIndex((option) => option.reportID === report.reportID); newReportOptions.push({ newReportOption, diff --git a/src/components/PDFThumbnail/index.native.tsx b/src/components/PDFThumbnail/index.native.tsx index 09778fde3194..024bbad2a627 100644 --- a/src/components/PDFThumbnail/index.native.tsx +++ b/src/components/PDFThumbnail/index.native.tsx @@ -2,9 +2,9 @@ import React, {useState} from 'react'; import {View} from 'react-native'; import Pdf from 'react-native-pdf'; import LoadingIndicator from '@components/LoadingIndicator'; -import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import useThemeStyles from '@hooks/useThemeStyles'; import addEncryptedAuthTokenToURL from '@libs/addEncryptedAuthTokenToURL'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import PDFThumbnailError from './PDFThumbnailError'; import type PDFThumbnailProps from './types'; diff --git a/src/components/Search/SearchFiltersChatsSelector.tsx b/src/components/Search/SearchFiltersChatsSelector.tsx index 24c27005f8fd..b445e63a4b82 100644 --- a/src/components/Search/SearchFiltersChatsSelector.tsx +++ b/src/components/Search/SearchFiltersChatsSelector.tsx @@ -67,8 +67,10 @@ function SearchFiltersChatsSelector({initialReportIDs, onFiltersUpdate, isScreen const selectedOptions: OptionData[] = selectedReportIDs.map((id) => { const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${id}`]; + const reportData = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`]; + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportData?.chatReportID}`]; const report = getSelectedOptionData( - createOptionFromReport({...reports?.[`${ONYXKEYS.COLLECTION.REPORT}${id}`], reportID: id}, personalDetails, currentUserAccountID, privateIsArchived, reportAttributesDerived), + createOptionFromReport({...reportData, reportID: id}, personalDetails, currentUserAccountID, chatReport, privateIsArchived, reportAttributesDerived), ); const isReportArchived = !!privateIsArchived; const alternateText = getAlternateText(report, {}, isReportArchived, currentUserAccountID, {}, reportAttributesDerived); diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 9db1bbc9960e..2b33db85cf8e 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -115,7 +115,8 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla } const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${contextualReportID}`]; - const option = createOptionFromReport(report, personalDetails, currentUserAccountID, privateIsArchived, undefined, {showPersonalDetails: true}); + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.chatReportID}`]; + const option = createOptionFromReport(report, personalDetails, currentUserAccountID, chatReport, privateIsArchived, undefined, {showPersonalDetails: true}); reportForContextualSearch = option; } diff --git a/src/components/TransactionItemRow/ReceiptPreview/index.tsx b/src/components/TransactionItemRow/ReceiptPreview/index.tsx index f2ffbb0c8da2..c102d9fdfc6f 100644 --- a/src/components/TransactionItemRow/ReceiptPreview/index.tsx +++ b/src/components/TransactionItemRow/ReceiptPreview/index.tsx @@ -4,7 +4,6 @@ import type {LayoutChangeEvent} from 'react-native'; import {StyleSheet, View} from 'react-native'; import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'; import ActivityIndicator from '@components/ActivityIndicator'; -import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import DistanceEReceipt from '@components/DistanceEReceipt'; import EReceiptWithSizeCalculation from '@components/EReceiptWithSizeCalculation'; import type {ImageOnLoadEvent} from '@components/Image/types'; @@ -12,6 +11,7 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useResponsiveLayoutOnWideRHP from '@hooks/useResponsiveLayoutOnWideRHP'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {hasReceiptSource, isDistanceRequest, isManualDistanceRequest, isPerDiemRequest} from '@libs/TransactionUtils'; import variables from '@styles/variables'; import Image from '@src/components/Image'; diff --git a/src/hooks/useUserToInviteReports.ts b/src/hooks/useUserToInviteReports.ts new file mode 100644 index 000000000000..1fe6f6edf904 --- /dev/null +++ b/src/hooks/useUserToInviteReports.ts @@ -0,0 +1,15 @@ +import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; +import type {SearchOptionData} from '@libs/OptionsListUtils/types'; +import ONYXKEYS from '@src/ONYXKEYS'; +import useOnyx from './useOnyx'; + +/** + * For policy expense chat invitees, resolves the expense report and its associated chat report. + */ +export default function useUserToInviteReports(userToInvite: SearchOptionData | null | undefined) { + const userToInviteReportID = userToInvite?.isPolicyExpenseChat ? userToInvite.reportID : undefined; + const [userToInviteExpenseReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(userToInviteReportID)}`); + const [userToInviteChatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(userToInviteExpenseReport?.chatReportID)}`); + + return {userToInviteExpenseReport, userToInviteChatReport}; +} diff --git a/src/libs/OptionsListUtils/index.ts b/src/libs/OptionsListUtils/index.ts index 2dd473d0db8a..f4b8ad5a68c2 100644 --- a/src/libs/OptionsListUtils/index.ts +++ b/src/libs/OptionsListUtils/index.ts @@ -452,6 +452,8 @@ function getAlternateText( reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ) { const report = getReportOrDraftReport(option.reportID); + // TODO: This allReports usage is temporary and will be removed once the full Onyx.connect() refactor is complete (https://github.com/Expensify/App/issues/66378) + const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; const isAdminRoom = reportUtilsIsAdminRoom(report); const isAnnounceRoom = reportUtilsIsAnnounceRoom(report); const isGroupChat = reportUtilsIsGroupChat(report); @@ -459,7 +461,7 @@ function getAlternateText( const formattedLastMessageText = formatReportLastMessageText(Parser.htmlToText(option.lastMessageText ?? '')) || // eslint-disable-next-line @typescript-eslint/no-deprecated - getLastMessageTextForReport({translate: translateLocal, report, lastActorDetails, isReportArchived, reportAttributesDerived}); + getLastMessageTextForReport({translate: translateLocal, report, lastActorDetails, isReportArchived, chatReport, reportAttributesDerived}); const reportPrefix = getReportSubtitlePrefix(report); const formattedLastMessageTextWithPrefix = reportPrefix + formattedLastMessageText; @@ -590,6 +592,7 @@ function getLastMessageTextForReport({ policy, isReportArchived = false, policyForMovingExpensesID, + chatReport, reportMetadata, reportAttributesDerived, policyTags, @@ -603,6 +606,7 @@ function getLastMessageTextForReport({ policy?: OnyxEntry; isReportArchived?: boolean; policyForMovingExpensesID?: string; + chatReport: OnyxEntry; reportMetadata?: OnyxEntry; reportAttributesDerived?: ReportAttributesDerivedValue['reports']; policyTags?: OnyxEntry; @@ -852,7 +856,6 @@ function getLastMessageTextForReport({ } if (reportID && !lastMessageTextFromReport && lastReportAction) { - const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; // If the report is a one-transaction report, get the last message text from combined report actions so the LHN can display modifications to the transaction thread or the report itself const transactionThreadReportID = getOneTransactionThreadReportID(report, chatReport, allSortedReportActions[reportID]); if (transactionThreadReportID) { @@ -894,6 +897,7 @@ function createOption( personalDetails: OnyxInputOrEntry, report: OnyxInputOrEntry, currentUserAccountID: number, + chatReport: OnyxEntry, config?: PreviewConfig, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], privateIsArchived?: string, @@ -978,6 +982,7 @@ function createOption( report, lastActorDetails, isReportArchived: !!result.private_isArchived, + chatReport, reportAttributesDerived, }); result.alternateText = @@ -1038,6 +1043,8 @@ function getReportOption( reportDrafts?: OnyxCollection, ): OptionData { const report = getReportOrDraftReport(participant.reportID, undefined, undefined, reportDrafts); + // TODO: This allReports usage is temporary and will be removed once the full Onyx.connect() refactor is complete (https://github.com/Expensify/App/issues/66378) + const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); const option = createOption( @@ -1045,6 +1052,7 @@ function getReportOption( personalDetails ?? {}, !isEmptyObject(report) ? report : undefined, currentUserAccountID, + chatReport, { showChatPreviewLine: false, forcePolicyNamePreview: false, @@ -1093,6 +1101,7 @@ function getReportDisplayOption( currentUserAccountID: number, personalDetails: OnyxEntry, privateIsArchived: string | undefined, + chatReport: OnyxEntry, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ): OptionData { const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); @@ -1102,6 +1111,7 @@ function getReportDisplayOption( personalDetails ?? {}, !isEmptyObject(report) ? report : undefined, currentUserAccountID, + chatReport, { showChatPreviewLine: false, forcePolicyNamePreview: false, @@ -1140,10 +1150,10 @@ function getPolicyExpenseReportOption( participant: Participant | SearchOptionData, currentUserAccountID: number, personalDetails: OnyxEntry, + expenseReport: OnyxEntry, + chatReport: OnyxEntry, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ): SearchOptionData { - const expenseReport = reportUtilsIsPolicyExpenseChat(participant) ? getReportOrDraftReport(participant.reportID) : null; - const visibleParticipantAccountIDs = Object.entries(expenseReport?.participants ?? {}) .filter(([, reportParticipant]) => reportParticipant && !isHiddenForCurrentUser(reportParticipant.notificationPreference)) .map(([accountID]) => Number(accountID)); @@ -1153,6 +1163,7 @@ function getPolicyExpenseReportOption( personalDetails ?? {}, !isEmptyObject(expenseReport) ? expenseReport : null, currentUserAccountID, + chatReport, { showChatPreviewLine: false, forcePolicyNamePreview: false, @@ -1268,6 +1279,7 @@ function processReport( personalDetails: OnyxEntry, privateIsArchived: string | undefined, currentUserAccountID: number, + chatReport: OnyxEntry, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ): { reportMapEntry?: [number, Report]; // The entry to add to reportMapForAccountIDs if applicable @@ -1292,7 +1304,7 @@ function processReport( reportMapEntry, reportOption: { item: report, - ...createOption(accountIDs, personalDetails, report, currentUserAccountID, undefined, reportAttributesDerived, privateIsArchived), + ...createOption(accountIDs, personalDetails, report, currentUserAccountID, chatReport, undefined, reportAttributesDerived, privateIsArchived), }, }; } @@ -1301,7 +1313,7 @@ function createOptionList( personalDetails: OnyxEntry, currentUserAccountID: number, privateIsArchivedMap: PrivateIsArchivedMap, - reports?: OnyxCollection, + reports: OnyxCollection, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], ) { const span = Sentry.startInactiveSpan({name: 'createOptionList'}); @@ -1312,7 +1324,8 @@ function createOptionList( if (reports) { for (const report of Object.values(reports)) { const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; - const {reportMapEntry, reportOption} = processReport(report, personalDetails, privateIsArchived, currentUserAccountID, reportAttributesDerived); + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; + const {reportMapEntry, reportOption} = processReport(report, personalDetails, privateIsArchived, currentUserAccountID, chatReport, reportAttributesDerived); if (reportMapEntry) { const [accountID, reportValue] = reportMapEntry; @@ -1328,6 +1341,7 @@ function createOptionList( const allPersonalDetailsOptions = Object.values(personalDetails ?? {}).map((personalDetail) => { const report = reportMapForAccountIDs[personalDetail?.accountID ?? CONST.DEFAULT_NUMBER_ID]; const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; return { item: personalDetail, @@ -1336,6 +1350,7 @@ function createOptionList( personalDetails, report, currentUserAccountID, + chatReport, { showPersonalDetails: true, }, @@ -1432,7 +1447,8 @@ function createFilteredOptionList( const reportOptions: Array> = []; for (const report of limitedReports) { const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`]; - const {reportMapEntry, reportOption} = processReport(report, personalDetails, privateIsArchived, currentUserAccountID, reportAttributesDerived); + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; + const {reportMapEntry, reportOption} = processReport(report, personalDetails, privateIsArchived, currentUserAccountID, chatReport, reportAttributesDerived); if (reportMapEntry) { const [accountID, reportValue] = reportMapEntry; @@ -1462,6 +1478,7 @@ function createFilteredOptionList( const report = reportMapForAccountIDs[accountID]; const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; + const chatReport = reports?.[`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`]; return { item: personalDetail, ...createOption( @@ -1469,6 +1486,7 @@ function createFilteredOptionList( personalDetails, reportMapForAccountIDs[accountID], currentUserAccountID, + chatReport, {showPersonalDetails: true}, reportAttributesDerived, privateIsArchived, @@ -1487,6 +1505,7 @@ function createOptionFromReport( report: Report, personalDetails: OnyxEntry, currentUserAccountID: number, + chatReport: OnyxEntry, privateIsArchived: string | undefined, reportAttributesDerived?: ReportAttributesDerivedValue['reports'], config?: PreviewConfig, @@ -1495,7 +1514,7 @@ function createOptionFromReport( return { item: report, - ...createOption(accountIDs, personalDetails, report, currentUserAccountID, config, reportAttributesDerived, privateIsArchived), + ...createOption(accountIDs, personalDetails, report, currentUserAccountID, chatReport, config, reportAttributesDerived, privateIsArchived), }; } @@ -1818,7 +1837,7 @@ function getUserToInviteOption({ login: searchValue, }, }; - const userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, currentUserAccountID, { + const userToInvite = createOption([optimisticAccountID], personalDetailsExtended, null, currentUserAccountID, undefined, { showChatPreviewLine, }); userToInvite.isOptimisticAccount = true; @@ -1953,7 +1972,7 @@ function getUserToInviteContactOption({ return userToInvite; } -function isValidReport(option: SearchOption, policy: OnyxEntry, config: IsValidReportsConfig, draftComment: string | undefined): boolean { +function isValidReport(option: SearchOption, policy: OnyxEntry, config: IsValidReportsConfig, draftComment: string | undefined, chatReport: OnyxEntry): boolean { const { betas = [], includeMultipleParticipantReports = false, @@ -1976,8 +1995,6 @@ function isValidReport(option: SearchOption, policy: OnyxEntry, shouldAlwaysIncludeDM, } = config; const topmostReportId = Navigation.getTopmostReportId(); - - const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${option.item.chatReportID}`]; const doesReportHaveViolations = shouldDisplayViolationsRBRInLHN(option.item, transactionViolations); const shouldBeInOptionList = shouldReportBeInOptionList({ @@ -2337,6 +2354,8 @@ function getValidOptions( } const draftComment = draftComments?.[`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${report.reportID}`]; + // TODO: This allReports usage is temporary and will be removed once the full Onyx.connect() refactor is complete (https://github.com/Expensify/App/issues/66378) + const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${report.item.chatReportID}`]; return isValidReport( report, @@ -2349,6 +2368,7 @@ function getValidOptions( currentUserAccountID, }, draftComment, + chatReport, ); }; @@ -2565,7 +2585,6 @@ function getSearchOptions({ loginList, currentUserAccountID, currentUserEmail, - { betas, includeRecentReports, @@ -2807,9 +2826,13 @@ function formatSectionsFromSearchTerm( data: shouldGetOptionDetails ? selectedOptions.map((participant) => { const isReportPolicyExpenseChat = participant.isPolicyExpenseChat ?? false; - return isReportPolicyExpenseChat - ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, reportAttributesDerived) - : getParticipantsOption(participant, personalDetails); + if (isReportPolicyExpenseChat) { + // TODO: This allReports usage is temporary and will be removed once the full Onyx.connect() refactor is complete (https://github.com/Expensify/App/issues/66378) + const expenseReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.reportID}`]; + const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.chatReportID}`]; + return getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, expenseReport, chatReport, reportAttributesDerived); + } + return getParticipantsOption(participant, personalDetails); }) : selectedOptions, }, @@ -2835,9 +2858,13 @@ function formatSectionsFromSearchTerm( data: shouldGetOptionDetails ? selectedParticipantsWithoutDetails.map((participant) => { const isReportPolicyExpenseChat = participant.isPolicyExpenseChat ?? false; - return isReportPolicyExpenseChat - ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, reportAttributesDerived) - : getParticipantsOption(participant, personalDetails); + if (isReportPolicyExpenseChat) { + // TODO: This allReports usage is temporary and will be removed once the full Onyx.connect() refactor is complete (https://github.com/Expensify/App/issues/66378) + const expenseReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${participant.reportID}`]; + const chatReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${expenseReport?.chatReportID}`]; + return getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, expenseReport, chatReport, reportAttributesDerived); + } + return getParticipantsOption(participant, personalDetails); }) : selectedParticipantsWithoutDetails, }, diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts index d1a292180817..bc509a066f2e 100644 --- a/src/libs/SidebarUtils.ts +++ b/src/libs/SidebarUtils.ts @@ -677,6 +677,7 @@ function getOptionData({ movedFromReport, movedToReport, currentUserAccountID, + chatReport, reportAttributesDerived, }: { report: OnyxEntry; @@ -698,6 +699,7 @@ function getOptionData({ movedFromReport?: OnyxEntry; movedToReport?: OnyxEntry; currentUserAccountID: number; + chatReport: OnyxEntry; reportAttributesDerived?: ReportAttributesDerivedValue['reports']; }): OptionData | undefined { // When a user signs out, Onyx is cleared. Due to the lazy rendering with a virtual list, it's possible for @@ -837,6 +839,7 @@ function getOptionData({ movedToReport, policy, isReportArchived, + chatReport, reportAttributesDerived, }); } diff --git a/src/pages/Share/ShareDetailsPage.tsx b/src/pages/Share/ShareDetailsPage.tsx index 1ea30a2ac3a0..e03e1832f308 100644 --- a/src/pages/Share/ShareDetailsPage.tsx +++ b/src/pages/Share/ShareDetailsPage.tsx @@ -68,9 +68,10 @@ function ShareDetailsPage({route}: ShareDetailsPageProps) { const privateIsArchivedMap = usePrivateIsArchivedMap(); const privateIsArchived = privateIsArchivedMap[`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`]; const ancestors = useAncestors(report); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report?.chatReportID}`); const displayReport = useMemo( - () => getReportDisplayOption(report, unknownUserDetails, personalDetail.accountID, personalDetails, privateIsArchived, reportAttributesDerived), - [report, unknownUserDetails, personalDetails, privateIsArchived, reportAttributesDerived, personalDetail.accountID], + () => getReportDisplayOption(report, unknownUserDetails, personalDetail.accountID, personalDetails, privateIsArchived, chatReport, reportAttributesDerived), + [report, unknownUserDetails, personalDetails, privateIsArchived, reportAttributesDerived, personalDetail.accountID, chatReport], ); const shouldShowAttachment = !isTextShared; diff --git a/src/pages/iou/SplitBillDetailsPage.tsx b/src/pages/iou/SplitBillDetailsPage.tsx index 46b57a4d2f74..98a68e6248c4 100644 --- a/src/pages/iou/SplitBillDetailsPage.tsx +++ b/src/pages/iou/SplitBillDetailsPage.tsx @@ -66,13 +66,14 @@ function SplitBillDetailsPage({route, report, reportAction}: SplitBillDetailsPag const [session] = useOnyx(ONYXKEYS.SESSION); const reportAttributesDerived = useReportAttributes(); const [betas] = useOnyx(ONYXKEYS.BETAS); + const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.chatReportID)}`); // In case this is workspace split expense, we manually add the workspace as the second participant of the split expense // because we don't save any accountID in the report action's originalMessage other than the payee's accountID let participants: Array; if (isPolicyExpenseChat(report)) { participants = [ getParticipantsOption({accountID: participantAccountIDs.at(0), selected: true, reportID: ''}, personalDetails), - getPolicyExpenseReportOption({...report, selected: true, reportID}, currentUserPersonalDetails.accountID, personalDetails, reportAttributesDerived), + getPolicyExpenseReportOption({...report, selected: true, reportID}, currentUserPersonalDetails.accountID, personalDetails, report, chatReport, reportAttributesDerived), ]; } else { participants = participantAccountIDs.map((accountID) => getParticipantsOption({accountID, selected: true, reportID: ''}, personalDetails)); diff --git a/src/pages/iou/request/MoneyRequestAccountantSelector.tsx b/src/pages/iou/request/MoneyRequestAccountantSelector.tsx index 80e011be7a78..91d27223c2a8 100644 --- a/src/pages/iou/request/MoneyRequestAccountantSelector.tsx +++ b/src/pages/iou/request/MoneyRequestAccountantSelector.tsx @@ -13,6 +13,7 @@ import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useReportAttributes from '@hooks/useReportAttributes'; import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus'; +import useUserToInviteReports from '@hooks/useUserToInviteReports'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import memoize from '@libs/memoize'; import { @@ -140,6 +141,8 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType return newOptions; }, [areOptionsInitialized, defaultOptions, debouncedSearchTerm, countryCode, loginList, currentUserAccountID, currentUserEmail, personalDetails]); + const {userToInviteExpenseReport, userToInviteChatReport} = useUserToInviteReports(chatOptions?.userToInvite); + /** * Returns the sections needed for the OptionsSelector */ @@ -189,7 +192,7 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType data: [chatOptions.userToInvite].map((participant) => { const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false; return isPolicyExpenseChat - ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, reportAttributesDerived) + ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, userToInviteExpenseReport, userToInviteChatReport, reportAttributesDerived) : getParticipantsOption(participant, personalDetails); }), sectionIndex: 3, @@ -213,6 +216,8 @@ function MoneyRequestAccountantSelector({onFinish, onAccountantSelected, iouType chatOptions.userToInvite, debouncedSearchTerm, personalDetails, + userToInviteExpenseReport, + userToInviteChatReport, reportAttributesDerived, translate, loginList, diff --git a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx index 19b379be5d3f..5f863d1f7c3c 100644 --- a/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx +++ b/src/pages/iou/request/MoneyRequestAttendeeSelector.tsx @@ -17,6 +17,7 @@ import useReportAttributes from '@hooks/useReportAttributes'; import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus'; import useSearchSelector from '@hooks/useSearchSelector'; import useThemeStyles from '@hooks/useThemeStyles'; +import useUserToInviteReports from '@hooks/useUserToInviteReports'; import {searchInServer} from '@libs/actions/Report'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import { @@ -151,6 +152,8 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde }; } + const {userToInviteExpenseReport, userToInviteChatReport} = useUserToInviteReports(orderedAvailableOptions?.userToInvite); + const shouldShowErrorMessage = selectedOptions.length < 1; const handleConfirmSelection = (_keyEvent?: GestureResponderEvent | KeyboardEvent, option?: OptionData) => { @@ -250,7 +253,7 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde data: [orderedAvailableOptions.userToInvite].map((participant) => { const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false; return isPolicyExpenseChat - ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, reportAttributesDerived) + ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, userToInviteExpenseReport, userToInviteChatReport, reportAttributesDerived) : getParticipantsOption(participant, personalDetails); }) as OptionData[], sectionIndex: 3, diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 5f805c131667..5e7c9125eb01 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -29,6 +29,7 @@ import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionS import useSearchSelector from '@hooks/useSearchSelector'; import useThemeStyles from '@hooks/useThemeStyles'; import useTransactionDraftValues from '@hooks/useTransactionDraftValues'; +import useUserToInviteReports from '@hooks/useUserToInviteReports'; import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import getPlatform from '@libs/getPlatform'; import goToSettings from '@libs/goToSettings'; @@ -238,6 +239,8 @@ function MoneyRequestParticipantsSelector({ const cleanSearchTerm = useMemo(() => debouncedSearchTerm.trim().toLowerCase(), [debouncedSearchTerm]); + const {userToInviteExpenseReport, userToInviteChatReport} = useUserToInviteReports(availableOptions?.userToInvite); + useEffect(() => { searchInServer(debouncedSearchTerm.trim()); }, [debouncedSearchTerm]); @@ -343,7 +346,7 @@ function MoneyRequestParticipantsSelector({ data: [availableOptions.userToInvite].map((participant) => { const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false; return isPolicyExpenseChat - ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, reportAttributesDerived) + ? getPolicyExpenseReportOption(participant, currentUserAccountID, personalDetails, userToInviteExpenseReport, userToInviteChatReport, reportAttributesDerived) : getParticipantsOption(participant, personalDetails); }), shouldShow: true, @@ -369,6 +372,8 @@ function MoneyRequestParticipantsSelector({ availableOptions.userToInvite, availableOptions.recentReports, availableOptions.personalDetails, + userToInviteExpenseReport, + userToInviteChatReport, isWorkspacesOnly, loginList, isPerDiemRequest, diff --git a/tests/perf-test/SidebarUtils.perf-test.ts b/tests/perf-test/SidebarUtils.perf-test.ts index 9947b3bdcbbb..348f9d055308 100644 --- a/tests/perf-test/SidebarUtils.perf-test.ts +++ b/tests/perf-test/SidebarUtils.perf-test.ts @@ -93,6 +93,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 1, + chatReport: undefined, }), ); }); diff --git a/tests/unit/OptionListContextProviderTest.tsx b/tests/unit/OptionListContextProviderTest.tsx index 1239397dcc63..3d4274e624b9 100644 --- a/tests/unit/OptionListContextProviderTest.tsx +++ b/tests/unit/OptionListContextProviderTest.tsx @@ -223,6 +223,154 @@ describe('OptionListContextProvider', () => { mockUsePersonalDetails.mockReturnValue(updatedPersonalDetails); rerender({shouldInitialize: false}); - expect(mockCreateOptionFromReport).toHaveBeenCalledWith(report, updatedPersonalDetails, expect.any(Number), 'true', undefined, {showPersonalDetails: true}); + expect(mockCreateOptionFromReport).toHaveBeenCalledWith(report, updatedPersonalDetails, expect.any(Number), undefined, 'true', undefined, {showPersonalDetails: true}); + }); + + it('passes resolved chatReport to processReport when changed reports have chatReportID', () => { + const reportID = '1'; + const chatReportID = '2'; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; + const chatReportKey = `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`; + const report = {reportID, chatReportID, participants: {}} as unknown as Report; + const chatReport = {reportID: chatReportID, participants: {}} as unknown as Report; + + const {result, rerender} = renderHook(({shouldInitialize}) => useOptionsList({shouldInitialize}), { + initialProps: {shouldInitialize: false}, + wrapper, + }); + + act(() => { + result.current.initializeOptions(); + }); + + mockProcessReport.mockClear(); + + onyxState = { + ...onyxState, + [ONYXKEYS.COLLECTION.REPORT]: { + [reportKey]: report, + [chatReportKey]: chatReport, + }, + }; + onyxSourceValues = { + ...onyxSourceValues, + [ONYXKEYS.COLLECTION.REPORT]: { + [reportKey]: report, + [chatReportKey]: chatReport, + }, + }; + + rerender({shouldInitialize: false}); + + expect(mockProcessReport).toHaveBeenCalledWith(report, {}, undefined, expect.any(Number), chatReport, undefined); + }); + + it('passes resolved chatReport to processReport when report actions change', () => { + const reportID = '1'; + const chatReportID = '2'; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; + const chatReportKey = `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`; + const report = {reportID, chatReportID, participants: {}} as unknown as Report; + const chatReport = {reportID: chatReportID, participants: {}} as unknown as Report; + + onyxState = { + ...onyxState, + [ONYXKEYS.COLLECTION.REPORT]: { + [reportKey]: report, + [chatReportKey]: chatReport, + }, + }; + onyxSourceValues = { + ...onyxSourceValues, + [ONYXKEYS.COLLECTION.REPORT]: { + [reportKey]: report, + [chatReportKey]: chatReport, + }, + }; + + const mockReportOption = {reportID, item: report, text: 'Report', keyForList: reportID} as unknown as SearchOption; + mockCreateOptionList.mockReturnValue({ + reports: [mockReportOption], + personalDetails: [], + } as OptionList); + + const {result, rerender} = renderHook(({shouldInitialize}) => useOptionsList({shouldInitialize}), { + initialProps: {shouldInitialize: false}, + wrapper, + }); + + act(() => { + result.current.initializeOptions(); + }); + + mockProcessReport.mockClear(); + + const reportActionsKey = `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportID}`; + onyxSourceValues = { + ...onyxSourceValues, + [ONYXKEYS.COLLECTION.REPORT_ACTIONS]: { + [reportActionsKey]: {someAction: {reportActionID: '100'}}, + }, + }; + + rerender({shouldInitialize: false}); + + expect(mockProcessReport).toHaveBeenCalledWith(report, {}, undefined, expect.any(Number), chatReport, undefined); + }); + + it('passes resolved chatReport to createOptionFromReport when personal details change and report has chatReportID', () => { + const reportID = '1'; + const chatReportID = '2'; + const accountID = '12345'; + const reportKey = `${ONYXKEYS.COLLECTION.REPORT}${reportID}`; + const chatReportKey = `${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`; + const report = { + reportID, + chatReportID, + participants: {[accountID]: {notificationPreference: 'always'}}, + }; + const chatReport = {reportID: chatReportID, participants: {}} as unknown as Report; + + const initialPersonalDetails = {[accountID]: {accountID: Number(accountID), firstName: 'John', lastName: 'Doe', login: 'john@test.com', displayName: 'John Doe'}}; + mockUsePersonalDetails.mockReturnValue(initialPersonalDetails); + + onyxState = { + ...onyxState, + [ONYXKEYS.COLLECTION.REPORT]: { + [reportKey]: report, + [chatReportKey]: chatReport, + }, + }; + onyxSourceValues = { + ...onyxSourceValues, + [ONYXKEYS.COLLECTION.REPORT]: { + [reportKey]: report, + [chatReportKey]: chatReport, + }, + }; + + const mockReportOption = {reportID, item: report, text: 'John Doe', keyForList: reportID} as unknown as SearchOption; + mockCreateOptionList.mockReturnValue({ + reports: [mockReportOption], + personalDetails: [], + } as OptionList); + mockCreateOptionFromReport.mockReturnValue(mockReportOption); + + const {result, rerender} = renderHook(({shouldInitialize}) => useOptionsList({shouldInitialize}), { + initialProps: {shouldInitialize: false}, + wrapper, + }); + + act(() => { + result.current.initializeOptions(); + }); + + mockCreateOptionFromReport.mockClear(); + + const updatedPersonalDetails = {[accountID]: {accountID: Number(accountID), firstName: 'Jane', lastName: 'Doe', login: 'john@test.com', displayName: 'Jane Doe'}}; + mockUsePersonalDetails.mockReturnValue(updatedPersonalDetails); + rerender({shouldInitialize: false}); + + expect(mockCreateOptionFromReport).toHaveBeenCalledWith(report, updatedPersonalDetails, expect.any(Number), chatReport, undefined, undefined, {showPersonalDetails: true}); }); }); diff --git a/tests/unit/OptionsListUtilsTest.tsx b/tests/unit/OptionsListUtilsTest.tsx index 727f56b95318..082732450972 100644 --- a/tests/unit/OptionsListUtilsTest.tsx +++ b/tests/unit/OptionsListUtilsTest.tsx @@ -4185,10 +4185,57 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION}${transaction.transactionID}`, transaction); await waitForBatchedUpdates(); - const result = createOption([1, 2], PERSONAL_DETAILS, report, CURRENT_USER_ACCOUNT_ID, {showChatPreviewLine: true}); + const result = createOption([1, 2], PERSONAL_DETAILS, report, CURRENT_USER_ACCOUNT_ID, undefined, {showChatPreviewLine: true}); expect(result.alternateText).toBe('Iron Man owes ₫34'); }); + + it('should work correctly when reports collection with chatReport is passed', async () => { + const reportID = '123'; + const chatReportID = '456'; + + const report: Report = { + ...createRandomReport(0, undefined), + reportID, + chatReportID, + participants: { + 1: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + 2: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + }, + }; + + const chatReport: Report = { + ...createRandomReport(1, undefined), + reportID: chatReportID, + }; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, chatReport); + await waitForBatchedUpdates(); + + const result = createOption([1, 2], PERSONAL_DETAILS, report, 1, chatReport); + + expect(result.reportID).toBe(reportID); + expect(typeof result.text).toBe('string'); + }); + + it('should work correctly when reports is undefined', async () => { + const report: Report = { + ...createRandomReport(0, undefined), + participants: { + 1: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + 2: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + }, + }; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, report); + await waitForBatchedUpdates(); + + // Should not throw when reports is undefined + const result = createOption([1, 2], PERSONAL_DETAILS, report, 1, undefined, undefined, undefined); + + expect(result.reportID).toBe(report.reportID); + }); }); describe('getLastMessageTextForReport', () => { @@ -4247,6 +4294,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); const reportPreviewMessage = getReportPreviewMessage(iouReport, iouAction, true, false, null, true, reportPreviewAction); const expected = formatReportLastMessageText(Parser.htmlToText(reportPreviewMessage)); @@ -4279,6 +4327,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(getMovedTransactionMessage(translateLocal, movedTransactionAction))); }); @@ -4302,6 +4351,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(translate(CONST.LOCALES.EN, 'iou.automaticallySubmitted'))); }); @@ -4326,6 +4376,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(translate(CONST.LOCALES.EN, 'iou.automaticallyApproved'))); }); @@ -4350,6 +4401,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(translate(CONST.LOCALES.EN, 'iou.automaticallyForwarded'))); }); @@ -4371,6 +4423,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(translate(CONST.LOCALES.EN, 'workspaceActions.forcedCorporateUpgrade'))); }); @@ -4391,6 +4444,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(getCustomTaxNameUpdateMessage(translateLocal, action)); }); @@ -4410,6 +4464,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(getCurrencyDefaultTaxUpdateMessage(translateLocal, action)); }); @@ -4429,6 +4484,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(getForeignCurrencyDefaultTaxUpdateMessage(translateLocal, action)); }); @@ -4448,6 +4504,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(getChangedApproverActionMessage(translateLocal, takeControlAction))); }); @@ -4467,6 +4524,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(getChangedApproverActionMessage(translateLocal, rerouteAction))); }); @@ -4486,6 +4544,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(Parser.htmlToText(getMovedActionMessage(translateLocal, movedAction, report))); }); @@ -4509,6 +4568,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); // Then it should return the DYNAMIC_EXTERNAL_WORKFLOW_ROUTED message @@ -4534,6 +4594,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(result).toBe(expectedVisibleText); }); @@ -4550,6 +4611,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(translateLocal('report.noActivityYet')); }); @@ -4578,6 +4640,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); const transactions = getReportTransactions(report.reportID); const scanningTransactions = transactions.filter((transaction) => isScanning(transaction)); @@ -4615,6 +4678,7 @@ describe('OptionsListUtils', () => { translate: translateLocal, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(result).toBe(''); }); @@ -4665,6 +4729,7 @@ describe('OptionsListUtils', () => { isReportArchived: false, policy, reportMetadata, + chatReport: undefined, }); expect(lastMessage).toBe(translate(CONST.LOCALES.EN, 'iou.queuedToSubmitViaDEW')); }); @@ -4696,6 +4761,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(customErrorMessage); }); @@ -4724,6 +4790,7 @@ describe('OptionsListUtils', () => { report, lastActorDetails: null, isReportArchived: false, + chatReport: undefined, }); expect(lastMessage).toBe(translate(CONST.LOCALES.EN, 'iou.error.genericCreateFailureMessage')); }); @@ -4956,7 +5023,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined); + const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined, undefined); // Then it should return an option with isSelfDM and alternateText set expect(result.isSelfDM).toBe(true); @@ -4976,7 +5043,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined); + const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined, undefined); // Then it should return an option with invoice room text and alternateText expect(result.isInvoiceRoom).toBe(true); @@ -4998,7 +5065,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, unknownUserDetails, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined); + const result = getReportDisplayOption(report, unknownUserDetails, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined, undefined); // Then it should return an option with unknownUserDetails data expect(result.text).toBe('Unknown User'); @@ -5019,7 +5086,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined); + const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined, undefined); // Then it should return an option with workspace name expect(result.text).toBe(POLICY.name); @@ -5046,7 +5113,7 @@ describe('OptionsListUtils', () => { }; // When we call getReportDisplayOption with custom personalDetails - const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, customPersonalDetails, undefined); + const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, customPersonalDetails, undefined, undefined); // Then it should use the custom personalDetails parameter expect(result).toBeDefined(); @@ -5063,7 +5130,7 @@ describe('OptionsListUtils', () => { const emptyPersonalDetails: PersonalDetailsList = {}; // When we call getReportDisplayOption - const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, emptyPersonalDetails, undefined); + const result = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, emptyPersonalDetails, undefined, undefined); // Then it should not throw and return a valid option expect(result).toBeDefined(); @@ -5075,7 +5142,7 @@ describe('OptionsListUtils', () => { const personalDetails: PersonalDetailsList = PERSONAL_DETAILS; // When we call getReportDisplayOption with undefined report - const result = getReportDisplayOption(undefined, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined); + const result = getReportDisplayOption(undefined, undefined, CURRENT_USER_ACCOUNT_ID, personalDetails, undefined, undefined); // Then it should return a valid option (createOption handles undefined) expect(result).toBeDefined(); @@ -5456,7 +5523,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived); + const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -5479,7 +5546,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived); + const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -5499,7 +5566,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, undefined); + const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, undefined, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -5523,7 +5590,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived); + const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -5547,7 +5614,7 @@ describe('OptionsListUtils', () => { await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); await waitForBatchedUpdates(); - const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived); + const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, reportNameValuePair?.private_isArchived, undefined); expect(option).toBeDefined(); expect(option.reportID).toBe(reportID); @@ -5858,7 +5925,7 @@ describe('OptionsListUtils', () => { selected: true, }; - const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, testPersonalDetails); + const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, testPersonalDetails, report, undefined); expect(option).toBeDefined(); expect(option.text).toBe('Test Workspace Policy'); @@ -5919,7 +5986,7 @@ describe('OptionsListUtils', () => { isPolicyExpenseChat: true, }; - const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, testPersonalDetails); + const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, testPersonalDetails, report, undefined); expect(option).toBeDefined(); expect(option.text).toBe('Team Workspace'); @@ -5963,7 +6030,7 @@ describe('OptionsListUtils', () => { }; // Should not throw when personalDetails is empty - const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, {}); + const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, {}, report, undefined); expect(option).toBeDefined(); expect(option.text).toBe('Workspace Without Details'); @@ -6007,7 +6074,7 @@ describe('OptionsListUtils', () => { }; // Should not throw when personalDetails is undefined - const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, undefined); + const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, undefined, report, undefined); expect(option).toBeDefined(); expect(option.text).toBe('Workspace Undefined Details'); @@ -6059,10 +6126,10 @@ describe('OptionsListUtils', () => { selected: false, }; - const optionSelected = getPolicyExpenseReportOption(participantSelected, CURRENT_USER_ACCOUNT_ID, {}); + const optionSelected = getPolicyExpenseReportOption(participantSelected, CURRENT_USER_ACCOUNT_ID, {}, report, undefined); // eslint-disable-next-line rulesdir/no-negated-variables - const optionNotSelected = getPolicyExpenseReportOption(participantNotSelected, CURRENT_USER_ACCOUNT_ID, {}); + const optionNotSelected = getPolicyExpenseReportOption(participantNotSelected, CURRENT_USER_ACCOUNT_ID, {}, report, undefined); expect(optionSelected.isSelected).toBe(true); expect(optionSelected.selected).toBe(true); @@ -6097,6 +6164,290 @@ describe('OptionsListUtils', () => { }); }); + describe('getLastMessageTextForReport with chatReport parameter', () => { + it('should work correctly when chatReport is passed', async () => { + const chatReportID = '999'; + + const report: Report = { + ...createRandomReport(0, undefined), + chatReportID, + type: CONST.REPORT.TYPE.EXPENSE, + }; + + const chatReport: Report = { + ...createRandomReport(1, undefined), + reportID: chatReportID, + }; + + // Test that the function works without crashing when chatReport is passed + const result = getLastMessageTextForReport({ + translate: jest.fn().mockReturnValue(''), + report, + lastActorDetails: null, + isReportArchived: false, + chatReport, + }); + + // The function should return a string (may be empty string) + expect(typeof result).toBe('string'); + }); + + it('should work correctly when chatReport is undefined', async () => { + const report: Report = { + ...createRandomReport(0, undefined), + type: CONST.REPORT.TYPE.CHAT, + }; + + const result = getLastMessageTextForReport({ + translate: jest.fn().mockReturnValue(''), + report, + lastActorDetails: null, + isReportArchived: false, + chatReport: undefined, + }); + + expect(typeof result).toBe('string'); + }); + }); + + describe('reports parameter functionality', () => { + it('getValidOptions should use reports parameter to look up chat reports', () => { + // When we call getValidOptions with the reports collection + const results = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + allPolicies, + {}, + nvpDismissedProductTraining, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + ); + + // Then the function should complete without errors and return valid results + expect(results).toBeDefined(); + expect(results.recentReports).toBeDefined(); + expect(results.personalDetails).toBeDefined(); + }); + + it('filterAndOrderOptions should use reports parameter correctly', () => { + // Given a set of options and reports collection + const options = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + allPolicies, + {}, + nvpDismissedProductTraining, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + ); + + // When we call filterAndOrderOptions with the reports parameter + const filteredOptions = filterAndOrderOptions(options, 'spider', COUNTRY_CODE, loginList, CURRENT_USER_EMAIL, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS); + + // Then the function should complete without errors and return valid results + expect(filteredOptions).toBeDefined(); + expect(filteredOptions.recentReports).toBeDefined(); + expect(filteredOptions.personalDetails).toBeDefined(); + }); + + it('getSearchOptions should use reports parameter from config', () => { + // When we call getSearchOptions with reports in the config + const options = getSearchOptions({ + options: OPTIONS, + draftComments: {}, + nvpDismissedProductTraining, + loginList, + policyCollection: {}, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + currentUserEmail: CURRENT_USER_EMAIL, + personalDetails: PERSONAL_DETAILS, + }); + + // Then the function should complete without errors and return valid results + expect(options).toBeDefined(); + expect(options.recentReports).toBeDefined(); + expect(options.personalDetails).toBeDefined(); + }); + + it('getMemberInviteOptions should use reports parameter correctly', () => { + // When we call getMemberInviteOptions with the reports parameter + const results = getMemberInviteOptions( + OPTIONS.personalDetails, + nvpDismissedProductTraining, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + PERSONAL_DETAILS, + [], + {}, + false, + COUNTRY_CODE, + ); + + // Then the function should complete without errors and return valid results + expect(results).toBeDefined(); + expect(results.personalDetails).toBeDefined(); + expect(results.recentReports).toEqual([]); + }); + + it('getUserToInviteOption should use reports parameter correctly', () => { + // Given a valid email search value and reports collection + const result = getUserToInviteOption({ + searchValue: 'newuser@example.com', + loginList: {}, + currentUserAccountID: CURRENT_USER_ACCOUNT_ID, + currentUserEmail: CURRENT_USER_EMAIL, + personalDetails: PERSONAL_DETAILS, + }); + + // Then the function should return a user to invite + expect(result).not.toBeNull(); + expect(result?.login).toBe('newuser@example.com'); + }); + + it('should work correctly when reports is an empty object', () => { + // When we call getValidOptions with empty reports + const results = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + allPolicies, + {}, + nvpDismissedProductTraining, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + ); + + // Then the function should still work correctly + expect(results).toBeDefined(); + expect(results.recentReports).toBeDefined(); + expect(results.personalDetails).toBeDefined(); + }); + + it('should work correctly when reports is undefined', () => { + // When we call getValidOptions without reports parameter + const results = getValidOptions( + {reports: OPTIONS.reports, personalDetails: OPTIONS.personalDetails}, + allPolicies, + {}, + nvpDismissedProductTraining, + loginList, + CURRENT_USER_ACCOUNT_ID, + CURRENT_USER_EMAIL, + ); + + // Then the function should still work correctly + expect(results).toBeDefined(); + expect(results.recentReports).toBeDefined(); + expect(results.personalDetails).toBeDefined(); + }); + + it('createOption should look up chatReport from reports collection when report has chatReportID', async () => { + // This test verifies the core functionality: using reports to look up linked chat reports + const reportID = 'expense-report-123'; + const chatReportID = 'linked-chat-456'; + + const expenseReport: Report = { + ...createRandomReport(0, undefined), + reportID, + chatReportID, + type: CONST.REPORT.TYPE.EXPENSE, + participants: { + 1: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + 2: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + }, + }; + + const linkedChatReport: Report = { + ...createRandomReport(1, undefined), + reportID: chatReportID, + type: CONST.REPORT.TYPE.CHAT, + reportName: 'Linked Chat Report', + }; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, expenseReport); + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, linkedChatReport); + await waitForBatchedUpdates(); + + // When we call createOption with the linked chat report + const result = createOption([1, 2], PERSONAL_DETAILS, expenseReport, CURRENT_USER_ACCOUNT_ID, linkedChatReport); + + // Then the option should be created successfully + expect(result).toBeDefined(); + expect(result.reportID).toBe(reportID); + }); + + it('getReportDisplayOption should use reports parameter to look up chat report', async () => { + const reportID = 'test-report-789'; + const chatReportID = 'test-chat-101'; + + const report: Report = { + ...createRandomReport(0, undefined), + reportID, + chatReportID, + participants: { + 2: {notificationPreference: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS}, + }, + }; + + const chatReport: Report = { + ...createRandomReport(1, undefined), + reportID: chatReportID, + }; + + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); + await Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, chatReport); + await waitForBatchedUpdates(); + + // When we call getReportDisplayOption with chat report + const option = getReportDisplayOption(report, undefined, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, undefined, chatReport); + + // Then the option should be created successfully using the reports collection + expect(option).toBeDefined(); + expect(option.reportID).toBe(reportID); + }); + + it('getPolicyExpenseReportOption should use reports parameter correctly', async () => { + const reportID = 'policy-expense-123'; + const testPolicyID = 'test-policy-456'; + + const report: Report = { + reportID, + reportName: 'Test Policy Expense Chat', + type: CONST.REPORT.TYPE.CHAT, + chatType: CONST.REPORT.CHAT_TYPE.POLICY_EXPENSE_CHAT, + policyID: testPolicyID, + ownerAccountID: CURRENT_USER_ACCOUNT_ID, + }; + + const policy: Policy = { + id: testPolicyID, + name: 'Test Reports Param Workspace', + type: CONST.POLICY.TYPE.TEAM, + owner: 'owner@test.com', + role: 'admin', + outputCurrency: 'USD', + isPolicyExpenseChatEnabled: true, + }; + + await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, report); + await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${testPolicyID}`, policy); + await waitForBatchedUpdates(); + + const participant = { + reportID, + policyID: testPolicyID, + isPolicyExpenseChat: true, + }; + + // When we call getPolicyExpenseReportOption with report passed directly + const option = getPolicyExpenseReportOption(participant, CURRENT_USER_ACCOUNT_ID, PERSONAL_DETAILS, report, undefined); + + // Then the option should be created successfully + expect(option).toBeDefined(); + expect(option.text).toBe('Test Reports Param Workspace'); + }); + }); + describe('createOptionFromReport', () => { it('should create an option from a report with all required parameters', () => { const report: Report = { @@ -6109,7 +6460,7 @@ describe('OptionsListUtils', () => { }, }; - const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined); + const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined, undefined); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -6128,7 +6479,7 @@ describe('OptionsListUtils', () => { }; const privateIsArchived = DateUtils.getDBTime(); - const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, privateIsArchived); + const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined, privateIsArchived); expect(result).toBeDefined(); expect(result.private_isArchived).toBe(privateIsArchived); @@ -6145,7 +6496,7 @@ describe('OptionsListUtils', () => { }, }; - const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined); + const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined, undefined); expect(result).toBeDefined(); expect(result.private_isArchived).toBeUndefined(); @@ -6163,7 +6514,7 @@ describe('OptionsListUtils', () => { }; // Pass undefined for reportAttributesDerived - the function should handle it gracefully - const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined, undefined); + const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined, undefined, undefined); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); @@ -6181,7 +6532,7 @@ describe('OptionsListUtils', () => { }; const config = {showPersonalDetails: true}; - const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined, undefined, config); + const result = createOptionFromReport(report, PERSONAL_DETAILS, CURRENT_USER_ACCOUNT_ID, undefined, undefined, undefined, config); expect(result).toBeDefined(); expect(result.reportID).toBe('1'); diff --git a/tests/unit/SidebarUtilsTest.ts b/tests/unit/SidebarUtilsTest.ts index c5da1169a3bf..a5b424dbac22 100644 --- a/tests/unit/SidebarUtilsTest.ts +++ b/tests/unit/SidebarUtilsTest.ts @@ -356,6 +356,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); const optionDataUnpinned = SidebarUtils.getOptionData({ @@ -375,6 +376,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1343,6 +1345,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1408,6 +1411,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1448,6 +1452,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1487,6 +1492,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1526,6 +1532,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1593,6 +1600,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1646,6 +1654,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); expect(optionData?.alternateText).toBe(`test message`); @@ -1689,6 +1698,7 @@ describe('SidebarUtils', () => { isReportArchived: true, lastActionReport: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: undefined, }); @@ -1730,6 +1740,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); expect(optionData?.alternateText).toBe(`test message`); @@ -1869,6 +1880,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, reportAttributesDerived: mockReportAttributesDerived, }); @@ -1915,6 +1927,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); expect(optionData?.alternateText).toBe(`${policy.name} ${CONST.DOT_SEPARATOR} test message`); @@ -1989,6 +2002,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: session.accountID, + chatReport: undefined, }); // Then the alternate text should be equal to the message of the last action prepended with the last actor display name. @@ -2052,6 +2066,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: session.accountID, + chatReport: undefined, }); expect(result?.alternateText).toBe(`You: moved this report to the Three's Workspace workspace`); @@ -2106,6 +2121,7 @@ describe('SidebarUtils', () => { isReportArchived: undefined, lastMessageTextFromReport: report.lastMessageText, currentUserAccountID: session.accountID, + chatReport: undefined, }); expect(result?.alternateText).toBe('You: someMessage'); @@ -2185,6 +2201,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: session.accountID, + chatReport: undefined, }); expect(result?.alternateText).toBe(`You: ${getReportActionMessageText(lastAction)}`); @@ -2306,6 +2323,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); expect(result?.alternateText).toContain(`${getReportActionMessageText(lastAction)}`); @@ -2394,6 +2412,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); expect(result?.alternateText).toBe(`One: submitted`); @@ -2494,6 +2513,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: managerID, + chatReport: undefined, }); const reportPreviewMessage = getReportPreviewMessage(iouReport, iouAction, true, true, null, true, lastReportPreviewAction); @@ -2595,6 +2615,7 @@ describe('SidebarUtils', () => { lastActionReport: undefined, isReportArchived: undefined, currentUserAccountID: managerID, + chatReport: undefined, }); const reportPreviewMessage = getReportPreviewMessage(iouReport, iouAction, true, true, null, true, lastReportPreviewAction); @@ -2628,6 +2649,7 @@ describe('SidebarUtils', () => { invoiceReceiverPolicy: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); // Then isConciergeChat should be true @@ -2659,6 +2681,7 @@ describe('SidebarUtils', () => { invoiceReceiverPolicy: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); // Then isConciergeChat should be false @@ -2689,6 +2712,7 @@ describe('SidebarUtils', () => { invoiceReceiverPolicy: undefined, isReportArchived: undefined, currentUserAccountID: 0, + chatReport: undefined, }); // Then isConciergeChat should be false