From 96037d47919383528135dc05f0d89b6b72fe016f Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 9 Jan 2025 17:15:20 +0100 Subject: [PATCH 1/6] adjust create expense flow --- .../MoneyRequestConfirmationList.tsx | 11 ++- src/languages/en.ts | 1 + src/languages/es.ts | 1 + src/libs/OptionsListUtils.ts | 98 ++++++++++++++++++- .../MoneyRequestParticipantsSelector.tsx | 72 +++++++------- .../step/IOURequestStepParticipants.tsx | 41 ++++---- 6 files changed, 160 insertions(+), 64 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index c470f0c50806..d4a5af3fb5cf 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -429,16 +429,19 @@ function MoneyRequestConfirmationList({ text = translate('common.next'); } } else if (isTypeTrackExpense) { - text = translate('iou.trackExpense'); + text = translate('iou.createExpense'); + if (iouAmount !== 0) { + text = translate('iou.createExpenseWithAmount', {amount: formattedAmount}); + } } else if (isTypeSplit && iouAmount === 0) { text = translate('iou.splitExpense'); } else if ((receiptPath && isTypeRequest) || isDistanceRequestWithPendingRoute || isPerDiemRequest) { - text = translate('iou.submitExpense'); + text = translate('iou.createExpense'); if (iouAmount !== 0) { - text = translate('iou.submitAmount', {amount: formattedAmount}); + text = translate('iou.createExpenseWithAmount', {amount: formattedAmount}); } } else { - const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.submitAmount'; + const translationKey = isTypeSplit ? 'iou.splitAmount' : 'iou.createExpenseWithAmount'; text = translate(translationKey, {amount: formattedAmount}); } return [ diff --git a/src/languages/en.ts b/src/languages/en.ts index 1ff4ef4c0ae4..6480b7e779ea 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -870,6 +870,7 @@ const translations = { createExpense: 'Create expense', trackExpense: 'Track expense', chooseRecipient: 'Choose recipient', + createExpenseWithAmount: ({amount}: {amount: string}) => `Create ${amount} expense`, confirmDetails: 'Confirm details', pay: 'Pay', cancelPayment: 'Cancel payment', diff --git a/src/languages/es.ts b/src/languages/es.ts index 92e94446aa48..b53787bfea3d 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -865,6 +865,7 @@ const translations = { paySomeone: ({name}: PaySomeoneParams = {}) => `Pagar a ${name ?? 'alguien'}`, trackExpense: 'Seguimiento de gastos', chooseRecipient: 'Elige destinatario', + createExpenseWithAmount: ({amount}: {amount: string}) => `Crear un gasto de ${amount}`, confirmDetails: 'Confirma los detalles', pay: 'Pagar', cancelPayment: 'Cancelar el pago', diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 4d1988a53bde..fa26294aea6a 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -108,6 +108,8 @@ type GetOptionsConfig = { action?: IOUAction; recentAttendees?: Attendee[]; shouldBoldTitleByDefault?: boolean; + shouldSeparateWorkspaceChat?: boolean; + shouldSeparateSelfDMChat?: boolean; }; type GetUserToInviteConfig = { @@ -138,6 +140,8 @@ type Options = { personalDetails: ReportUtils.OptionData[]; userToInvite: ReportUtils.OptionData | null; currentUserOption: ReportUtils.OptionData | null | undefined; + workspaceChats?: ReportUtils.OptionData[]; + selfDMChat?: ReportUtils.OptionData | undefined; }; type PreviewConfig = {showChatPreviewLine?: boolean; forcePolicyNamePreview?: boolean; showPersonalDetails?: boolean}; @@ -165,7 +169,7 @@ type OrderReportOptionsConfig = { preferRecentExpenseReports?: boolean; }; -type ReportAndPersonalDetailOptions = Pick; +type ReportAndPersonalDetailOptions = Pick; /** * OptionsListUtils is used to build a list options passed to the OptionsList component. Several different UI views can @@ -1009,6 +1013,23 @@ function orderReportOptionsWithSearch( ); } +function orderWorkspaceOptions(options: ReportUtils.OptionData[]): ReportUtils.OptionData[] { + return lodashOrderBy( + options, + [ + (option) => { + // Put default workspace on top + if (option.isPolicyExpenseChat && option.policyID === activePolicyID) { + return 0; + } + + return 1; + }, + ], + ['asc'], + ); +} + function sortComparatorReportOptionByArchivedStatus(option: ReportUtils.OptionData) { return option.private_isArchived ? 1 : 0; } @@ -1036,10 +1057,12 @@ function orderOptions(options: ReportAndPersonalDetailOptions, searchValue?: str orderedReportOptions = orderReportOptions(options.recentReports); } const orderedPersonalDetailsOptions = orderPersonalDetailsOptions(options.personalDetails); + const orderedWorkspaceChats = orderWorkspaceOptions(options?.workspaceChats ?? []); return { recentReports: orderedReportOptions, personalDetails: orderedPersonalDetailsOptions, + workspaceChats: orderedWorkspaceChats, }; } @@ -1148,6 +1171,8 @@ function getValidOptions( action, recentAttendees, shouldBoldTitleByDefault = true, + shouldSeparateSelfDMChat = false, + shouldSeparateWorkspaceChat = false, }: GetOptionsConfig = {}, ): Options { const topmostReportId = Navigation.getTopmostReportId(); @@ -1223,6 +1248,12 @@ function getValidOptions( return true; }); + let workspaceChats: ReportUtils.OptionData[] = []; + + if (shouldSeparateWorkspaceChat) { + workspaceChats = allReportOptions.filter((option) => option.isOwnPolicyExpenseChat && !option.private_isArchived); + } + const allPersonalDetailsOptions = includeP2P ? options.personalDetails.filter((detail) => !!detail?.login && !!detail.accountID && !detail?.isOptimisticPersonalDetail && (includeDomainEmail || !Str.isDomainEmail(detail.login))) : []; @@ -1341,6 +1372,15 @@ function getValidOptions( } const currentUserOption = allPersonalDetailsOptions.find((personalDetailsOption) => personalDetailsOption.login === currentUserLogin); + let selfDMChat: ReportUtils.OptionData | undefined; + + if (shouldSeparateWorkspaceChat) { + recentReportOptions = recentReportOptions.filter((option) => !option.isPolicyExpenseChat); + } + if (shouldSeparateSelfDMChat) { + selfDMChat = recentReportOptions.find((option) => option.isSelfDM); + recentReportOptions = recentReportOptions.filter((option) => !option.isSelfDM); + } return { personalDetails: personalDetailsOptions, @@ -1349,6 +1389,8 @@ function getValidOptions( // User to invite is generated by the search input of a user. // As this function isn't concerned with any search input yet, this is null (will be set when using filterOptions). userToInvite: null, + workspaceChats, + selfDMChat, }; } @@ -1588,6 +1630,7 @@ function formatSectionsFromSearchTerm( filteredPersonalDetails: ReportUtils.OptionData[], personalDetails: OnyxEntry = {}, shouldGetOptionDetails = false, + filteredWorkspaceChats: ReportUtils.OptionData[] = [], ): SectionForSearchTerm { // We show the selected participants at the top of the list when there is no search term or maximum number of participants has already been selected // However, if there is a search term we remove the selected participants from the top of the list unless they are part of the search results @@ -1613,8 +1656,9 @@ function formatSectionsFromSearchTerm( const selectedParticipantsWithoutDetails = selectedOptions.filter((participant) => { const accountID = participant.accountID ?? null; const isPartOfSearchTerm = getPersonalDetailSearchTerms(participant).join(' ').toLowerCase().includes(cleanSearchTerm); - const isReportInRecentReports = filteredRecentReports.some((report) => report.accountID === accountID); + const isReportInRecentReports = filteredRecentReports.some((report) => report.accountID === accountID) || filteredWorkspaceChats.some((report) => report.accountID === accountID); const isReportInPersonalDetails = filteredPersonalDetails.some((personalDetail) => personalDetail.accountID === accountID); + return isPartOfSearchTerm && !isReportInRecentReports && !isReportInPersonalDetails; }); @@ -1700,6 +1744,23 @@ function filterReports(reports: ReportUtils.OptionData[], searchTerms: string[]) return filteredReports; } +function filterWorkspaceChats(reports: ReportUtils.OptionData[], searchTerms: string[]): ReportUtils.OptionData[] { + const filteredReports = searchTerms.reduceRight( + (items, term) => + filterArrayByMatch(items, term, (item) => { + const values: string[] = []; + if (item.text) { + values.push(item.text); + } + return uniqFast(values); + }), + // We start from all unfiltered reports: + reports, + ); + + return filteredReports; +} + function filterPersonalDetails(personalDetails: ReportUtils.OptionData[], searchTerms: string[]): ReportUtils.OptionData[] { return searchTerms.reduceRight( (items, term) => @@ -1751,6 +1812,34 @@ function filterUserToInvite(options: Omit, searchValue: }); } +function filterSelfDMChat(report: ReportUtils.OptionData, searchTerms: string[]): ReportUtils.OptionData | undefined { + const isMatch = searchTerms.every((term) => { + const values: string[] = []; + + if (report.text) { + values.push(report.text); + } + if (report.login) { + values.push(report.login); + values.push(report.login.replace(CONST.EMAIL_SEARCH_REGEX, '')); + } + if (report.isThread) { + if (report.alternateText) { + values.push(report.alternateText); + } + } else if (!!report.isChatRoom || !!report.isPolicyExpenseChat) { + if (report.subtitle) { + values.push(report.subtitle); + } + } + + // Remove duplicate values and check if the term matches any value + return uniqFast(values).some((value) => value.includes(term)); + }); + + return isMatch ? report : undefined; +} + function filterOptions(options: Options, searchInputValue: string, config?: FilterUserToInviteConfig): Options { const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue))); const searchValue = parsedPhoneNumber.possible && parsedPhoneNumber.number?.e164 ? parsedPhoneNumber.number.e164 : searchInputValue.toLowerCase(); @@ -1768,12 +1857,17 @@ function filterOptions(options: Options, searchInputValue: string, config?: Filt searchValue, config, ); + const workspaceChats = filterWorkspaceChats(options.workspaceChats ?? [], searchTerms); + + const selfDMChat = options.selfDMChat ? filterSelfDMChat(options.selfDMChat, searchTerms) : undefined; return { personalDetails, recentReports, userToInvite, currentUserOption, + workspaceChats, + selfDMChat, }; } diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index 07e34d9692b1..c5232901be57 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -7,8 +7,6 @@ import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import EmptySelectionListContent from '@components/EmptySelectionListContent'; import FormHelpMessage from '@components/FormHelpMessage'; -import * as Expensicons from '@components/Icon/Expensicons'; -import MenuItem from '@components/MenuItem'; import {usePersonalDetails} from '@components/OnyxProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; import ReferralProgramCTA from '@components/ReferralProgramCTA'; @@ -34,6 +32,7 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {Participant} from '@src/types/onyx/IOU'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; type MoneyRequestParticipantsSelectorProps = { /** Callback to request parent modal to go to next step, which should be split */ @@ -42,9 +41,6 @@ type MoneyRequestParticipantsSelectorProps = { /** Callback to add participants in MoneyRequestModal */ onParticipantsAdded: (value: Participant[]) => void; - /** Callback to navigate to Track Expense confirmation flow */ - onTrackExpensePress?: () => void; - /** Selected participants from MoneyRequestModal with login */ participants?: Participant[] | typeof CONST.EMPTY_ARRAY; @@ -53,20 +49,9 @@ type MoneyRequestParticipantsSelectorProps = { /** The action of the IOU, i.e. create, split, move */ action: IOUAction; - - /** Whether we should display the Track Expense button at the top of the participants list */ - shouldDisplayTrackExpenseButton?: boolean; }; -function MoneyRequestParticipantsSelector({ - participants = CONST.EMPTY_ARRAY, - onTrackExpensePress, - onFinish, - onParticipantsAdded, - iouType, - action, - shouldDisplayTrackExpenseButton, -}: MoneyRequestParticipantsSelectorProps) { +function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onFinish, onParticipantsAdded, iouType, action}: MoneyRequestParticipantsSelectorProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); @@ -123,6 +108,9 @@ function MoneyRequestParticipantsSelector({ includeP2P: !isCategorizeOrShareAction, includeInvoiceRooms: iouType === CONST.IOU.TYPE.INVOICE, action, + shouldSeparateSelfDMChat: true, + shouldSeparateWorkspaceChat: true, + includeSelfDM: true, }, ); @@ -142,6 +130,8 @@ function MoneyRequestParticipantsSelector({ personalDetails: [], currentUserOption: null, headerMessage: '', + workspaceChats: [], + selfDMChat: null, }; } @@ -168,7 +158,7 @@ function MoneyRequestParticipantsSelector({ const formatResults = OptionsListUtils.formatSectionsFromSearchTerm( debouncedSearchTerm, - participants.map((participant) => ({...participant, reportID: participant.reportID ?? '-1'})), + participants.map((participant) => ({...participant, reportID: participant.reportID})) as ReportUtils.OptionData[], chatOptions.recentReports, chatOptions.personalDetails, personalDetails, @@ -177,6 +167,18 @@ function MoneyRequestParticipantsSelector({ newSections.push(formatResults.section); + newSections.push({ + title: translate('workspace.common.workspace'), + data: chatOptions.workspaceChats ?? [], + shouldShow: (chatOptions.workspaceChats ?? []).length > 0, + }); + + newSections.push({ + title: translate('workspace.invoices.paymentMethods.personal'), + data: chatOptions.selfDMChat ? [chatOptions.selfDMChat] : [], + shouldShow: !!chatOptions.selfDMChat, + }); + newSections.push({ title: translate('common.recents'), data: chatOptions.recentReports, @@ -191,7 +193,11 @@ function MoneyRequestParticipantsSelector({ if ( chatOptions.userToInvite && - !OptionsListUtils.isCurrentUser({...chatOptions.userToInvite, accountID: chatOptions.userToInvite?.accountID ?? -1, status: chatOptions.userToInvite?.status ?? undefined}) + !OptionsListUtils.isCurrentUser({ + ...chatOptions.userToInvite, + accountID: chatOptions.userToInvite?.accountID ?? CONST.DEFAULT_NUMBER_ID, + status: chatOptions.userToInvite?.status ?? undefined, + }) ) { newSections.push({ title: undefined, @@ -204,7 +210,7 @@ function MoneyRequestParticipantsSelector({ } const headerMessage = OptionsListUtils.getHeaderMessage( - (chatOptions.personalDetails ?? []).length + (chatOptions.recentReports ?? []).length !== 0, + (chatOptions.personalDetails ?? []).length + (chatOptions.recentReports ?? []).length + (chatOptions.workspaceChats ?? []).length !== 0 || !isEmptyObject(chatOptions.selfDMChat), !!chatOptions?.userToInvite, debouncedSearchTerm.trim(), participants.some((participant) => OptionsListUtils.getPersonalDetailSearchTerms(participant).join(' ').toLowerCase().includes(cleanSearchTerm)), @@ -218,6 +224,8 @@ function MoneyRequestParticipantsSelector({ participants, chatOptions.recentReports, chatOptions.personalDetails, + chatOptions.selfDMChat, + chatOptions.workspaceChats, chatOptions.userToInvite, personalDetails, translate, @@ -233,7 +241,7 @@ function MoneyRequestParticipantsSelector({ (option: Participant) => { const newParticipants: Participant[] = [ { - ...lodashPick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText', 'policyID'), + ...lodashPick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText', 'policyID', 'isSelfDM', 'text', 'phoneNumber'), selected: true, iouType, }, @@ -250,7 +258,10 @@ function MoneyRequestParticipantsSelector({ } onParticipantsAdded(newParticipants); - onFinish(); + + if (!option.isSelfDM) { + onFinish(); + } }, // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- we don't want to trigger this callback when iouType changes [onFinish, onParticipantsAdded, currentUserLogin], @@ -343,22 +354,6 @@ function MoneyRequestParticipantsSelector({ const shouldShowReferralBanner = !isDismissed && iouType !== CONST.IOU.TYPE.INVOICE && !shouldShowListEmptyContent; - const headerContent = useMemo(() => { - if (!shouldDisplayTrackExpenseButton) { - return; - } - - // We only display the track expense button if the user is coming from the combined submit/track flow. - return ( - - ); - }, [shouldDisplayTrackExpenseButton, translate, onTrackExpensePress]); - const footerContent = useMemo(() => { if (isDismissed && !shouldShowSplitBillErrorMessage && !participants.length) { return; @@ -444,7 +439,6 @@ function MoneyRequestParticipantsSelector({ shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} onSelectRow={onSelectRow} shouldSingleExecuteRowSelect - headerContent={headerContent} footerContent={footerContent} listEmptyContent={} headerMessage={header} diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index 6a67a1040f1b..df2f39158056 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -69,7 +69,6 @@ function IOURequestStepParticipants({ const selfDMReportID = useMemo(() => ReportUtils.findSelfDMReportID(), []); const [selfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`); - const shouldDisplayTrackExpenseButton = !!selfDMReportID && iouType === CONST.IOU.TYPE.CREATE; const receiptFilename = transaction?.filename; const receiptPath = transaction?.receipt?.source; @@ -88,10 +87,31 @@ function IOURequestStepParticipants({ IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename ?? '', receiptPath ?? '', () => {}, iouRequestType, iouType, transactionID, reportID, receiptType ?? ''); }, [receiptType, receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID, action]); + const trackExpense = useCallback(() => { + // If coming from the combined submit/track flow and the user proceeds to just track the expense, + // we will use the track IOU type in the confirmation flow. + if (!selfDMReportID) { + return; + } + + const rateID = DistanceRequestUtils.getCustomUnitRateID(selfDMReportID); + IOU.setCustomUnitRateID(transactionID, rateID); + IOU.setMoneyRequestParticipantsFromReport(transactionID, selfDMReport); + const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, CONST.IOU.TYPE.TRACK, transactionID, selfDMReportID); + Navigation.navigate(iouConfirmationPageRoute); + }, [action, selfDMReport, selfDMReportID, transactionID]); + const addParticipant = useCallback( (val: Participant[]) => { HttpUtils.cancelPendingRequests(READ_COMMANDS.SEARCH_FOR_REPORTS); + const firstParticipant = val.at(0); + + if (firstParticipant?.isSelfDM) { + trackExpense(); + return; + } + const firstParticipantReportID = val.at(0)?.reportID; const rateID = DistanceRequestUtils.getCustomUnitRateID(firstParticipantReportID); const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoomWithID(firstParticipantReportID); @@ -110,7 +130,7 @@ function IOURequestStepParticipants({ // When a participant is selected, the reportID needs to be saved because that's the reportID that will be used in the confirmation step. selectedReportID.current = firstParticipantReportID ?? reportID; }, - [iouType, reportID, transactionID], + [iouType, reportID, trackExpense, transactionID], ); const handleNavigation = useCallback( @@ -163,21 +183,6 @@ function IOURequestStepParticipants({ IOUUtils.navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID, action); }, [iouRequestType, iouType, transactionID, reportID, action]); - const trackExpense = () => { - // If coming from the combined submit/track flow and the user proceeds to just track the expense, - // we will use the track IOU type in the confirmation flow. - if (!selfDMReportID) { - return; - } - - const rateID = DistanceRequestUtils.getCustomUnitRateID(selfDMReportID); - IOU.setCustomUnitRateID(transactionID, rateID); - IOU.setMoneyRequestParticipantsFromReport(transactionID, selfDMReport); - const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, CONST.IOU.TYPE.TRACK, transactionID, selfDMReportID); - - handleNavigation(iouConfirmationPageRoute); - }; - useEffect(() => { const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; const isShareAction = action === CONST.IOU.ACTION.SHARE; @@ -206,10 +211,8 @@ function IOURequestStepParticipants({ participants={isSplitRequest ? participants : []} onParticipantsAdded={addParticipant} onFinish={goToNextStep} - onTrackExpensePress={trackExpense} iouType={iouType} action={action} - shouldDisplayTrackExpenseButton={shouldDisplayTrackExpenseButton} /> ); From 8b79cccda33b9f8207740977ec0fd182f8f4c425 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Fri, 10 Jan 2025 09:22:54 +0100 Subject: [PATCH 2/6] prevent users from sending invoices to themselves --- src/pages/iou/request/MoneyRequestParticipantsSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index c5232901be57..feefb3f96ecb 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -108,7 +108,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF includeP2P: !isCategorizeOrShareAction, includeInvoiceRooms: iouType === CONST.IOU.TYPE.INVOICE, action, - shouldSeparateSelfDMChat: true, + shouldSeparateSelfDMChat: iouType !== CONST.IOU.TYPE.INVOICE, shouldSeparateWorkspaceChat: true, includeSelfDM: true, }, From c32c9086d19a0c19387c808b2653c1350ac13061 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 16 Jan 2025 10:59:53 +0100 Subject: [PATCH 3/6] remove unused translations --- src/languages/en.ts | 1 - src/languages/es.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 6480b7e779ea..b24f633d583b 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1081,7 +1081,6 @@ const translations = { bookingArchivedDescription: 'This booking is archived because the trip date has passed. Add an expense for the final amount if needed.', attendees: 'Attendees', paymentComplete: 'Payment complete', - justTrackIt: 'Just track it (don’t submit it)', time: 'Time', startDate: 'Start date', endDate: 'End date', diff --git a/src/languages/es.ts b/src/languages/es.ts index b53787bfea3d..2e67befd6974 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1079,7 +1079,6 @@ const translations = { bookingArchivedDescription: 'Esta reserva está archivada porque la fecha del viaje ha pasado. Agregue un gasto por el monto final si es necesario.', attendees: 'Asistentes', paymentComplete: 'Pago completo', - justTrackIt: 'Solo guardarlo (no enviarlo)', time: 'Tiempo', startDate: 'Fecha de inicio', endDate: 'Fecha de finalización', From 73d16881db85eeb251641d405bbf7ceb40e9a970 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 16 Jan 2025 13:35:28 +0100 Subject: [PATCH 4/6] use native .sort method to sort workspaces --- src/libs/OptionsListUtils.ts | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index fa26294aea6a..d1033c3f3f39 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -1014,20 +1014,19 @@ function orderReportOptionsWithSearch( } function orderWorkspaceOptions(options: ReportUtils.OptionData[]): ReportUtils.OptionData[] { - return lodashOrderBy( - options, - [ - (option) => { - // Put default workspace on top - if (option.isPolicyExpenseChat && option.policyID === activePolicyID) { - return 0; - } + return options.sort((a, b) => { + // Check if `a` is the default workspace + if (a.isPolicyExpenseChat && a.policyID === activePolicyID) { + return -1; + } - return 1; - }, - ], - ['asc'], - ); + // Check if `b` is the default workspace + if (b.isPolicyExpenseChat && b.policyID === activePolicyID) { + return 1; + } + + return 0; + }); } function sortComparatorReportOptionByArchivedStatus(option: ReportUtils.OptionData) { From e5b4b71e8e5022a6d2c2da3500b0ba3bb2a2ec27 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Thu, 16 Jan 2025 16:17:48 +0100 Subject: [PATCH 5/6] fix lint errors --- .../MoneyRequestConfirmationList.tsx | 142 ++--- src/libs/OptionsListUtils.ts | 506 ++++++++++-------- .../MoneyRequestParticipantsSelector.tsx | 56 +- .../step/IOURequestStepParticipants.tsx | 50 +- 4 files changed, 435 insertions(+), 319 deletions(-) diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx index 29d9fe3c6449..36b11ff58b40 100755 --- a/src/components/MoneyRequestConfirmationList.tsx +++ b/src/components/MoneyRequestConfirmationList.tsx @@ -11,19 +11,44 @@ import {MouseProvider} from '@hooks/useMouseContext'; import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import blurActiveElement from '@libs/Accessibility/blurActiveElement'; -import * as CurrencyUtils from '@libs/CurrencyUtils'; +import { + adjustRemainingSplitShares, + resetSplitShares, + setCustomUnitRateID, + setIndividualShare, + setMoneyRequestAmount, + setMoneyRequestCategory, + setMoneyRequestMerchant, + setMoneyRequestPendingFields, + setMoneyRequestTag, + setMoneyRequestTaxAmount, + setMoneyRequestTaxRate, + setSplitShares, +} from '@libs/actions/IOU'; +import {convertToBackendAmount, convertToDisplayString, convertToDisplayStringWithoutCurrency, getCurrencyDecimals} from '@libs/CurrencyUtils'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; -import * as IOUUtils from '@libs/IOUUtils'; +import {calculateAmount, insertTagIntoTransactionTagsString, isMovingTransactionFromTrackExpense as isMovingTransactionFromTrackExpenseUtil} from '@libs/IOUUtils'; import Log from '@libs/Log'; -import * as MoneyRequestUtils from '@libs/MoneyRequestUtils'; +import {validateAmount} from '@libs/MoneyRequestUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import {getDistanceRateCustomUnitRate, isTaxTrackingEnabled} from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; +import {getIOUConfirmationOptionsFromPayeePersonalDetail, hasEnabledOptions} from '@libs/OptionsListUtils'; +import {getDistanceRateCustomUnitRate, getTagLists, isTaxTrackingEnabled} from '@libs/PolicyUtils'; +import {isDraftReport, isOptimisticPersonalDetail} from '@libs/ReportUtils'; +import type {OptionData} from '@libs/ReportUtils'; import playSound, {SOUNDS} from '@libs/Sound'; -import * as TransactionUtils from '@libs/TransactionUtils'; -import * as IOU from '@userActions/IOU'; +import { + areRequiredFieldsEmpty, + calculateTaxAmount, + getDefaultTaxCode, + getDistanceInMeters, + getRateID, + getTag, + getTaxValue, + hasMissingSmartscanFields, + hasRoute as hasRouteUtil, + isMerchantMissing, + isScanRequest as isScanRequestUtil, +} from '@libs/TransactionUtils'; import {hasInvoicingDetails} from '@userActions/Policy/Policy'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; @@ -145,7 +170,7 @@ type MoneyRequestConfirmationListProps = { isConfirmed?: boolean; }; -type MoneyRequestConfirmationListItem = Participant | ReportUtils.OptionData; +type MoneyRequestConfirmationListItem = Participant | OptionData; function MoneyRequestConfirmationList({ transaction, @@ -204,10 +229,10 @@ function MoneyRequestConfirmationList({ const isTypeSend = iouType === CONST.IOU.TYPE.PAY; const isTypeTrackExpense = iouType === CONST.IOU.TYPE.TRACK; const isTypeInvoice = iouType === CONST.IOU.TYPE.INVOICE; - const isScanRequest = useMemo(() => TransactionUtils.isScanRequest(transaction), [transaction]); + const isScanRequest = useMemo(() => isScanRequestUtil(transaction), [transaction]); const transactionID = transaction?.transactionID; - const customUnitRateID = TransactionUtils.getRateID(transaction); + const customUnitRateID = getRateID(transaction); useEffect(() => { if (customUnitRateID !== '-1' || !isDistanceRequest || !transactionID || !policy?.id) { @@ -222,7 +247,7 @@ function MoneyRequestConfirmationList({ return; } - IOU.setCustomUnitRateID(transactionID, rateID); + setCustomUnitRateID(transactionID, rateID); }, [defaultMileageRate, customUnitRateID, lastSelectedDistanceRates, policy?.id, transactionID, isDistanceRequest]); const mileageRate = DistanceRequestUtils.getRate({transaction, policy, policyDraft}); @@ -233,11 +258,11 @@ function MoneyRequestConfirmationList({ const prevCurrency = usePrevious(currency); // A flag for showing the categories field - const shouldShowCategories = (isPolicyExpenseChat || isTypeInvoice) && (!!iouCategory || OptionsListUtils.hasEnabledOptions(Object.values(policyCategories ?? {}))); + const shouldShowCategories = (isPolicyExpenseChat || isTypeInvoice) && (!!iouCategory || hasEnabledOptions(Object.values(policyCategories ?? {}))); const shouldShowMerchant = shouldShowSmartScanFields && !isDistanceRequest && !isTypeSend && !isPerDiemRequest; - const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); + const policyTagLists = useMemo(() => getTagLists(policyTags), [policyTags]); const shouldShowTax = isTaxTrackingEnabled(isPolicyExpenseChat, policy, isDistanceRequest) && !isPerDiemRequest; @@ -259,26 +284,26 @@ function MoneyRequestConfirmationList({ ) { return; } - const defaultTaxCode = TransactionUtils.getDefaultTaxCode(policy, transaction); - IOU.setMoneyRequestTaxRate(transactionID, defaultTaxCode ?? ''); + const defaultTaxCode = getDefaultTaxCode(policy, transaction); + setMoneyRequestTaxRate(transactionID, defaultTaxCode ?? ''); }, [customUnitRateID, policy, previousCustomUnitRateID, previousTransactionCurrency, previousTransactionModifiedCurrency, shouldShowTax, transaction, transactionID]); - const isMovingTransactionFromTrackExpense = IOUUtils.isMovingTransactionFromTrackExpense(action); + const isMovingTransactionFromTrackExpense = isMovingTransactionFromTrackExpenseUtil(action); - const distance = TransactionUtils.getDistanceInMeters(transaction, unit); + const distance = getDistanceInMeters(transaction, unit); const prevDistance = usePrevious(distance); const shouldCalculateDistanceAmount = isDistanceRequest && (iouAmount === 0 || prevRate !== rate || prevDistance !== distance || prevCurrency !== currency); - const hasRoute = TransactionUtils.hasRoute(transaction, isDistanceRequest); + const hasRoute = hasRouteUtil(transaction, isDistanceRequest); const isDistanceRequestWithPendingRoute = isDistanceRequest && (!hasRoute || !rate) && !isMovingTransactionFromTrackExpense; const distanceRequestAmount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate ?? 0); const formattedAmount = isDistanceRequestWithPendingRoute ? '' - : CurrencyUtils.convertToDisplayString(shouldCalculateDistanceAmount ? distanceRequestAmount : iouAmount, isDistanceRequest ? currency : iouCurrencyCode); + : convertToDisplayString(shouldCalculateDistanceAmount ? distanceRequestAmount : iouAmount, isDistanceRequest ? currency : iouCurrencyCode); const formattedAmountPerAttendee = isDistanceRequestWithPendingRoute ? '' - : CurrencyUtils.convertToDisplayString( + : convertToDisplayString( (shouldCalculateDistanceAmount ? distanceRequestAmount : iouAmount) / (iouAttendees?.length && iouAttendees.length > 0 ? iouAttendees.length : 1), isDistanceRequest ? currency : iouCurrencyCode, ); @@ -293,25 +318,20 @@ function MoneyRequestConfirmationList({ return false; } - return (!!hasSmartScanFailed && TransactionUtils.hasMissingSmartscanFields(transaction)) || (didConfirmSplit && TransactionUtils.areRequiredFieldsEmpty(transaction)); + return (!!hasSmartScanFailed && hasMissingSmartscanFields(transaction)) || (didConfirmSplit && areRequiredFieldsEmpty(transaction)); }, [isEditingSplitBill, hasSmartScanFailed, transaction, didConfirmSplit]); - const isMerchantEmpty = useMemo(() => !iouMerchant || TransactionUtils.isMerchantMissing(transaction), [transaction, iouMerchant]); + const isMerchantEmpty = useMemo(() => !iouMerchant || isMerchantMissing(transaction), [transaction, iouMerchant]); const isMerchantRequired = isPolicyExpenseChat && (!isScanRequest || isEditingSplitBill) && shouldShowMerchant; const isCategoryRequired = !!policy?.requiresCategory; const shouldDisableParticipant = (participant: Participant): boolean => { - if (ReportUtils.isDraftReport(participant.reportID)) { + if (isDraftReport(participant.reportID)) { return true; } - if ( - !participant.isInvoiceRoom && - !participant.isPolicyExpenseChat && - !participant.isSelfDM && - ReportUtils.isOptimisticPersonalDetail(participant.accountID ?? CONST.DEFAULT_NUMBER_ID) - ) { + if (!participant.isInvoiceRoom && !participant.isPolicyExpenseChat && !participant.isSelfDM && isOptimisticPersonalDetail(participant.accountID ?? CONST.DEFAULT_NUMBER_ID)) { return true; } @@ -344,7 +364,7 @@ function MoneyRequestConfirmationList({ return; } const amount = DistanceRequestUtils.getDistanceRequestAmount(distance, unit ?? CONST.CUSTOM_UNITS.DISTANCE_UNIT_MILES, rate ?? 0); - IOU.setMoneyRequestAmount(transactionID, amount, currency ?? ''); + setMoneyRequestAmount(transactionID, amount, currency ?? ''); isFirstUpdatedDistanceAmount.current = true; }, [distance, rate, unit, transactionID, currency, isDistanceRequest]); @@ -354,12 +374,12 @@ function MoneyRequestConfirmationList({ } const amount = distanceRequestAmount; - IOU.setMoneyRequestAmount(transactionID, amount, currency ?? ''); + setMoneyRequestAmount(transactionID, amount, currency ?? ''); // If it's a split request among individuals, set the split shares const participantAccountIDs: number[] = selectedParticipantsProp.map((participant) => participant.accountID ?? CONST.DEFAULT_NUMBER_ID); if (isTypeSplit && !isPolicyExpenseChat && amount && transaction?.currency) { - IOU.setSplitShares(transaction, amount, currency, participantAccountIDs); + setSplitShares(transaction, amount, currency, participantAccountIDs); } }, [shouldCalculateDistanceAmount, distanceRequestAmount, transactionID, currency, isTypeSplit, isPolicyExpenseChat, selectedParticipantsProp, transaction]); @@ -389,14 +409,14 @@ function MoneyRequestConfirmationList({ } } else { taxableAmount = transaction.amount ?? 0; - taxCode = transaction.taxCode ?? TransactionUtils.getDefaultTaxCode(policy, transaction) ?? ''; + taxCode = transaction.taxCode ?? getDefaultTaxCode(policy, transaction) ?? ''; } if (taxCode && taxableAmount) { - const taxPercentage = TransactionUtils.getTaxValue(policy, transaction, taxCode) ?? ''; - const taxAmount = TransactionUtils.calculateTaxAmount(taxPercentage, taxableAmount, transaction.currency); - const taxAmountInSmallestCurrencyUnits = CurrencyUtils.convertToBackendAmount(Number.parseFloat(taxAmount.toString())); - IOU.setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); + const taxPercentage = getTaxValue(policy, transaction, taxCode) ?? ''; + const taxAmount = calculateTaxAmount(taxPercentage, taxableAmount, transaction.currency); + const taxAmountInSmallestCurrencyUnits = convertToBackendAmount(Number.parseFloat(taxAmount.toString())); + setMoneyRequestTaxAmount(transaction.transactionID, taxAmountInSmallestCurrencyUnits); } }, [ policy, @@ -470,8 +490,8 @@ function MoneyRequestConfirmationList({ if (!transaction?.transactionID) { return; } - const amountInCents = CurrencyUtils.convertToBackendAmount(value); - IOU.setIndividualShare(transaction?.transactionID, accountID, amountInCents); + const amountInCents = convertToBackendAmount(value); + setIndividualShare(transaction?.transactionID, accountID, amountInCents); }, [transaction], ); @@ -512,7 +532,7 @@ function MoneyRequestConfirmationList({ if (!isTypeSplit || !transaction?.splitShares) { return; } - IOU.adjustRemainingSplitShares(transaction); + adjustRemainingSplitShares(transaction); }, [isTypeSplit, transaction]); const selectedParticipants = useMemo(() => selectedParticipantsProp.filter((participant) => participant.selected), [selectedParticipantsProp]); @@ -524,7 +544,7 @@ function MoneyRequestConfirmationList({ return []; } - const payeeOption = OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails); + const payeeOption = getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails); if (shouldShowReadOnlySplits) { return [payeeOption, ...selectedParticipants].map((participantOption: Participant) => { const isPayer = participantOption.accountID === payeeOption.accountID; @@ -532,7 +552,7 @@ function MoneyRequestConfirmationList({ if (iouAmount > 0) { amount = transaction?.comment?.splits?.find((split) => split.accountID === participantOption.accountID)?.amount ?? - IOUUtils.calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode ?? '', isPayer); + calculateAmount(selectedParticipants.length, iouAmount, iouCurrencyCode ?? '', isPayer); } return { ...participantOption, @@ -540,7 +560,7 @@ function MoneyRequestConfirmationList({ isInteractive: !shouldDisableParticipant(participantOption), rightElement: ( - {amount ? CurrencyUtils.convertToDisplayString(amount, iouCurrencyCode) : ''} + {amount ? convertToDisplayString(amount, iouCurrencyCode) : ''} ), }; @@ -548,7 +568,7 @@ function MoneyRequestConfirmationList({ } const currencySymbol = currencyList?.[iouCurrencyCode ?? '']?.symbol ?? iouCurrencyCode; - const formattedTotalAmount = CurrencyUtils.convertToDisplayStringWithoutCurrency(iouAmount, iouCurrencyCode); + const formattedTotalAmount = convertToDisplayStringWithoutCurrency(iouAmount, iouCurrencyCode); return [payeeOption, ...selectedParticipants].map((participantOption: Participant) => ({ ...participantOption, @@ -570,7 +590,7 @@ function MoneyRequestConfirmationList({ inputStyle={[styles.optionRowAmountInput]} containerStyle={[styles.textInputContainer]} touchableInputWrapperStyle={[styles.ml3]} - onFormatAmount={CurrencyUtils.convertToDisplayStringWithoutCurrency} + onFormatAmount={convertToDisplayStringWithoutCurrency} onAmountChange={(value: string) => onSplitShareChange(participantOption.accountID ?? CONST.DEFAULT_NUMBER_ID, Number(value))} maxLength={formattedTotalAmount.length} contentWidth={formattedTotalAmount.length * 8} @@ -611,7 +631,7 @@ function MoneyRequestConfirmationList({ {!shouldShowReadOnlySplits && !!isSplitModified && ( { - IOU.resetSplitShares(transaction); + resetSplitShares(transaction); }} accessibilityLabel={CONST.ROLE.BUTTON} role={CONST.ROLE.BUTTON} @@ -645,7 +665,7 @@ function MoneyRequestConfirmationList({ ...[ { title: translate('moneyRequestConfirmationList.paidBy'), - data: [OptionsListUtils.getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails)], + data: [getIOUConfirmationOptionsFromPayeePersonalDetail(payeePersonalDetails)], shouldShow: true, }, { @@ -682,10 +702,10 @@ function MoneyRequestConfirmationList({ When the user completes the initial steps of the IOU flow offline and then goes online on the confirmation page. In this scenario, the route will be fetched from the server, and the waypoints will no longer be pending. */ - IOU.setMoneyRequestPendingFields(transactionID, {waypoints: isDistanceRequestWithPendingRoute ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : null}); + setMoneyRequestPendingFields(transactionID, {waypoints: isDistanceRequestWithPendingRoute ? CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD : null}); const distanceMerchant = DistanceRequestUtils.getDistanceMerchant(hasRoute, distance, unit, rate ?? 0, currency ?? CONST.CURRENCY.USD, translate, toLocaleDigit); - IOU.setMoneyRequestMerchant(transactionID, distanceMerchant, true); + setMoneyRequestMerchant(transactionID, distanceMerchant, true); }, [ isDistanceRequestWithPendingRoute, hasRoute, @@ -708,7 +728,7 @@ function MoneyRequestConfirmationList({ if (!transactionID || iouCategory || !shouldShowCategories || enabledCategories.length !== 1 || !isCategoryRequired) { return; } - IOU.setMoneyRequestCategory(transactionID, enabledCategories.at(0)?.name ?? '', policy?.id); + setMoneyRequestCategory(transactionID, enabledCategories.at(0)?.name ?? '', policy?.id); // Keep 'transaction' out to ensure that we autoselect the option only once // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps }, [shouldShowCategories, policyCategories, isCategoryRequired, policy?.id]); @@ -719,20 +739,20 @@ function MoneyRequestConfirmationList({ return; } - let updatedTagsString = TransactionUtils.getTag(transaction); + let updatedTagsString = getTag(transaction); policyTagLists.forEach((tagList, index) => { const isTagListRequired = tagList.required ?? false; if (!isTagListRequired) { return; } const enabledTags = Object.values(tagList.tags).filter((tag) => tag.enabled); - if (enabledTags.length !== 1 || TransactionUtils.getTag(transaction, index)) { + if (enabledTags.length !== 1 || getTag(transaction, index)) { return; } - updatedTagsString = IOUUtils.insertTagIntoTransactionTagsString(updatedTagsString, enabledTags.at(0)?.name ?? '', index); + updatedTagsString = insertTagIntoTransactionTagsString(updatedTagsString, enabledTags.at(0)?.name ?? '', index); }); - if (updatedTagsString !== TransactionUtils.getTag(transaction) && updatedTagsString) { - IOU.setMoneyRequestTag(transactionID, updatedTagsString); + if (updatedTagsString !== getTag(transaction) && updatedTagsString) { + setMoneyRequestTag(transactionID, updatedTagsString); } // Keep 'transaction' out to ensure that we autoselect the option only once // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps @@ -773,7 +793,7 @@ function MoneyRequestConfirmationList({ setFormError('iou.error.noParticipantSelected'); return; } - if (!isEditingSplitBill && isMerchantRequired && (isMerchantEmpty || (shouldDisplayFieldError && TransactionUtils.isMerchantMissing(transaction)))) { + if (!isEditingSplitBill && isMerchantRequired && (isMerchantEmpty || (shouldDisplayFieldError && isMerchantMissing(transaction)))) { setFormError('iou.error.invalidMerchant'); return; } @@ -789,13 +809,13 @@ function MoneyRequestConfirmationList({ if (iouType !== CONST.IOU.TYPE.PAY) { // validate the amount for distance expenses - const decimals = CurrencyUtils.getCurrencyDecimals(iouCurrencyCode); - if (isDistanceRequest && !isDistanceRequestWithPendingRoute && !MoneyRequestUtils.validateAmount(String(iouAmount), decimals)) { + const decimals = getCurrencyDecimals(iouCurrencyCode); + if (isDistanceRequest && !isDistanceRequestWithPendingRoute && !validateAmount(String(iouAmount), decimals)) { setFormError('common.error.invalidAmount'); return; } - if (isEditingSplitBill && TransactionUtils.areRequiredFieldsEmpty(transaction)) { + if (isEditingSplitBill && areRequiredFieldsEmpty(transaction)) { setDidConfirmSplit(true); setFormError('iou.error.genericSmartscanFailureMessage'); return; diff --git a/src/libs/OptionsListUtils.ts b/src/libs/OptionsListUtils.ts index 15eee25a0654..7ef893dca7e1 100644 --- a/src/libs/OptionsListUtils.ts +++ b/src/libs/OptionsListUtils.ts @@ -29,22 +29,105 @@ import type DeepValueOf from '@src/types/utils/DeepValueOf'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import Timing from './actions/Timing'; import filterArrayByMatch from './filterArrayByMatch'; -import * as LocalePhoneNumber from './LocalePhoneNumber'; -import * as Localize from './Localize'; -import * as LoginUtils from './LoginUtils'; +import {formatPhoneNumber} from './LocalePhoneNumber'; +import {translate, translateLocal} from './Localize'; +import {appendCountryCode, getPhoneNumberWithoutSpecialChars} from './LoginUtils'; import ModifiedExpenseMessage from './ModifiedExpenseMessage'; import Navigation from './Navigation/Navigation'; import Parser from './Parser'; import Performance from './Performance'; -import * as PersonalDetailsUtils from './PersonalDetailsUtils'; -import * as PhoneNumber from './PhoneNumber'; -import * as PolicyUtils from './PolicyUtils'; -import * as ReportActionUtils from './ReportActionsUtils'; -import * as ReportUtils from './ReportUtils'; -import * as TaskUtils from './TaskUtils'; -import * as UserUtils from './UserUtils'; - -type SearchOption = ReportUtils.OptionData & { +import {getDisplayNameOrDefault} from './PersonalDetailsUtils'; +import {addSMSDomainIfPhoneNumber, parsePhoneNumber} from './PhoneNumber'; +import {canSendInvoiceFromWorkspace} from './PolicyUtils'; +import { + getCombinedReportActions, + getExportIntegrationLastMessageText, + getIOUReportIDFromReportActionPreview, + getMessageOfOldDotReportAction, + getOneTransactionThreadReportID, + getOriginalMessage, + getReportActionMessageText, + getSortedReportActions, + isActionableAddPaymentCard, + isActionOfType, + isClosedAction, + isCreatedTaskReportAction, + isDeletedParentAction, + isModifiedExpenseAction, + isMoneyRequestAction, + isOldDotReportAction, + isPendingRemove, + isReimbursementDeQueuedAction, + isReimbursementQueuedAction, + isReportPreviewAction, + isResolvedActionTrackExpense, + isTaskAction, + isThreadParentMessage, + isUnapprovedAction, + isWhisperAction, + shouldReportActionBeVisible, +} from './ReportActionsUtils'; +import { + canUserPerformWriteAction as canUserPerformWriteActionUtil, + formatReportLastMessageText, + getAllReportErrors, + getChatRoomSubtitle, + getDeletedParentActionMessageForChatReport, + getDisplayNameForParticipant, + getDowngradeWorkspaceMessage, + getIcons, + getIOUApprovedMessage, + getIOUForwardedMessage, + getIOUSubmittedMessage, + getIOUUnapprovedMessage, + getMoneyRequestSpendBreakdown, + getParticipantsAccountIDsForDisplay, + getPolicyName, + getReimbursementDeQueuedActionMessage, + getReimbursementQueuedActionMessage, + getRejectedReportMessage, + getReportAutomaticallyApprovedMessage, + getReportAutomaticallyForwardedMessage, + getReportAutomaticallySubmittedMessage, + getReportLastMessage, + getReportName, + getReportNotificationPreference, + getReportOrDraftReport, + getReportParticipantsTitle, + getReportPreviewMessage, + getUpgradeWorkspaceMessage, + hasIOUWaitingOnCurrentUserBankAccount, + isAdminRoom as isAdminRoomUtil, + isAnnounceRoom as isAnnounceRoomUtil, + isArchivedNonExpenseReport, + isChatReport, + isChatRoom as isChatRoomUtil, + isChatThread, + isDefaultRoom, + isDraftReport, + isExpenseReport, + isGroupChat as isGroupChatUtil, + isHiddenForCurrentUser, + isInvoiceRoom, + isIOUOwnedByCurrentUser, + isMoneyRequest, + isMoneyRequestReport as isMoneyRequestReportUtil, + isOneOnOneChat as isOneOnOneChatUtil, + isPolicyAdmin, + isPolicyExpenseChat as isPolicyExpenseChatUtil, + isReportMessageAttachment, + isSelfDM as isSelfDMUtil, + isTaskReport as isTaskReportUtil, + isUnread, + shouldDisplayViolationsRBRInLHN, + shouldReportBeInOptionList, + shouldReportShowSubscript, +} from './ReportUtils'; +import type {OptionData} from './ReportUtils'; +import {getTaskCreatedMessage, getTaskReportActionMessage} from './TaskUtils'; +import {generateAccountID} from './UserUtils'; + +type SearchOption = OptionData & { item: T; }; @@ -53,7 +136,7 @@ type OptionList = { personalDetails: Array>; }; -type Option = Partial; +type Option = Partial; /** * A narrowed version of `Option` is used when we have a guarantee that given values exist. @@ -114,7 +197,7 @@ type GetOptionsConfig = { type GetUserToInviteConfig = { searchValue: string | undefined; - optionsToExclude?: Array>; + optionsToExclude?: Array>; reportActions?: ReportActions; shouldAcceptName?: boolean; } & Pick; @@ -136,12 +219,12 @@ type SectionForSearchTerm = { section: Section; }; type Options = { - recentReports: ReportUtils.OptionData[]; - personalDetails: ReportUtils.OptionData[]; - userToInvite: ReportUtils.OptionData | null; - currentUserOption: ReportUtils.OptionData | null | undefined; - workspaceChats?: ReportUtils.OptionData[]; - selfDMChat?: ReportUtils.OptionData | undefined; + recentReports: OptionData[]; + personalDetails: OptionData[]; + userToInvite: OptionData | null; + currentUserOption: OptionData | null | undefined; + workspaceChats?: OptionData[]; + selfDMChat?: OptionData | undefined; }; type PreviewConfig = {showChatPreviewLine?: boolean; forcePolicyNamePreview?: boolean; showPersonalDetails?: boolean}; @@ -259,15 +342,15 @@ Onyx.connect({ } const reportActionsArray = Object.values(reportActions[1] ?? {}); - let sortedReportActions = ReportActionUtils.getSortedReportActions(reportActionsArray, true); + let sortedReportActions = getSortedReportActions(reportActionsArray, true); allSortedReportActions[reportID] = sortedReportActions; // If the report is a one-transaction report and has , we need to return the combined reportActions so that the LHN can display modifications // to the transaction thread or the report itself - const transactionThreadReportID = ReportActionUtils.getOneTransactionThreadReportID(reportID, actions[reportActions[0]]); + const transactionThreadReportID = getOneTransactionThreadReportID(reportID, actions[reportActions[0]]); if (transactionThreadReportID) { const transactionThreadReportActionsArray = Object.values(actions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transactionThreadReportID}`] ?? {}); - sortedReportActions = ReportActionUtils.getCombinedReportActions(sortedReportActions, transactionThreadReportID, transactionThreadReportActionsArray, reportID, false); + sortedReportActions = getCombinedReportActions(sortedReportActions, transactionThreadReportID, transactionThreadReportActionsArray, reportID, false); } const firstReportAction = sortedReportActions.at(0); @@ -278,17 +361,17 @@ Onyx.connect({ } const report = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`]; - const canUserPerformWriteAction = ReportUtils.canUserPerformWriteAction(report); + const canUserPerformWriteAction = canUserPerformWriteActionUtil(report); // The report is only visible if it is the last action not deleted that // does not match a closed or created state. const reportActionsForDisplay = sortedReportActions.filter( (reportAction, actionKey) => - ReportActionUtils.shouldReportActionBeVisible(reportAction, actionKey, canUserPerformWriteAction) && - !ReportActionUtils.isWhisperAction(reportAction) && + shouldReportActionBeVisible(reportAction, actionKey, canUserPerformWriteAction) && + !isWhisperAction(reportAction) && reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.CREATED && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - !ReportActionUtils.isResolvedActionTrackExpense(reportAction), + !isResolvedActionTrackExpense(reportAction), ); const reportActionForDisplay = reportActionsForDisplay.at(0); if (!reportActionForDisplay) { @@ -370,11 +453,11 @@ function isPersonalDetailsReady(personalDetails: OnyxEntry) /** * Get the participant option for a report. */ -function getParticipantsOption(participant: ReportUtils.OptionData | Participant, personalDetails: OnyxEntry): Participant { +function getParticipantsOption(participant: OptionData | Participant, personalDetails: OnyxEntry): Participant { const detail = participant.accountID ? getPersonalDetailsForAccountIDs([participant.accountID], personalDetails)[participant.accountID] : undefined; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const login = detail?.login || participant.login || ''; - const displayName = LocalePhoneNumber.formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(detail, login || participant.text)); + const displayName = formatPhoneNumber(getDisplayNameOrDefault(detail, login || participant.text)); return { keyForList: String(detail?.accountID), @@ -383,7 +466,7 @@ function getParticipantsOption(participant: ReportUtils.OptionData | Participant text: displayName, firstName: detail?.firstName ?? '', lastName: detail?.lastName ?? '', - alternateText: LocalePhoneNumber.formatPhoneNumber(login) || displayName, + alternateText: formatPhoneNumber(login) || displayName, icons: [ { source: detail?.avatar ?? FallbackAvatar, @@ -424,27 +507,27 @@ function uniqFast(items: string[]): string[] { function getLastActorDisplayName(lastActorDetails: Partial | null, hasMultipleParticipants: boolean) { return hasMultipleParticipants && lastActorDetails && lastActorDetails.accountID !== currentUserAccountID ? // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - lastActorDetails.firstName || LocalePhoneNumber.formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails)) + lastActorDetails.firstName || formatPhoneNumber(getDisplayNameOrDefault(lastActorDetails)) : ''; } /** * Update alternate text for the option when applicable */ -function getAlternateText(option: ReportUtils.OptionData, {showChatPreviewLine = false, forcePolicyNamePreview = false}: PreviewConfig) { - const report = ReportUtils.getReportOrDraftReport(option.reportID); - const isAdminRoom = ReportUtils.isAdminRoom(report); - const isAnnounceRoom = ReportUtils.isAnnounceRoom(report); - const isGroupChat = ReportUtils.isGroupChat(report); - const isExpenseThread = ReportUtils.isMoneyRequest(report); - const formattedLastMessageText = ReportUtils.formatReportLastMessageText(Parser.htmlToText(option.lastMessageText ?? '')); +function getAlternateText(option: OptionData, {showChatPreviewLine = false, forcePolicyNamePreview = false}: PreviewConfig) { + const report = getReportOrDraftReport(option.reportID); + const isAdminRoom = isAdminRoomUtil(report); + const isAnnounceRoom = isAnnounceRoomUtil(report); + const isGroupChat = isGroupChatUtil(report); + const isExpenseThread = isMoneyRequest(report); + const formattedLastMessageText = formatReportLastMessageText(Parser.htmlToText(option.lastMessageText ?? '')); if (isExpenseThread || option.isMoneyRequestReport) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : Localize.translate(preferredLocale, 'iou.expense'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : translate(preferredLocale, 'iou.expense'); } if (option.isThread) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : Localize.translate(preferredLocale, 'threads.thread'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : translate(preferredLocale, 'threads.thread'); } if (option.isChatRoom && !isAdminRoom && !isAnnounceRoom) { @@ -456,16 +539,16 @@ function getAlternateText(option: ReportUtils.OptionData, {showChatPreviewLine = } if (option.isTaskReport) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : Localize.translate(preferredLocale, 'task.task'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : translate(preferredLocale, 'task.task'); } if (isGroupChat) { - return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : Localize.translate(preferredLocale, 'common.group'); + return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText : translate(preferredLocale, 'common.group'); } return showChatPreviewLine && formattedLastMessageText ? formattedLastMessageText - : LocalePhoneNumber.formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList.at(0)?.login ?? '' : ''); + : formatPhoneNumber(option.participantsList && option.participantsList.length > 0 ? option.participantsList.at(0)?.login ?? '' : ''); } function isSearchStringMatchUserDetails(personalDetail: PersonalDetails, searchValue: string) { @@ -480,7 +563,7 @@ function isSearchStringMatchUserDetails(personalDetail: PersonalDetails, searchV memberDetails += ` ${personalDetail.lastName}`; } if (personalDetail.displayName) { - memberDetails += ` ${PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail)}`; + memberDetails += ` ${getDisplayNameOrDefault(personalDetail)}`; } if (personalDetail.phoneNumber) { memberDetails += ` ${personalDetail.phoneNumber}`; @@ -496,10 +579,10 @@ function getIOUReportIDOfLastAction(report: OnyxEntry): string | undefin return; } const lastAction = lastVisibleReportActions[report.reportID]; - if (!ReportActionUtils.isReportPreviewAction(lastAction)) { + if (!isReportPreviewAction(lastAction)) { return; } - return ReportUtils.getReportOrDraftReport(ReportActionUtils.getIOUReportIDFromReportActionPreview(lastAction))?.reportID; + return getReportOrDraftReport(getIOUReportIDFromReportActionPreview(lastAction))?.reportID; } /** @@ -513,117 +596,114 @@ function getLastMessageTextForReport(report: OnyxEntry, lastActorDetails const lastOriginalReportAction = reportID ? lastReportActions[reportID] : undefined; let lastMessageTextFromReport = ''; - if (ReportUtils.isArchivedNonExpenseReport(report)) { + if (isArchivedNonExpenseReport(report)) { const archiveReason = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (ReportActionUtils.isClosedAction(lastOriginalReportAction) && ReportActionUtils.getOriginalMessage(lastOriginalReportAction)?.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; + (isClosedAction(lastOriginalReportAction) && getOriginalMessage(lastOriginalReportAction)?.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT; switch (archiveReason) { case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED: case CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY: case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: { - lastMessageTextFromReport = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { - displayName: LocalePhoneNumber.formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails)), - policyName: ReportUtils.getPolicyName(report, false, policy), + lastMessageTextFromReport = translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, { + displayName: formatPhoneNumber(getDisplayNameOrDefault(lastActorDetails)), + policyName: getPolicyName(report, false, policy), }); break; } case CONST.REPORT.ARCHIVE_REASON.BOOKING_END_DATE_HAS_PASSED: { - lastMessageTextFromReport = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`); + lastMessageTextFromReport = translate(preferredLocale, `reportArchiveReasons.${archiveReason}`); break; } default: { - lastMessageTextFromReport = Localize.translate(preferredLocale, `reportArchiveReasons.default`); + lastMessageTextFromReport = translate(preferredLocale, `reportArchiveReasons.default`); } } - } else if (ReportActionUtils.isMoneyRequestAction(lastReportAction)) { - const properSchemaForMoneyRequestMessage = ReportUtils.getReportPreviewMessage(report, lastReportAction, true, false, null, true); - lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForMoneyRequestMessage); - } else if (ReportActionUtils.isReportPreviewAction(lastReportAction)) { - const iouReport = ReportUtils.getReportOrDraftReport(ReportActionUtils.getIOUReportIDFromReportActionPreview(lastReportAction)); + } else if (isMoneyRequestAction(lastReportAction)) { + const properSchemaForMoneyRequestMessage = getReportPreviewMessage(report, lastReportAction, true, false, null, true); + lastMessageTextFromReport = formatReportLastMessageText(properSchemaForMoneyRequestMessage); + } else if (isReportPreviewAction(lastReportAction)) { + const iouReport = getReportOrDraftReport(getIOUReportIDFromReportActionPreview(lastReportAction)); const lastIOUMoneyReportAction = iouReport?.reportID ? allSortedReportActions[iouReport.reportID]?.find( (reportAction, key): reportAction is ReportAction => - ReportActionUtils.shouldReportActionBeVisible(reportAction, key, ReportUtils.canUserPerformWriteAction(report)) && + shouldReportActionBeVisible(reportAction, key, canUserPerformWriteActionUtil(report)) && reportAction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && - ReportActionUtils.isMoneyRequestAction(reportAction), + isMoneyRequestAction(reportAction), ) : undefined; - const reportPreviewMessage = ReportUtils.getReportPreviewMessage( + const reportPreviewMessage = getReportPreviewMessage( !isEmptyObject(iouReport) ? iouReport : null, lastIOUMoneyReportAction, true, - ReportUtils.isChatReport(report), + isChatReport(report), null, true, lastReportAction, ); - lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(reportPreviewMessage); - } else if (ReportActionUtils.isReimbursementQueuedAction(lastReportAction)) { - lastMessageTextFromReport = ReportUtils.getReimbursementQueuedActionMessage(lastReportAction, report); - } else if (ReportActionUtils.isReimbursementDeQueuedAction(lastReportAction)) { - lastMessageTextFromReport = ReportUtils.getReimbursementDeQueuedActionMessage(lastReportAction, report, true); - } else if (ReportActionUtils.isDeletedParentAction(lastReportAction) && ReportUtils.isChatReport(report)) { - lastMessageTextFromReport = ReportUtils.getDeletedParentActionMessageForChatReport(lastReportAction); - } else if (ReportActionUtils.isPendingRemove(lastReportAction) && report?.reportID && ReportActionUtils.isThreadParentMessage(lastReportAction, report.reportID)) { - lastMessageTextFromReport = Localize.translateLocal('parentReportAction.hiddenMessage'); - } else if (ReportUtils.isReportMessageAttachment({text: report?.lastMessageText ?? '', html: report?.lastMessageHtml, type: ''})) { - lastMessageTextFromReport = `[${Localize.translateLocal('common.attachment')}]`; - } else if (ReportActionUtils.isModifiedExpenseAction(lastReportAction)) { + lastMessageTextFromReport = formatReportLastMessageText(reportPreviewMessage); + } else if (isReimbursementQueuedAction(lastReportAction)) { + lastMessageTextFromReport = getReimbursementQueuedActionMessage(lastReportAction, report); + } else if (isReimbursementDeQueuedAction(lastReportAction)) { + lastMessageTextFromReport = getReimbursementDeQueuedActionMessage(lastReportAction, report, true); + } else if (isDeletedParentAction(lastReportAction) && isChatReport(report)) { + lastMessageTextFromReport = getDeletedParentActionMessageForChatReport(lastReportAction); + } else if (isPendingRemove(lastReportAction) && report?.reportID && isThreadParentMessage(lastReportAction, report.reportID)) { + lastMessageTextFromReport = translateLocal('parentReportAction.hiddenMessage'); + } else if (isReportMessageAttachment({text: report?.lastMessageText ?? '', html: report?.lastMessageHtml, type: ''})) { + lastMessageTextFromReport = `[${translateLocal('common.attachment')}]`; + } else if (isModifiedExpenseAction(lastReportAction)) { const properSchemaForModifiedExpenseMessage = ModifiedExpenseMessage.getForReportAction(report?.reportID, lastReportAction); - lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForModifiedExpenseMessage, true); - } else if (ReportActionUtils.isTaskAction(lastReportAction)) { - lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(TaskUtils.getTaskReportActionMessage(lastReportAction).text); - } else if (ReportActionUtils.isCreatedTaskReportAction(lastReportAction)) { - lastMessageTextFromReport = TaskUtils.getTaskCreatedMessage(lastReportAction); - } else if ( - ReportActionUtils.isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || - ReportActionUtils.isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED) - ) { - const wasSubmittedViaHarvesting = ReportActionUtils.getOriginalMessage(lastReportAction)?.harvesting ?? false; + lastMessageTextFromReport = formatReportLastMessageText(properSchemaForModifiedExpenseMessage, true); + } else if (isTaskAction(lastReportAction)) { + lastMessageTextFromReport = formatReportLastMessageText(getTaskReportActionMessage(lastReportAction).text); + } else if (isCreatedTaskReportAction(lastReportAction)) { + lastMessageTextFromReport = getTaskCreatedMessage(lastReportAction); + } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) || isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED)) { + const wasSubmittedViaHarvesting = getOriginalMessage(lastReportAction)?.harvesting ?? false; if (wasSubmittedViaHarvesting) { - lastMessageTextFromReport = ReportUtils.getReportAutomaticallySubmittedMessage(lastReportAction); + lastMessageTextFromReport = getReportAutomaticallySubmittedMessage(lastReportAction); } else { - lastMessageTextFromReport = ReportUtils.getIOUSubmittedMessage(lastReportAction); + lastMessageTextFromReport = getIOUSubmittedMessage(lastReportAction); } - } else if (ReportActionUtils.isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { - const {automaticAction} = ReportActionUtils.getOriginalMessage(lastReportAction) ?? {}; + } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.APPROVED)) { + const {automaticAction} = getOriginalMessage(lastReportAction) ?? {}; if (automaticAction) { - lastMessageTextFromReport = ReportUtils.getReportAutomaticallyApprovedMessage(lastReportAction); + lastMessageTextFromReport = getReportAutomaticallyApprovedMessage(lastReportAction); } else { - lastMessageTextFromReport = ReportUtils.getIOUApprovedMessage(lastReportAction); + lastMessageTextFromReport = getIOUApprovedMessage(lastReportAction); } - } else if (ReportActionUtils.isUnapprovedAction(lastReportAction)) { - lastMessageTextFromReport = ReportUtils.getIOUUnapprovedMessage(lastReportAction); - } else if (ReportActionUtils.isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.FORWARDED)) { - const {automaticAction} = ReportActionUtils.getOriginalMessage(lastReportAction) ?? {}; + } else if (isUnapprovedAction(lastReportAction)) { + lastMessageTextFromReport = getIOUUnapprovedMessage(lastReportAction); + } else if (isActionOfType(lastReportAction, CONST.REPORT.ACTIONS.TYPE.FORWARDED)) { + const {automaticAction} = getOriginalMessage(lastReportAction) ?? {}; if (automaticAction) { - lastMessageTextFromReport = ReportUtils.getReportAutomaticallyForwardedMessage(lastReportAction, reportID); + lastMessageTextFromReport = getReportAutomaticallyForwardedMessage(lastReportAction, reportID); } else { - lastMessageTextFromReport = ReportUtils.getIOUForwardedMessage(lastReportAction, report); + lastMessageTextFromReport = getIOUForwardedMessage(lastReportAction, report); } } else if (lastReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REJECTED) { - lastMessageTextFromReport = ReportUtils.getRejectedReportMessage(); + lastMessageTextFromReport = getRejectedReportMessage(); } else if (lastReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.CORPORATE_UPGRADE) { - lastMessageTextFromReport = ReportUtils.getUpgradeWorkspaceMessage(); + lastMessageTextFromReport = getUpgradeWorkspaceMessage(); } else if (lastReportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.TEAM_DOWNGRADE) { - lastMessageTextFromReport = ReportUtils.getDowngradeWorkspaceMessage(); - } else if (ReportActionUtils.isActionableAddPaymentCard(lastReportAction)) { - lastMessageTextFromReport = ReportActionUtils.getReportActionMessageText(lastReportAction); + lastMessageTextFromReport = getDowngradeWorkspaceMessage(); + } else if (isActionableAddPaymentCard(lastReportAction)) { + lastMessageTextFromReport = getReportActionMessageText(lastReportAction); } else if (lastReportAction?.actionName === 'EXPORTINTEGRATION') { - lastMessageTextFromReport = ReportActionUtils.getExportIntegrationLastMessageText(lastReportAction); - } else if (lastReportAction?.actionName && ReportActionUtils.isOldDotReportAction(lastReportAction)) { - lastMessageTextFromReport = ReportActionUtils.getMessageOfOldDotReportAction(lastReportAction, false); + lastMessageTextFromReport = getExportIntegrationLastMessageText(lastReportAction); + } else if (lastReportAction?.actionName && isOldDotReportAction(lastReportAction)) { + lastMessageTextFromReport = getMessageOfOldDotReportAction(lastReportAction, false); } // we do not want to show report closed in LHN for non archived report so use getReportLastMessage as fallback instead of lastMessageText from report if (reportID && !report.private_isArchived && report.lastActionType === CONST.REPORT.ACTIONS.TYPE.CLOSED) { - return lastMessageTextFromReport || (ReportUtils.getReportLastMessage(reportID).lastMessageText ?? ''); + return lastMessageTextFromReport || (getReportLastMessage(reportID).lastMessageText ?? ''); } return lastMessageTextFromReport || (report?.lastMessageText ?? ''); } function hasReportErrors(report: Report, reportActions: OnyxEntry) { - return !isEmptyObject(ReportUtils.getAllReportErrors(report, reportActions)); + return !isEmptyObject(getAllReportErrors(report, reportActions)); } /** @@ -635,9 +715,9 @@ function createOption( report: OnyxInputOrEntry, reportActions: ReportActions, config?: PreviewConfig, -): ReportUtils.OptionData { +): OptionData { const {showChatPreviewLine = false, forcePolicyNamePreview = false, showPersonalDetails = false} = config ?? {}; - const result: ReportUtils.OptionData = { + const result: OptionData = { text: undefined, alternateText: undefined, pendingAction: undefined, @@ -680,39 +760,39 @@ function createOption( result.participantsList = personalDetailList; result.isOptimisticPersonalDetail = personalDetail?.isOptimisticPersonalDetail; if (report) { - result.isChatRoom = ReportUtils.isChatRoom(report); - result.isDefaultRoom = ReportUtils.isDefaultRoom(report); + result.isChatRoom = isChatRoomUtil(report); + result.isDefaultRoom = isDefaultRoom(report); // eslint-disable-next-line @typescript-eslint/naming-convention result.private_isArchived = report.private_isArchived; - result.isExpenseReport = ReportUtils.isExpenseReport(report); - result.isInvoiceRoom = ReportUtils.isInvoiceRoom(report); - result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report); - result.isThread = ReportUtils.isChatThread(report); - result.isTaskReport = ReportUtils.isTaskReport(report); - result.shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report); - result.isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(report); + result.isExpenseReport = isExpenseReport(report); + result.isInvoiceRoom = isInvoiceRoom(report); + result.isMoneyRequestReport = isMoneyRequestReportUtil(report); + result.isThread = isChatThread(report); + result.isTaskReport = isTaskReportUtil(report); + result.shouldShowSubscript = shouldReportShowSubscript(report); + result.isPolicyExpenseChat = isPolicyExpenseChatUtil(report); result.isOwnPolicyExpenseChat = report.isOwnPolicyExpenseChat ?? false; - result.allReportErrors = ReportUtils.getAllReportErrors(report, reportActions); + result.allReportErrors = getAllReportErrors(report, reportActions); result.brickRoadIndicator = hasReportErrors(report, reportActions) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom ?? report.pendingFields.createChat : undefined; result.ownerAccountID = report.ownerAccountID; result.reportID = report.reportID; - result.isUnread = ReportUtils.isUnread(report); + result.isUnread = isUnread(report); result.isPinned = report.isPinned; result.iouReportID = report.iouReportID; result.keyForList = String(report.reportID); result.isWaitingOnBankAccount = report.isWaitingOnBankAccount; result.policyID = report.policyID; - result.isSelfDM = ReportUtils.isSelfDM(report); - result.notificationPreference = ReportUtils.getReportNotificationPreference(report); + result.isSelfDM = isSelfDMUtil(report); + result.notificationPreference = getReportNotificationPreference(report); result.lastVisibleActionCreated = report.lastVisibleActionCreated; - const visibleParticipantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report, true); + const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); - result.tooltipText = ReportUtils.getReportParticipantsTitle(visibleParticipantAccountIDs); + result.tooltipText = getReportParticipantsTitle(visibleParticipantAccountIDs); - hasMultipleParticipants = personalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat || ReportUtils.isGroupChat(report); - subtitle = ReportUtils.getChatRoomSubtitle(report); + hasMultipleParticipants = personalDetailList.length > 1 || result.isChatRoom || result.isPolicyExpenseChat || isGroupChatUtil(report); + subtitle = getChatRoomSubtitle(report); const lastActorDetails = report.lastActorAccountID ? personalDetailMap[report.lastActorAccountID] : null; const lastActorDisplayName = getLastActorDisplayName(lastActorDetails, hasMultipleParticipants); @@ -731,28 +811,26 @@ function createOption( // If displaying chat preview line is needed, let's overwrite the default alternate text result.alternateText = showPersonalDetails && personalDetail?.login ? personalDetail.login : getAlternateText(result, {showChatPreviewLine, forcePolicyNamePreview}); - reportName = showPersonalDetails - ? ReportUtils.getDisplayNameForParticipant(accountIDs.at(0)) || LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? '') - : ReportUtils.getReportName(report); + reportName = showPersonalDetails ? getDisplayNameForParticipant(accountIDs.at(0)) || formatPhoneNumber(personalDetail?.login ?? '') : getReportName(report); } else { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - reportName = ReportUtils.getDisplayNameForParticipant(accountIDs.at(0)) || LocalePhoneNumber.formatPhoneNumber(personalDetail?.login ?? ''); + reportName = getDisplayNameForParticipant(accountIDs.at(0)) || formatPhoneNumber(personalDetail?.login ?? ''); result.keyForList = String(accountIDs.at(0)); - result.alternateText = LocalePhoneNumber.formatPhoneNumber(personalDetails?.[accountIDs[0]]?.login ?? ''); + result.alternateText = formatPhoneNumber(personalDetails?.[accountIDs[0]]?.login ?? ''); } - result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result); - result.iouReportAmount = ReportUtils.getMoneyRequestSpendBreakdown(result).totalDisplaySpend; + result.isIOUReportOwner = isIOUOwnedByCurrentUser(result); + result.iouReportAmount = getMoneyRequestSpendBreakdown(result).totalDisplaySpend; - if (!hasMultipleParticipants && (!report || (report && !ReportUtils.isGroupChat(report) && !ReportUtils.isChatRoom(report)))) { + if (!hasMultipleParticipants && (!report || (report && !isGroupChatUtil(report) && !isChatRoomUtil(report)))) { result.login = personalDetail?.login; result.accountID = Number(personalDetail?.accountID); result.phoneNumber = personalDetail?.phoneNumber; } result.text = reportName; - result.icons = ReportUtils.getIcons(report, personalDetails, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID, null); + result.icons = getIcons(report, personalDetails, personalDetail?.avatar, personalDetail?.login, personalDetail?.accountID, null); result.subtitle = subtitle; return result; @@ -761,9 +839,9 @@ function createOption( /** * Get the option for a given report. */ -function getReportOption(participant: Participant): ReportUtils.OptionData { - const report = ReportUtils.getReportOrDraftReport(participant.reportID); - const visibleParticipantAccountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report, true); +function getReportOption(participant: Participant): OptionData { + const report = getReportOrDraftReport(participant.reportID); + const visibleParticipantAccountIDs = getParticipantsAccountIDsForDisplay(report, true); const option = createOption( visibleParticipantAccountIDs, @@ -778,15 +856,15 @@ function getReportOption(participant: Participant): ReportUtils.OptionData { // Update text & alternateText because createOption returns workspace name only if report is owned by the user if (option.isSelfDM) { - option.alternateText = Localize.translateLocal('reportActionsView.yourSpace'); + option.alternateText = translateLocal('reportActionsView.yourSpace'); } else if (option.isInvoiceRoom) { - option.text = ReportUtils.getReportName(report); - option.alternateText = Localize.translateLocal('workspace.common.invoices'); + option.text = getReportName(report); + option.alternateText = translateLocal('workspace.common.invoices'); } else { - option.text = ReportUtils.getPolicyName(report); - option.alternateText = Localize.translateLocal('workspace.common.workspace'); + option.text = getPolicyName(report); + option.alternateText = translateLocal('workspace.common.workspace'); } - option.isDisabled = ReportUtils.isDraftReport(participant.reportID); + option.isDisabled = isDraftReport(participant.reportID); option.selected = participant.selected; option.isSelected = participant.selected; return option; @@ -795,11 +873,11 @@ function getReportOption(participant: Participant): ReportUtils.OptionData { /** * Get the option for a policy expense report. */ -function getPolicyExpenseReportOption(participant: Participant | ReportUtils.OptionData): ReportUtils.OptionData { - const expenseReport = ReportUtils.isPolicyExpenseChat(participant) ? ReportUtils.getReportOrDraftReport(participant.reportID) : null; +function getPolicyExpenseReportOption(participant: Participant | OptionData): OptionData { + const expenseReport = isPolicyExpenseChatUtil(participant) ? getReportOrDraftReport(participant.reportID) : null; const visibleParticipantAccountIDs = Object.entries(expenseReport?.participants ?? {}) - .filter(([, reportParticipant]) => reportParticipant && !ReportUtils.isHiddenForCurrentUser(reportParticipant.notificationPreference)) + .filter(([, reportParticipant]) => reportParticipant && !isHiddenForCurrentUser(reportParticipant.notificationPreference)) .map(([accountID]) => Number(accountID)); const option = createOption( @@ -814,8 +892,8 @@ function getPolicyExpenseReportOption(participant: Participant | ReportUtils.Opt ); // Update text & alternateText because createOption returns workspace name only if report is owned by the user - option.text = ReportUtils.getPolicyName(expenseReport); - option.alternateText = Localize.translateLocal('workspace.common.workspace'); + option.text = getPolicyName(expenseReport); + option.alternateText = translateLocal('workspace.common.workspace'); option.selected = participant.selected; option.isSelected = participant.selected; return option; @@ -850,7 +928,7 @@ function isCurrentUser(userDetails: PersonalDetails): boolean { } // If user login is a mobile number, append sms domain if not appended already. - const userDetailsLogin = PhoneNumber.addSMSDomainIfPhoneNumber(userDetails.login ?? ''); + const userDetailsLogin = addSMSDomainIfPhoneNumber(userDetails.login ?? ''); if (currentUserLogin?.toLowerCase() === userDetailsLogin.toLowerCase()) { return true; @@ -868,7 +946,7 @@ function getEnabledCategoriesCount(options: PolicyCategories): number { } function getSearchValueForPhoneOrEmail(searchTerm: string) { - const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchTerm))); + const parsedPhoneNumber = parsePhoneNumber(appendCountryCode(Str.removeSMSDomain(searchTerm))); return parsedPhoneNumber.possible ? parsedPhoneNumber.number?.e164 ?? '' : searchTerm.toLowerCase(); } @@ -886,7 +964,7 @@ function hasEnabledOptions(options: PolicyCategories | PolicyTag[]): boolean { * @param selectedOptions - Array of selected options to compare with. * @returns true if the report option matches any of the selected options by accountID or reportID, false otherwise. */ -function isReportSelected(reportOption: ReportUtils.OptionData, selectedOptions: Array>) { +function isReportSelected(reportOption: OptionData, selectedOptions: Array>) { if (!selectedOptions || selectedOptions.length === 0) { return false; } @@ -905,10 +983,10 @@ function createOptionList(personalDetails: OnyxEntry, repor return; } - const isOneOnOneChat = ReportUtils.isOneOnOneChat(report); - const accountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report); + const isOneOnOneChat = isOneOnOneChatUtil(report); + const accountIDs = getParticipantsAccountIDsForDisplay(report); - const isChatRoom = ReportUtils.isChatRoom(report); + const isChatRoom = isChatRoomUtil(report); if ((!accountIDs || accountIDs.length === 0) && !isChatRoom) { return; } @@ -945,7 +1023,7 @@ function createOptionList(personalDetails: OnyxEntry, repor } function createOptionFromReport(report: Report, personalDetails: OnyxEntry) { - const accountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report); + const accountIDs = getParticipantsAccountIDsForDisplay(report); return { item: report, @@ -953,7 +1031,7 @@ function createOptionFromReport(report: Report, personalDetails: OnyxEntry personalDetail.text?.toLowerCase()], 'asc'); } @@ -962,7 +1040,7 @@ function orderPersonalDetailsOptions(options: ReportUtils.OptionData[]) { * Orders report options without grouping them by kind. * Usually used when there is no search value */ -function orderReportOptions(options: ReportUtils.OptionData[]) { +function orderReportOptions(options: OptionData[]) { return lodashOrderBy(options, [sortComparatorReportOptionByArchivedStatus, sortComparatorReportOptionByDate], ['asc', 'desc']); } @@ -973,7 +1051,7 @@ function orderReportOptions(options: ReportUtils.OptionData[]) { * @returns a sorted list of options */ function orderReportOptionsWithSearch( - options: ReportUtils.OptionData[], + options: OptionData[], searchValue: string, {preferChatroomsOverThreads = false, preferPolicyExpenseChat = false, preferRecentExpenseReports = false}: OrderReportOptionsConfig = {}, ) { @@ -1021,7 +1099,7 @@ function orderReportOptionsWithSearch( ); } -function orderWorkspaceOptions(options: ReportUtils.OptionData[]): ReportUtils.OptionData[] { +function orderWorkspaceOptions(options: OptionData[]): OptionData[] { return options.sort((a, b) => { // Check if `a` is the default workspace if (a.isPolicyExpenseChat && a.policyID === activePolicyID) { @@ -1037,11 +1115,11 @@ function orderWorkspaceOptions(options: ReportUtils.OptionData[]): ReportUtils.O }); } -function sortComparatorReportOptionByArchivedStatus(option: ReportUtils.OptionData) { +function sortComparatorReportOptionByArchivedStatus(option: OptionData) { return option.private_isArchived ? 1 : 0; } -function sortComparatorReportOptionByDate(options: ReportUtils.OptionData) { +function sortComparatorReportOptionByDate(options: OptionData) { // If there is no date (ie. a personal detail option), the option will be sorted to the bottom // (comparing a dateString > '' returns true, and we are sorting descending, so the dateString will come before '') return options.lastVisibleActionCreated ?? ''; @@ -1057,7 +1135,7 @@ function orderOptions(options: ReportAndPersonalDetailOptions): ReportAndPersona */ function orderOptions(options: ReportAndPersonalDetailOptions, searchValue: string, config?: OrderReportOptionsConfig): ReportAndPersonalDetailOptions; function orderOptions(options: ReportAndPersonalDetailOptions, searchValue?: string, config?: OrderReportOptionsConfig): ReportAndPersonalDetailOptions { - let orderedReportOptions: ReportUtils.OptionData[]; + let orderedReportOptions: OptionData[]; if (searchValue) { orderedReportOptions = orderReportOptionsWithSearch(options.recentReports, searchValue, config); } else { @@ -1079,9 +1157,9 @@ function canCreateOptimisticPersonalDetailOption({ currentUserOption, searchValue, }: { - recentReportOptions: ReportUtils.OptionData[]; - personalDetailsOptions: ReportUtils.OptionData[]; - currentUserOption?: ReportUtils.OptionData | null; + recentReportOptions: OptionData[]; + personalDetailsOptions: OptionData[]; + currentUserOption?: OptionData | null; searchValue: string; }) { if (recentReportOptions.length + personalDetailsOptions.length > 0) { @@ -1090,7 +1168,7 @@ function canCreateOptimisticPersonalDetailOption({ if (!currentUserOption) { return true; } - return currentUserOption.login !== PhoneNumber.addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase() && currentUserOption.login !== searchValue?.toLowerCase(); + return currentUserOption.login !== addSMSDomainIfPhoneNumber(searchValue ?? '').toLowerCase() && currentUserOption.login !== searchValue?.toLowerCase(); } /** @@ -1107,25 +1185,25 @@ function getUserToInviteOption({ reportActions = {}, showChatPreviewLine = false, shouldAcceptName = false, -}: GetUserToInviteConfig): ReportUtils.OptionData | null { +}: GetUserToInviteConfig): OptionData | null { if (!searchValue) { return null; } - const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchValue))); + const parsedPhoneNumber = parsePhoneNumber(appendCountryCode(Str.removeSMSDomain(searchValue))); const isCurrentUserLogin = isCurrentUser({login: searchValue} as PersonalDetails); const isInSelectedOption = selectedOptions.some((option) => 'login' in option && option.login === searchValue); const isValidEmail = Str.isValidEmail(searchValue) && !Str.isDomainEmail(searchValue) && !Str.endsWith(searchValue, CONST.SMS.DOMAIN); - const isValidPhoneNumber = parsedPhoneNumber.possible && Str.isValidE164Phone(LoginUtils.getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')); + const isValidPhoneNumber = parsedPhoneNumber.possible && Str.isValidE164Phone(getPhoneNumberWithoutSpecialChars(parsedPhoneNumber.number?.input ?? '')); const isInOptionToExclude = - optionsToExclude.findIndex((optionToExclude) => 'login' in optionToExclude && optionToExclude.login === PhoneNumber.addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) !== -1; + optionsToExclude.findIndex((optionToExclude) => 'login' in optionToExclude && optionToExclude.login === addSMSDomainIfPhoneNumber(searchValue).toLowerCase()) !== -1; if (isCurrentUserLogin || isInSelectedOption || (!isValidEmail && !isValidPhoneNumber && !shouldAcceptName) || isInOptionToExclude) { return null; } // Generates an optimistic account ID for new users not yet saved in Onyx - const optimisticAccountID = UserUtils.generateAccountID(searchValue); + const optimisticAccountID = generateAccountID(searchValue); const personalDetailsExtended = { ...allPersonalDetails, [optimisticAccountID]: { @@ -1191,9 +1269,9 @@ function getValidOptions( // Filter out all the reports that shouldn't be displayed const filteredReportOptions = options.reports.filter((option) => { const report = option.item; - const doesReportHaveViolations = ReportUtils.shouldDisplayViolationsRBRInLHN(report, transactionViolations); + const doesReportHaveViolations = shouldDisplayViolationsRBRInLHN(report, transactionViolations); - return ReportUtils.shouldReportBeInOptionList({ + return shouldReportBeInOptionList({ report, currentReportId: topmostReportId, betas, @@ -1220,7 +1298,7 @@ function getValidOptions( const isMoneyRequestReport = option.isMoneyRequestReport; const isSelfDM = option.isSelfDM; const isChatRoom = option.isChatRoom; - const accountIDs = ReportUtils.getParticipantsAccountIDsForDisplay(report); + const accountIDs = getParticipantsAccountIDsForDisplay(report); if (isPolicyExpenseChat && report.isOwnPolicyExpenseChat && !includeOwnedWorkspaceChats) { return false; @@ -1248,7 +1326,7 @@ function getValidOptions( } // In case user needs to add credit bank account, don't allow them to submit an expense from the workspace. - if (includeOwnedWorkspaceChats && ReportUtils.hasIOUWaitingOnCurrentUserBankAccount(report)) { + if (includeOwnedWorkspaceChats && hasIOUWaitingOnCurrentUserBankAccount(report)) { return false; } @@ -1259,7 +1337,7 @@ function getValidOptions( return true; }); - let workspaceChats: ReportUtils.OptionData[] = []; + let workspaceChats: OptionData[] = []; if (shouldSeparateWorkspaceChat) { workspaceChats = allReportOptions.filter((option) => option.isOwnPolicyExpenseChat && !option.private_isArchived); @@ -1282,8 +1360,8 @@ function getValidOptions( optionsToExclude.push({login}); }); - let recentReportOptions: ReportUtils.OptionData[] = []; - const personalDetailsOptions: ReportUtils.OptionData[] = []; + let recentReportOptions: OptionData[] = []; + const personalDetailsOptions: OptionData[] = []; const preferRecentExpenseReports = action === CONST.IOU.ACTION.CREATE; @@ -1299,10 +1377,10 @@ function getValidOptions( const shouldShowInvoiceRoom = includeInvoiceRooms && - ReportUtils.isInvoiceRoom(reportOption.item) && - ReportUtils.isPolicyAdmin(reportOption.policyID, policies) && + isInvoiceRoom(reportOption.item) && + isPolicyAdmin(reportOption.policyID, policies) && !reportOption.private_isArchived && - PolicyUtils.canSendInvoiceFromWorkspace(reportOption.policyID); + canSendInvoiceFromWorkspace(reportOption.policyID); /** Exclude the report option if it doesn't meet any of the following conditions: @@ -1335,12 +1413,10 @@ function getValidOptions( // Add a field to sort the recent reports by the time of last IOU request for create actions if (preferRecentExpenseReports) { - const reportPreviewAction = allSortedReportActions[reportOption.reportID]?.find((reportAction) => - ReportActionUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW), - ); + const reportPreviewAction = allSortedReportActions[reportOption.reportID]?.find((reportAction) => isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW)); if (reportPreviewAction) { - const iouReportID = ReportActionUtils.getIOUReportIDFromReportActionPreview(reportPreviewAction); + const iouReportID = getIOUReportIDFromReportActionPreview(reportPreviewAction); const iouReportActions = iouReportID ? allSortedReportActions[iouReportID] ?? [] : []; const lastIOUAction = iouReportActions.find((iouAction) => iouAction.actionName === CONST.REPORT.ACTIONS.TYPE.IOU); if (lastIOUAction) { @@ -1368,7 +1444,7 @@ function getValidOptions( } } else if (recentAttendees && recentAttendees?.length > 0) { recentAttendees.filter((attendee) => attendee.login ?? attendee.displayName).forEach((a) => optionsToExclude.push({login: a.login ?? a.displayName})); - recentReportOptions = recentAttendees as ReportUtils.OptionData[]; + recentReportOptions = recentAttendees as OptionData[]; } const personalDetailsOptionsToExclude = [...optionsToExclude, {login: currentUserLogin}]; @@ -1383,7 +1459,7 @@ function getValidOptions( } const currentUserOption = allPersonalDetailsOptions.find((personalDetailsOption) => personalDetailsOption.login === currentUserLogin); - let selfDMChat: ReportUtils.OptionData | undefined; + let selfDMChat: OptionData | undefined; if (shouldSeparateWorkspaceChat) { recentReportOptions = recentReportOptions.filter((option) => !option.isPolicyExpenseChat); @@ -1452,8 +1528,8 @@ function getShareLogOptions(options: OptionList, betas: Beta[] = []): Options { function getIOUConfirmationOptionsFromPayeePersonalDetail(personalDetail: OnyxEntry, amountText?: string): PayeePersonalDetails { const login = personalDetail?.login ?? ''; return { - text: LocalePhoneNumber.formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, login)), - alternateText: LocalePhoneNumber.formatPhoneNumber(login || PersonalDetailsUtils.getDisplayNameOrDefault(personalDetail, '', false)), + text: formatPhoneNumber(getDisplayNameOrDefault(personalDetail, login)), + alternateText: formatPhoneNumber(login || getDisplayNameOrDefault(personalDetail, '', false)), icons: [ { source: personalDetail?.avatar ?? FallbackAvatar, @@ -1506,7 +1582,7 @@ function getShareDestinationOptions( reports: Array> = [], personalDetails: Array> = [], betas: OnyxEntry = [], - selectedOptions: Array> = [], + selectedOptions: Array> = [], excludeLogins: string[] = [], includeOwnedWorkspaceChats = true, ) { @@ -1534,7 +1610,7 @@ function getShareDestinationOptions( * @param member - personalDetails or userToInvite * @param config - keys to overwrite the default values */ -function formatMemberForList(member: ReportUtils.OptionData): MemberForList { +function formatMemberForList(member: OptionData): MemberForList { const accountID = member.accountID; return { @@ -1588,27 +1664,27 @@ function getMemberInviteOptions( * Helper method that returns the text to be used for the header's message and title (if any) */ function getHeaderMessage(hasSelectableOptions: boolean, hasUserToInvite: boolean, searchValue: string, hasMatchedParticipant = false): string { - const isValidPhone = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(searchValue)).possible; + const isValidPhone = parsePhoneNumber(appendCountryCode(searchValue)).possible; const isValidEmail = Str.isValidEmail(searchValue); if (searchValue && CONST.REGEX.DIGITS_AND_PLUS.test(searchValue) && !isValidPhone && !hasSelectableOptions) { - return Localize.translate(preferredLocale, 'messages.errorMessageInvalidPhone'); + return translate(preferredLocale, 'messages.errorMessageInvalidPhone'); } // Without a search value, it would be very confusing to see a search validation message. // Therefore, this skips the validation when there is no search value. if (searchValue && !hasSelectableOptions && !hasUserToInvite) { if (/^\d+$/.test(searchValue) && !isValidPhone) { - return Localize.translate(preferredLocale, 'messages.errorMessageInvalidPhone'); + return translate(preferredLocale, 'messages.errorMessageInvalidPhone'); } if (/@/.test(searchValue) && !isValidEmail) { - return Localize.translate(preferredLocale, 'messages.errorMessageInvalidEmail'); + return translate(preferredLocale, 'messages.errorMessageInvalidEmail'); } if (hasMatchedParticipant && (isValidEmail || isValidPhone)) { return ''; } - return Localize.translate(preferredLocale, 'common.noResultsFound'); + return translate(preferredLocale, 'common.noResultsFound'); } return ''; @@ -1619,7 +1695,7 @@ function getHeaderMessage(hasSelectableOptions: boolean, hasUserToInvite: boolea */ function getHeaderMessageForNonUserList(hasSelectableOptions: boolean, searchValue: string): string { if (searchValue && !hasSelectableOptions) { - return Localize.translate(preferredLocale, 'common.noResultsFound'); + return translate(preferredLocale, 'common.noResultsFound'); } return ''; } @@ -1627,7 +1703,7 @@ function getHeaderMessageForNonUserList(hasSelectableOptions: boolean, searchVal /** * Helper method to check whether an option can show tooltip or not */ -function shouldOptionShowTooltip(option: ReportUtils.OptionData): boolean { +function shouldOptionShowTooltip(option: OptionData): boolean { return !option.private_isArchived; } @@ -1636,12 +1712,12 @@ function shouldOptionShowTooltip(option: ReportUtils.OptionData): boolean { */ function formatSectionsFromSearchTerm( searchTerm: string, - selectedOptions: ReportUtils.OptionData[], - filteredRecentReports: ReportUtils.OptionData[], - filteredPersonalDetails: ReportUtils.OptionData[], + selectedOptions: OptionData[], + filteredRecentReports: OptionData[], + filteredPersonalDetails: OptionData[], personalDetails: OnyxEntry = {}, shouldGetOptionDetails = false, - filteredWorkspaceChats: ReportUtils.OptionData[] = [], + filteredWorkspaceChats: OptionData[] = [], ): SectionForSearchTerm { // We show the selected participants at the top of the list when there is no search term or maximum number of participants has already been selected // However, if there is a search term we remove the selected participants from the top of the list unless they are part of the search results @@ -1700,18 +1776,18 @@ function getFirstKeyForList(data?: Option[] | null) { return firstNonEmptyDataObj?.keyForList ? firstNonEmptyDataObj?.keyForList : ''; } -function getPersonalDetailSearchTerms(item: Partial) { +function getPersonalDetailSearchTerms(item: Partial) { return [item.participantsList?.[0]?.displayName ?? '', item.login ?? '', item.login?.replace(CONST.EMAIL_SEARCH_REGEX, '') ?? '']; } -function getCurrentUserSearchTerms(item: ReportUtils.OptionData) { +function getCurrentUserSearchTerms(item: OptionData) { return [item.text ?? '', item.login ?? '', item.login?.replace(CONST.EMAIL_SEARCH_REGEX, '') ?? '']; } /** * Remove the personal details for the DMs that are already in the recent reports so that we don't show duplicates. */ -function filteredPersonalDetailsOfRecentReports(recentReports: ReportUtils.OptionData[], personalDetails: ReportUtils.OptionData[]) { +function filteredPersonalDetailsOfRecentReports(recentReports: OptionData[], personalDetails: OptionData[]) { const excludedLogins = new Set(recentReports.map((report) => report.login)); return personalDetails.filter((personalDetail) => !excludedLogins.has(personalDetail.login)); } @@ -1719,7 +1795,7 @@ function filteredPersonalDetailsOfRecentReports(recentReports: ReportUtils.Optio /** * Filters options based on the search input value */ -function filterReports(reports: ReportUtils.OptionData[], searchTerms: string[]): ReportUtils.OptionData[] { +function filterReports(reports: OptionData[], searchTerms: string[]): OptionData[] { // We search eventually for multiple whitespace separated search terms. // We start with the search term at the end, and then narrow down those filtered search results with the next search term. // We repeat (reduce) this until all search terms have been used: @@ -1755,7 +1831,7 @@ function filterReports(reports: ReportUtils.OptionData[], searchTerms: string[]) return filteredReports; } -function filterWorkspaceChats(reports: ReportUtils.OptionData[], searchTerms: string[]): ReportUtils.OptionData[] { +function filterWorkspaceChats(reports: OptionData[], searchTerms: string[]): OptionData[] { const filteredReports = searchTerms.reduceRight( (items, term) => filterArrayByMatch(items, term, (item) => { @@ -1772,7 +1848,7 @@ function filterWorkspaceChats(reports: ReportUtils.OptionData[], searchTerms: st return filteredReports; } -function filterPersonalDetails(personalDetails: ReportUtils.OptionData[], searchTerms: string[]): ReportUtils.OptionData[] { +function filterPersonalDetails(personalDetails: OptionData[], searchTerms: string[]): OptionData[] { return searchTerms.reduceRight( (items, term) => filterArrayByMatch(items, term, (item) => { @@ -1783,7 +1859,7 @@ function filterPersonalDetails(personalDetails: ReportUtils.OptionData[], search ); } -function filterCurrentUserOption(currentUserOption: ReportUtils.OptionData | null | undefined, searchTerms: string[]): ReportUtils.OptionData | null | undefined { +function filterCurrentUserOption(currentUserOption: OptionData | null | undefined, searchTerms: string[]): OptionData | null | undefined { return searchTerms.reduceRight((item, term) => { if (!item) { return null; @@ -1794,7 +1870,7 @@ function filterCurrentUserOption(currentUserOption: ReportUtils.OptionData | nul }, currentUserOption); } -function filterUserToInvite(options: Omit, searchValue: string, config?: FilterUserToInviteConfig): ReportUtils.OptionData | null { +function filterUserToInvite(options: Omit, searchValue: string, config?: FilterUserToInviteConfig): OptionData | null { const {canInviteUser = true, excludeLogins = []} = config ?? {}; if (!canInviteUser) { return null; @@ -1823,7 +1899,7 @@ function filterUserToInvite(options: Omit, searchValue: }); } -function filterSelfDMChat(report: ReportUtils.OptionData, searchTerms: string[]): ReportUtils.OptionData | undefined { +function filterSelfDMChat(report: OptionData, searchTerms: string[]): OptionData | undefined { const isMatch = searchTerms.every((term) => { const values: string[] = []; @@ -1852,7 +1928,7 @@ function filterSelfDMChat(report: ReportUtils.OptionData, searchTerms: string[]) } function filterOptions(options: Options, searchInputValue: string, config?: FilterUserToInviteConfig): Options { - const parsedPhoneNumber = PhoneNumber.parsePhoneNumber(LoginUtils.appendCountryCode(Str.removeSMSDomain(searchInputValue))); + const parsedPhoneNumber = parsePhoneNumber(appendCountryCode(Str.removeSMSDomain(searchInputValue))); const searchValue = parsedPhoneNumber.possible && parsedPhoneNumber.number?.e164 ? parsedPhoneNumber.number.e164 : searchInputValue.toLowerCase(); const searchTerms = searchValue ? searchValue.split(' ') : []; @@ -1950,9 +2026,9 @@ function getEmptyOptions(): Options { }; } -function shouldUseBoldText(report: ReportUtils.OptionData): boolean { - const notificationPreference = report.notificationPreference ?? ReportUtils.getReportNotificationPreference(report); - return report.isUnread === true && notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE && !ReportUtils.isHiddenForCurrentUser(notificationPreference); +function shouldUseBoldText(report: OptionData): boolean { + const notificationPreference = report.notificationPreference ?? getReportNotificationPreference(report); + return report.isUnread === true && notificationPreference !== CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE && !isHiddenForCurrentUser(notificationPreference); } export { diff --git a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx index feefb3f96ecb..5f9cb87d15b8 100644 --- a/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx +++ b/src/pages/iou/request/MoneyRequestParticipantsSelector.tsx @@ -19,14 +19,26 @@ import useNetwork from '@hooks/useNetwork'; import usePolicy from '@hooks/usePolicy'; import useScreenWrapperTranstionStatus from '@hooks/useScreenWrapperTransitionStatus'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import {canUseTouchScreen} from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; -import * as OptionsListUtils from '@libs/OptionsListUtils'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as SubscriptionUtils from '@libs/SubscriptionUtils'; -import * as Policy from '@userActions/Policy/Policy'; -import * as Report from '@userActions/Report'; +import { + filterAndOrderOptions, + formatSectionsFromSearchTerm, + getHeaderMessage, + getParticipantsOption, + getPersonalDetailSearchTerms, + getPolicyExpenseReportOption, + getValidOptions, + isCurrentUser, + orderOptions, +} from '@libs/OptionsListUtils'; +import type {Section} from '@libs/OptionsListUtils'; +import {isPaidGroupPolicy as isPaidGroupPolicyUtil} from '@libs/PolicyUtils'; +import type {OptionData} from '@libs/ReportUtils'; +import {isInvoiceRoom} from '@libs/ReportUtils'; +import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; +import {getInvoicePrimaryWorkspace} from '@userActions/Policy/Policy'; +import {searchInServer} from '@userActions/Report'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -71,12 +83,12 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF const cleanSearchTerm = useMemo(() => debouncedSearchTerm.trim().toLowerCase(), [debouncedSearchTerm]); const offlineMessage: string = isOffline ? `${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}` : ''; - const isPaidGroupPolicy = useMemo(() => PolicyUtils.isPaidGroupPolicy(policy), [policy]); + const isPaidGroupPolicy = useMemo(() => isPaidGroupPolicyUtil(policy), [policy]); const isIOUSplit = iouType === CONST.IOU.TYPE.SPLIT; const isCategorizeOrShareAction = [CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].some((option) => option === action); useEffect(() => { - Report.searchInServer(debouncedSearchTerm.trim()); + searchInServer(debouncedSearchTerm.trim()); }, [debouncedSearchTerm]); const defaultOptions = useMemo(() => { @@ -90,7 +102,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF }; } - const optionList = OptionsListUtils.getValidOptions( + const optionList = getValidOptions( { reports: options.reports, personalDetails: options.personalDetails, @@ -114,7 +126,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF }, ); - const orderedOptions = OptionsListUtils.orderOptions(optionList); + const orderedOptions = orderOptions(optionList); return { ...optionList, @@ -135,7 +147,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF }; } - const newOptions = OptionsListUtils.filterAndOrderOptions(defaultOptions, debouncedSearchTerm, { + const newOptions = filterAndOrderOptions(defaultOptions, debouncedSearchTerm, { canInviteUser: !isCategorizeOrShareAction, selectedOptions: participants as Participant[], excludeLogins: CONST.EXPENSIFY_EMAILS, @@ -151,14 +163,14 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF * @returns {Array} */ const [sections, header] = useMemo(() => { - const newSections: OptionsListUtils.Section[] = []; + const newSections: Section[] = []; if (!areOptionsInitialized || !didScreenTransitionEnd) { return [newSections, '']; } - const formatResults = OptionsListUtils.formatSectionsFromSearchTerm( + const formatResults = formatSectionsFromSearchTerm( debouncedSearchTerm, - participants.map((participant) => ({...participant, reportID: participant.reportID})) as ReportUtils.OptionData[], + participants.map((participant) => ({...participant, reportID: participant.reportID})) as OptionData[], chatOptions.recentReports, chatOptions.personalDetails, personalDetails, @@ -193,7 +205,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF if ( chatOptions.userToInvite && - !OptionsListUtils.isCurrentUser({ + !isCurrentUser({ ...chatOptions.userToInvite, accountID: chatOptions.userToInvite?.accountID ?? CONST.DEFAULT_NUMBER_ID, status: chatOptions.userToInvite?.status ?? undefined, @@ -203,17 +215,17 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF title: undefined, data: [chatOptions.userToInvite].map((participant) => { const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false; - return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails); + return isPolicyExpenseChat ? getPolicyExpenseReportOption(participant) : getParticipantsOption(participant, personalDetails); }), shouldShow: true, }); } - const headerMessage = OptionsListUtils.getHeaderMessage( + const headerMessage = getHeaderMessage( (chatOptions.personalDetails ?? []).length + (chatOptions.recentReports ?? []).length + (chatOptions.workspaceChats ?? []).length !== 0 || !isEmptyObject(chatOptions.selfDMChat), !!chatOptions?.userToInvite, debouncedSearchTerm.trim(), - participants.some((participant) => OptionsListUtils.getPersonalDetailSearchTerms(participant).join(' ').toLowerCase().includes(cleanSearchTerm)), + participants.some((participant) => getPersonalDetailSearchTerms(participant).join(' ').toLowerCase().includes(cleanSearchTerm)), ); return [newSections, headerMessage]; @@ -248,7 +260,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF ]; if (iouType === CONST.IOU.TYPE.INVOICE) { - const policyID = option.item && ReportUtils.isInvoiceRoom(option.item) ? option.policyID : Policy.getInvoicePrimaryWorkspace(currentUserLogin)?.id; + const policyID = option.item && isInvoiceRoom(option.item) ? option.policyID : getInvoicePrimaryWorkspace(currentUserLogin)?.id; newParticipants.push({ policyID, isSender: true, @@ -412,7 +424,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF const onSelectRow = useCallback( (option: Participant) => { - if (option.isPolicyExpenseChat && option.policyID && SubscriptionUtils.shouldRestrictUserBillableActions(option.policyID)) { + if (option.isPolicyExpenseChat && option.policyID && shouldRestrictUserBillableActions(option.policyID)) { Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(option.policyID)); return; } @@ -436,7 +448,7 @@ function MoneyRequestParticipantsSelector({participants = CONST.EMPTY_ARRAY, onF textInputLabel={translate('selectionList.nameEmailOrPhoneNumber')} textInputHint={offlineMessage} onChangeText={setSearchTerm} - shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()} + shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()} onSelectRow={onSelectRow} shouldSingleExecuteRowSelect footerContent={footerContent} diff --git a/src/pages/iou/request/step/IOURequestStepParticipants.tsx b/src/pages/iou/request/step/IOURequestStepParticipants.tsx index df2f39158056..c9a4353d854a 100644 --- a/src/pages/iou/request/step/IOURequestStepParticipants.tsx +++ b/src/pages/iou/request/step/IOURequestStepParticipants.tsx @@ -5,16 +5,24 @@ import FormHelpMessage from '@components/FormHelpMessage'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {READ_COMMANDS} from '@libs/API/types'; -import * as Browser from '@libs/Browser'; +import {isMobileSafari as isMobileSafariUtil} from '@libs/Browser'; import DistanceRequestUtils from '@libs/DistanceRequestUtils'; import getPlatform from '@libs/getPlatform'; import HttpUtils from '@libs/HttpUtils'; -import * as IOUUtils from '@libs/IOUUtils'; +import {isMovingTransactionFromTrackExpense, navigateToStartMoneyRequestStep} from '@libs/IOUUtils'; import Navigation from '@libs/Navigation/Navigation'; -import * as ReportUtils from '@libs/ReportUtils'; -import * as TransactionUtils from '@libs/TransactionUtils'; +import {createDraftWorkspaceAndNavigateToConfirmationScreen, findSelfDMReportID, isInvoiceRoomWithID} from '@libs/ReportUtils'; +import {getRequestType} from '@libs/TransactionUtils'; import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyRequestParticipantsSelector'; -import * as IOU from '@userActions/IOU'; +import { + navigateToStartStepIfScanFileCannotBeRead, + setCustomUnitRateID, + setMoneyRequestCategory, + setMoneyRequestParticipants, + setMoneyRequestParticipantsFromReport, + setMoneyRequestTag, + setSplitShares, +} from '@userActions/IOU'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; @@ -46,7 +54,7 @@ function IOURequestStepParticipants({ // We need to set selectedReportID if user has navigated back from confirmation page and navigates to confirmation page with already selected participant const selectedReportID = useRef(participants?.length === 1 ? participants.at(0)?.reportID ?? reportID : reportID); const numberOfParticipants = useRef(participants?.length ?? 0); - const iouRequestType = TransactionUtils.getRequestType(transaction); + const iouRequestType = getRequestType(transaction); const isSplitRequest = iouType === CONST.IOU.TYPE.SPLIT; const headerTitle = useMemo(() => { if (action === CONST.IOU.ACTION.CATEGORIZE) { @@ -67,24 +75,24 @@ function IOURequestStepParticipants({ return translate('iou.chooseRecipient'); }, [iouType, translate, isSplitRequest, action]); - const selfDMReportID = useMemo(() => ReportUtils.findSelfDMReportID(), []); + const selfDMReportID = useMemo(() => findSelfDMReportID(), []); const [selfDMReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${selfDMReportID}`); const receiptFilename = transaction?.filename; const receiptPath = transaction?.receipt?.source; const receiptType = transaction?.receipt?.type; const isAndroidNative = getPlatform() === CONST.PLATFORM.ANDROID; - const isMobileSafari = Browser.isMobileSafari(); + const isMobileSafari = isMobileSafariUtil(); // When the component mounts, if there is a receipt, see if the image can be read from the disk. If not, redirect the user to the starting step of the flow. // This is because until the expense is saved, the receipt file is only stored in the browsers memory as a blob:// and if the browser is refreshed, then // the image ceases to exist. The best way for the user to recover from this is to start over from the start of the expense process. // skip this in case user is moving the transaction as the receipt path will be valid in that case useEffect(() => { - if (IOUUtils.isMovingTransactionFromTrackExpense(action)) { + if (isMovingTransactionFromTrackExpense(action)) { return; } - IOU.navigateToStartStepIfScanFileCannotBeRead(receiptFilename ?? '', receiptPath ?? '', () => {}, iouRequestType, iouType, transactionID, reportID, receiptType ?? ''); + navigateToStartStepIfScanFileCannotBeRead(receiptFilename ?? '', receiptPath ?? '', () => {}, iouRequestType, iouType, transactionID, reportID, receiptType ?? ''); }, [receiptType, receiptPath, receiptFilename, iouRequestType, iouType, transactionID, reportID, action]); const trackExpense = useCallback(() => { @@ -95,8 +103,8 @@ function IOURequestStepParticipants({ } const rateID = DistanceRequestUtils.getCustomUnitRateID(selfDMReportID); - IOU.setCustomUnitRateID(transactionID, rateID); - IOU.setMoneyRequestParticipantsFromReport(transactionID, selfDMReport); + setCustomUnitRateID(transactionID, rateID); + setMoneyRequestParticipantsFromReport(transactionID, selfDMReport); const iouConfirmationPageRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(action, CONST.IOU.TYPE.TRACK, transactionID, selfDMReportID); Navigation.navigate(iouConfirmationPageRoute); }, [action, selfDMReport, selfDMReportID, transactionID]); @@ -114,11 +122,11 @@ function IOURequestStepParticipants({ const firstParticipantReportID = val.at(0)?.reportID; const rateID = DistanceRequestUtils.getCustomUnitRateID(firstParticipantReportID); - const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && ReportUtils.isInvoiceRoomWithID(firstParticipantReportID); + const isInvoice = iouType === CONST.IOU.TYPE.INVOICE && isInvoiceRoomWithID(firstParticipantReportID); numberOfParticipants.current = val.length; - IOU.setMoneyRequestParticipants(transactionID, val); - IOU.setCustomUnitRateID(transactionID, rateID); + setMoneyRequestParticipants(transactionID, val); + setCustomUnitRateID(transactionID, rateID); // When multiple participants are selected, the reportID is generated at the end of the confirmation step. // So we are resetting selectedReportID ref to the reportID coming from params. @@ -153,13 +161,13 @@ function IOURequestStepParticipants({ const isPolicyExpenseChat = participants?.some((participant) => participant.isPolicyExpenseChat); if (iouType === CONST.IOU.TYPE.SPLIT && !isPolicyExpenseChat && transaction?.amount && transaction?.currency) { const participantAccountIDs = participants?.map((participant) => participant.accountID) as number[]; - IOU.setSplitShares(transaction, transaction.amount, transaction.currency, participantAccountIDs); + setSplitShares(transaction, transaction.amount, transaction.currency, participantAccountIDs); } - IOU.setMoneyRequestTag(transactionID, ''); - IOU.setMoneyRequestCategory(transactionID, ''); + setMoneyRequestTag(transactionID, ''); + setMoneyRequestCategory(transactionID, ''); if ((isCategorizing || isShareAction) && numberOfParticipants.current === 0) { - ReportUtils.createDraftWorkspaceAndNavigateToConfirmationScreen(transactionID, action); + createDraftWorkspaceAndNavigateToConfirmationScreen(transactionID, action); return; } @@ -180,14 +188,14 @@ function IOURequestStepParticipants({ }, [action, participants, iouType, transaction, transactionID, reportID, handleNavigation]); const navigateBack = useCallback(() => { - IOUUtils.navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID, action); + navigateToStartMoneyRequestStep(iouRequestType, iouType, transactionID, reportID, action); }, [iouRequestType, iouType, transactionID, reportID, action]); useEffect(() => { const isCategorizing = action === CONST.IOU.ACTION.CATEGORIZE; const isShareAction = action === CONST.IOU.ACTION.SHARE; if (isFocused && (isCategorizing || isShareAction)) { - IOU.setMoneyRequestParticipants(transactionID, []); + setMoneyRequestParticipants(transactionID, []); numberOfParticipants.current = 0; } }, [isFocused, action, transactionID]); From 4fd9f58f72153a546e452b23f0ca7b45f4204887 Mon Sep 17 00:00:00 2001 From: Julian Kobrynski Date: Mon, 20 Jan 2025 16:13:28 +0100 Subject: [PATCH 6/6] modify trackScan translation --- src/languages/en.ts | 2 +- src/languages/es.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 2ee6555dcbb3..3cf649d5792c 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -847,7 +847,7 @@ const translations = { assignTask: 'Assign task', header: 'Quick action', trackManual: 'Create expense', - trackScan: 'Create expense for receipt', + trackScan: 'Scan receipt', trackDistance: 'Track distance', noLongerHaveReportAccess: 'You no longer have access to your previous quick action destination. Pick a new one below.', updateDestination: 'Update destination', diff --git a/src/languages/es.ts b/src/languages/es.ts index 48afdf3bf4a9..500ed42526bd 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -842,7 +842,7 @@ const translations = { assignTask: 'Assignar tarea', header: 'Acción rápida', trackManual: 'Crear gasto', - trackScan: 'Crear gasto por recibo', + trackScan: 'Escanear recibo', trackDistance: 'Crear gasto por desplazamiento', noLongerHaveReportAccess: 'Ya no tienes acceso al destino previo de esta acción rápida. Escoge uno nuevo a continuación.', updateDestination: 'Actualiza el destino',