From 543ed1de369238b4a2847f20bdaa41d40603654a Mon Sep 17 00:00:00 2001 From: Olgierd Date: Thu, 25 Sep 2025 19:19:06 +0200 Subject: [PATCH 01/32] Removal of backTo --- src/ROUTES.ts | 41 +++++++++++++++---- src/SCREENS.ts | 7 ++++ src/components/SettlementButton/index.tsx | 34 ++++++++++++++- .../ModalStackNavigators/index.tsx | 11 +++++ .../Navigators/RightModalNavigator.tsx | 4 ++ src/libs/Navigation/linkingConfig/config.ts | 10 +++++ src/libs/Navigation/types.ts | 26 ++++++++++++ ...rchMoneyRequestReportVerifyAccountPage.tsx | 14 +++++++ .../Search/SearchReportVerifyAccountPage.tsx | 14 +++++++ .../Search/SearchRootVerifyAccountPage.tsx | 9 ++++ .../home/report/ReportVerifyAccountPage.tsx | 14 +++++++ .../MoneyRequestCreateVerifyAccountPage.tsx | 14 +++++++ ...questStepConfirmationVerifyAccountPage.tsx | 18 ++++++++ 13 files changed, 207 insertions(+), 9 deletions(-) create mode 100644 src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx create mode 100644 src/pages/Search/SearchReportVerifyAccountPage.tsx create mode 100644 src/pages/Search/SearchRootVerifyAccountPage.tsx create mode 100644 src/pages/home/report/ReportVerifyAccountPage.tsx create mode 100644 src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx create mode 100644 src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index fed1194852e3a..b8bb5f1892ef0 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -60,6 +60,7 @@ const ROUTES = { return `search?q=${encodeURIComponent(query)}${name ? `&name=${name}` : ''}` as const; }, }, + SEARCH_ROOT_VERIFY_ACCOUNT: `search/${VERIFY_ACCOUNT}`, SEARCH_SAVED_SEARCH_RENAME: { route: 'search/saved-search/rename', getRoute: ({name, jsonQuery}: {name: string; jsonQuery: SearchQueryString}) => `search/saved-search/rename?name=${name}&q=${jsonQuery}` as const, @@ -83,6 +84,10 @@ const ROUTES = { return getUrlWithBackToParam(baseRoute, backTo); }, }, + SEARCH_REPORT_VERIFY_ACCOUNT: { + route: `search/view/:reportID/${VERIFY_ACCOUNT}`, + getRoute: (reportID: string) => `search/view/${reportID}/${VERIFY_ACCOUNT}`, + }, SEARCH_MONEY_REQUEST_REPORT: { route: 'search/r/:reportID', getRoute: ({reportID, backTo}: {reportID: string; backTo?: string}) => { @@ -92,6 +97,10 @@ const ROUTES = { return getUrlWithBackToParam(baseRoute, backTo); }, }, + SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT: { + route: `search/r/:reportID/${VERIFY_ACCOUNT}`, + getRoute: (reportID: string) => `search/r/${reportID}/${VERIFY_ACCOUNT}`, + }, SEARCH_MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS: { route: 'search/r/:reportID/hold', getRoute: ({reportID, backTo}: {reportID: string; backTo?: string}) => { @@ -493,6 +502,10 @@ const ROUTES = { return getUrlWithBackToParam(`r/${reportID}/details/shareCode` as const, backTo); }, }, + REPORT_VERIFY_ACCOUNT: { + route: `r/:reportID/${VERIFY_ACCOUNT}`, + getRoute: (reportID: string) => `r/${reportID}/${VERIFY_ACCOUNT}`, + }, REPORT_PARTICIPANTS: { route: 'r/:reportID/participants', @@ -691,8 +704,15 @@ const ROUTES = { }, MONEY_REQUEST_CREATE: { route: ':action/:iouType/start/:transactionID/:reportID/:backToReport?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string) => - `${action as string}/${iouType as string}/start/${transactionID}/${reportID}/${backToReport ?? ''}` as const, + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string) => { + const optionalRoutePart = backToReport !== undefined ? `/${backToReport}` : ''; + return `${action as string}/${iouType as string}/start/${transactionID}/${reportID}${optionalRoutePart}` as const; + }, + }, + MONEY_REQUEST_CREATE_VERIFY_ACCOUNT: { + route: `:action/:iouType/start/:transactionID/:reportID/${VERIFY_ACCOUNT}`, + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => + `${action as string}/${iouType as string}/start/${transactionID}/${reportID}/${VERIFY_ACCOUNT}`, }, MONEY_REQUEST_STEP_SEND_FROM: { route: 'create/:iouType/from/:transactionID/:reportID', @@ -708,12 +728,19 @@ const ROUTES = { }, MONEY_REQUEST_STEP_CONFIRMATION: { route: ':action/:iouType/confirmation/:transactionID/:reportID/:backToReport?', - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string | undefined, backToReport?: string, participantsAutoAssigned?: boolean, backTo?: string) => + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string | undefined, backToReport?: string, participantsAutoAssigned?: boolean, backTo?: string) => { + let optionalRoutePart = ''; + if (backToReport !== undefined || participantsAutoAssigned !== undefined) { + optionalRoutePart = `/${backToReport ?? ''}${participantsAutoAssigned ? '?participantsAutoAssigned=true' : ''}`; + } // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - getUrlWithBackToParam( - `${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}/${backToReport ?? ''}${participantsAutoAssigned ? '?participantsAutoAssigned=true' : ''}`, - backTo, - ), + return getUrlWithBackToParam(`${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}${optionalRoutePart}`, backTo); + }, + }, + MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT: { + route: `:action/:iouType/confirmation/:transactionID/:reportID/${VERIFY_ACCOUNT}`, + getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => + `${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}/${VERIFY_ACCOUNT}`, }, MONEY_REQUEST_STEP_AMOUNT: { route: ':action/:iouType/amount/:transactionID/:reportID/:reportActionID?/:pageIndex?/:backToReport?', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index b4baa0ef4ab97..fd08bc3a54e8f 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -40,9 +40,12 @@ const SCREENS = { }, SEARCH: { ROOT: 'Search_Root', + ROOT_VERIFY_ACCOUNT: 'Search_Root_Verify_Account', MONEY_REQUEST_REPORT: 'Search_Money_Request_Report', + MONEY_REQUEST_REPORT_VERIFY_ACCOUNT: 'Search_Money_Request_Report_Verify_Account', MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS: 'Search_Money_Request_Report_Hold_Transactions', REPORT_RHP: 'Search_Report_RHP', + REPORT_VERIFY_ACCOUNT: 'Search_Report_Verify_Account', ADVANCED_FILTERS_RHP: 'Search_Advanced_Filters_RHP', ADVANCED_FILTERS_TYPE_RHP: 'Search_Advanced_Filters_Type_RHP', ADVANCED_FILTERS_GROUP_BY_RHP: 'Search_Advanced_Filters_GroupBy_RHP', @@ -245,6 +248,7 @@ const SCREENS = { ADD_UNREPORTED_EXPENSE: 'AddUnreportedExpense', SCHEDULE_CALL: 'ScheduleCall', REPORT_CHANGE_APPROVER: 'Report_Change_Approver', + REPORT_VERIFY_ACCOUNT: 'Report_Verify_Account', MERGE_TRANSACTION: 'MergeTransaction', }, PUBLIC_CONSOLE_DEBUG: 'Console_Debug', @@ -257,9 +261,11 @@ const SCREENS = { MONEY_REQUEST: { CREATE: 'Money_Request_Create', + CREATE_VERIFY_ACCOUNT: 'Create_Verify_Account', HOLD: 'Money_Request_Hold_Reason', REJECT: 'Money_Request_Reject_Reason', STEP_CONFIRMATION: 'Money_Request_Step_Confirmation', + STEP_CONFIRMATION_VERIFY_ACCOUNT: 'Money_Request_Step_Confirmation_Verify_Account', START: 'Money_Request_Start', STEP_UPGRADE: 'Money_Request_Step_Upgrade', STEP_AMOUNT: 'Money_Request_Step_Amount', @@ -763,6 +769,7 @@ const SCREENS = { REIMBURSEMENT_ACCOUNT: 'ReimbursementAccount', REIMBURSEMENT_ACCOUNT_ENTER_SIGNER_INFO: 'Reimbursement_Account_Signer_Info', REFERRAL_DETAILS: 'Referral_Details', + REPORT_VERIFY_ACCOUNT: 'Report_Verify_Account', KEYBOARD_SHORTCUTS: 'KeyboardShortcuts', SHARE: { ROOT: 'Share_Root', diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index bd5d6125bf27a..a6e0184b09e6a 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -375,8 +375,38 @@ function SettlementButton({ const selectPaymentMethod = (event: KYCFlowEvent, triggerKYCFlow: TriggerKYCFlow, paymentMethod?: PaymentMethod, selectedPolicy?: Policy) => { if (!isUserValidated) { - Navigation.navigate(ROUTES.SETTINGS_CONTACT_METHOD_VERIFY_ACCOUNT.getRoute(Navigation.getActiveRoute())); - return; + const activeRoute = Navigation.getActiveRoute(); + + if (activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''}))) { + Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT); + return; + } + if (reportID && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID}))) { + Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + return; + } + if (reportID && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID}))) { + Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + return; + } + if (activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID))) { + Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)); + return; + } + if (reportID && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID))) { + Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + return; + } + if (activeRoute.includes(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID))) { + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), + ); + return; + } + if (activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID))) { + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)); + return; + } } if (policy && shouldRestrictUserBillableActions(policy.id)) { diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 46bbfe11fd520..e20f7d3b5b7c3 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -32,6 +32,7 @@ import type { ReportDescriptionNavigatorParamList, ReportDetailsNavigatorParamList, ReportSettingsNavigatorParamList, + ReportVerifyAccountNavigatorParamList, RoomMembersNavigatorParamList, ScheduleCallParamList, SearchAdvancedFiltersParamList, @@ -136,7 +137,9 @@ function createModalStackNavigator(screens: Scr const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.MONEY_REQUEST.START]: () => require('../../../../pages/iou/request/IOURequestRedirectToStartPage').default, [SCREENS.MONEY_REQUEST.CREATE]: () => require('../../../../pages/iou/request/IOURequestStartPage').default, + [SCREENS.MONEY_REQUEST.CREATE_VERIFY_ACCOUNT]: () => require('../../../../pages/iou/request/MoneyRequestCreateVerifyAccountPage').default, [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: () => require('../../../../pages/iou/request/step/IOURequestStepConfirmation').default, + [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION_VERIFY_ACCOUNT]: () => require('../../../../pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage').default, [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: () => require('../../../../pages/iou/request/step/IOURequestStepAmount').default, [SCREENS.MONEY_REQUEST.STEP_TAX_AMOUNT]: () => require('../../../../pages/iou/request/step/IOURequestStepTaxAmountPage').default, [SCREENS.MONEY_REQUEST.STEP_TAX_RATE]: () => require('../../../../pages/iou/request/step/IOURequestStepTaxRatePage').default, @@ -244,6 +247,10 @@ const TaskModalStackNavigator = createModalStackNavigator require('../../../../pages/tasks/TaskAssigneeSelectorModal').default, }); +const ReportVerifyAccountModalStackNavigator = createModalStackNavigator({ + [SCREENS.REPORT_VERIFY_ACCOUNT]: () => require('../../../../pages/home/report/ReportVerifyAccountPage').default, +}); + const ReportDescriptionModalStackNavigator = createModalStackNavigator({ [SCREENS.REPORT_DESCRIPTION_ROOT]: () => require('../../../../pages/ReportDescriptionPage').default, }); @@ -805,6 +812,9 @@ const MergeTransactionStackNavigator = createModalStackNavigator({ [SCREENS.SEARCH.REPORT_RHP]: () => require('../../../../pages/home/ReportScreen').default, + [SCREENS.SEARCH.ROOT_VERIFY_ACCOUNT]: () => require('../../../../pages/Search/SearchRootVerifyAccountPage').default, + [SCREENS.SEARCH.REPORT_VERIFY_ACCOUNT]: () => require('../../../../pages/Search/SearchReportVerifyAccountPage').default, + [SCREENS.SEARCH.MONEY_REQUEST_REPORT_VERIFY_ACCOUNT]: () => require('../../../../pages/Search/SearchMoneyRequestReportVerifyAccountPage').default, [SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS]: () => require('../../../../pages/Search/SearchHoldReasonPage').default, [SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: () => require('../../../../pages/Search/SearchHoldReasonPage').default, [SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP]: () => require('../../../../pages/Search/SearchTransactionsChangeReport').default, @@ -921,6 +931,7 @@ export { DomainCardModalStackNavigator, SplitDetailsModalStackNavigator, TaskModalStackNavigator, + ReportVerifyAccountModalStackNavigator, WalletStatementStackNavigator, TransactionDuplicateStackNavigator, SearchReportModalStackNavigator, diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index f4a7a441a62f9..5336a9c473635 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -125,6 +125,10 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) { name={SCREENS.RIGHT_MODAL.REPORT_DESCRIPTION} component={ModalStackNavigators.ReportDescriptionModalStackNavigator} /> + ['config'] = { }, }, }, + [SCREENS.MONEY_REQUEST.CREATE_VERIFY_ACCOUNT]: ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.route, [SCREENS.MONEY_REQUEST.DISTANCE_CREATE]: { path: ROUTES.DISTANCE_REQUEST_CREATE.route, exact: true, @@ -1345,6 +1346,7 @@ const config: LinkingOptions['config'] = { [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: ROUTES.MONEY_REQUEST_STEP_AMOUNT.route, [SCREENS.MONEY_REQUEST.STEP_CATEGORY]: ROUTES.MONEY_REQUEST_STEP_CATEGORY.route, [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.route, + [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION_VERIFY_ACCOUNT]: ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.route, [SCREENS.MONEY_REQUEST.STEP_CURRENCY]: ROUTES.MONEY_REQUEST_STEP_CURRENCY.route, [SCREENS.MONEY_REQUEST.STEP_DATE]: ROUTES.MONEY_REQUEST_STEP_DATE.route, [SCREENS.MONEY_REQUEST.STEP_DESCRIPTION]: ROUTES.MONEY_REQUEST_STEP_DESCRIPTION.route, @@ -1484,6 +1486,11 @@ const config: LinkingOptions['config'] = { [SCREENS.REFERRAL_DETAILS]: ROUTES.REFERRAL_DETAILS_MODAL.route, }, }, + [SCREENS.RIGHT_MODAL.REPORT_VERIFY_ACCOUNT]: { + screens: { + [SCREENS.REPORT_VERIFY_ACCOUNT]: ROUTES.REPORT_VERIFY_ACCOUNT.route, + }, + }, [SCREENS.RIGHT_MODAL.TRAVEL]: { screens: { [SCREENS.TRAVEL.MY_TRIPS]: ROUTES.TRAVEL_MY_TRIPS, @@ -1506,7 +1513,10 @@ const config: LinkingOptions['config'] = { }, [SCREENS.RIGHT_MODAL.SEARCH_REPORT]: { screens: { + [SCREENS.SEARCH.ROOT_VERIFY_ACCOUNT]: ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT, [SCREENS.SEARCH.REPORT_RHP]: ROUTES.SEARCH_REPORT.route, + [SCREENS.SEARCH.REPORT_VERIFY_ACCOUNT]: ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.route, + [SCREENS.SEARCH.MONEY_REQUEST_REPORT_VERIFY_ACCOUNT]: ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.route, [SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS]: ROUTES.SEARCH_MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS.route, [SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: ROUTES.TRANSACTION_HOLD_REASON_RHP, [SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP]: ROUTES.MOVE_TRANSACTIONS_SEARCH_RHP, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index c431b82e8eedb..40642af357c06 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -81,6 +81,12 @@ type ConsoleNavigatorParamList = { }; }; +type ReportVerifyAccountNavigatorParamList = { + [SCREENS.REPORT_VERIFY_ACCOUNT]: { + reportID: string; + }; +}; + type SettingsNavigatorParamList = { [SCREENS.SETTINGS.SHARE_CODE]: undefined; [SCREENS.SETTINGS.PROFILE.PRONOUNS]: undefined; @@ -1445,6 +1451,12 @@ type MoneyRequestNavigatorParamList = { backToReport?: string; reportActionID?: string; }; + [SCREENS.MONEY_REQUEST.CREATE_VERIFY_ACCOUNT]: { + action: IOUAction; + iouType: IOUType; + transactionID: string; + reportID: string; + }; [SCREENS.MONEY_REQUEST.START]: { iouType: IOUType; reportID: string; @@ -1480,6 +1492,12 @@ type MoneyRequestNavigatorParamList = { participantsAutoAssigned?: string; backToReport?: string; }; + [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION_VERIFY_ACCOUNT]: { + action: IOUAction; + iouType: IOUType; + transactionID: string; + reportID: string; + }; [SCREENS.MONEY_REQUEST.STEP_SCAN]: { action: IOUAction; iouType: IOUType; @@ -1812,6 +1830,7 @@ type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.DETAILS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.PROFILE]: NavigatorScreenParams; [SCREENS.SETTINGS.SHARE_CODE]: undefined; + [SCREENS.RIGHT_MODAL.REPORT_VERIFY_ACCOUNT]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.NEW_REPORT_WORKSPACE_SELECTION]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.REPORT_DETAILS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.REPORT_CHANGE_WORKSPACE]: NavigatorScreenParams; @@ -2249,6 +2268,12 @@ type SearchReportParamList = { reportActionID?: string; backTo?: Routes; }; + [SCREENS.SEARCH.REPORT_VERIFY_ACCOUNT]: { + reportID: string; + }; + [SCREENS.SEARCH.MONEY_REQUEST_REPORT_VERIFY_ACCOUNT]: { + reportID: string; + }; [SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP]: { /** ID of the transaction the page was opened for */ transactionID: string; @@ -2433,6 +2458,7 @@ export type { ProfileNavigatorParamList, PublicScreensParamList, ReferralDetailsNavigatorParamList, + ReportVerifyAccountNavigatorParamList, ReimbursementAccountNavigatorParamList, ReimbursementAccountEnterSignerInfoNavigatorParamList, NewReportWorkspaceSelectionNavigatorParamList, diff --git a/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx b/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx new file mode 100644 index 0000000000000..965e6d2a1b364 --- /dev/null +++ b/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SearchReportParamList} from '@libs/Navigation/types'; +import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type SearchMoneyRequestReportVerifyAccountPageParamList = PlatformStackScreenProps; + +function SearchMoneyRequestReportVerifyAccountPage({route}: SearchMoneyRequestReportVerifyAccountPageParamList) { + return ; +} + +export default SearchMoneyRequestReportVerifyAccountPage; diff --git a/src/pages/Search/SearchReportVerifyAccountPage.tsx b/src/pages/Search/SearchReportVerifyAccountPage.tsx new file mode 100644 index 0000000000000..2150f37ae25f1 --- /dev/null +++ b/src/pages/Search/SearchReportVerifyAccountPage.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {SearchReportParamList} from '@libs/Navigation/types'; +import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type SearchReportVerifyAccountPageParamList = PlatformStackScreenProps; + +function SearchReportVerifyAccountPage({route}: SearchReportVerifyAccountPageParamList) { + return ; +} + +export default SearchReportVerifyAccountPage; diff --git a/src/pages/Search/SearchRootVerifyAccountPage.tsx b/src/pages/Search/SearchRootVerifyAccountPage.tsx new file mode 100644 index 0000000000000..9b6fa120d4a5a --- /dev/null +++ b/src/pages/Search/SearchRootVerifyAccountPage.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; +import ROUTES from '@src/ROUTES'; + +function SearchRootVerifyAccountPage() { + return ; +} + +export default SearchRootVerifyAccountPage; diff --git a/src/pages/home/report/ReportVerifyAccountPage.tsx b/src/pages/home/report/ReportVerifyAccountPage.tsx new file mode 100644 index 0000000000000..a905068635389 --- /dev/null +++ b/src/pages/home/report/ReportVerifyAccountPage.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {ReportVerifyAccountNavigatorParamList} from '@libs/Navigation/types'; +import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type ReportVerifyAccountPageProps = PlatformStackScreenProps; + +function ReportVerifyAccountPage({route}: ReportVerifyAccountPageProps) { + return ; +} + +export default ReportVerifyAccountPage; diff --git a/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx b/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx new file mode 100644 index 0000000000000..9b4f66147306c --- /dev/null +++ b/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types'; +import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type MoneyRequestCreateVerifyAccountPageParamList = PlatformStackScreenProps; + +function MoneyRequestCreateVerifyAccountPage({route}: MoneyRequestCreateVerifyAccountPageParamList) { + return ; +} + +export default MoneyRequestCreateVerifyAccountPage; diff --git a/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx b/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx new file mode 100644 index 0000000000000..69c650c83bff7 --- /dev/null +++ b/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types'; +import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; + +type MoneyRequestStepConfirmationVerifyAccountPageParamList = PlatformStackScreenProps; + +function MoneyRequestStepConfirmationVerifyAccountPage({route}: MoneyRequestStepConfirmationVerifyAccountPageParamList) { + return ( + + ); +} + +export default MoneyRequestStepConfirmationVerifyAccountPage; From 0c68d1ce7e4d16abfeb6dd2d1227db391106277e Mon Sep 17 00:00:00 2001 From: Olgierd Date: Fri, 26 Sep 2025 13:41:53 +0200 Subject: [PATCH 02/32] Fix of RoutesValidationError in ROUTES --- src/ROUTES.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b8bb5f1892ef0..06a1ec3d87a69 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -60,7 +60,7 @@ const ROUTES = { return `search?q=${encodeURIComponent(query)}${name ? `&name=${name}` : ''}` as const; }, }, - SEARCH_ROOT_VERIFY_ACCOUNT: `search/${VERIFY_ACCOUNT}`, + SEARCH_ROOT_VERIFY_ACCOUNT: `search/${VERIFY_ACCOUNT}` as const, SEARCH_SAVED_SEARCH_RENAME: { route: 'search/saved-search/rename', getRoute: ({name, jsonQuery}: {name: string; jsonQuery: SearchQueryString}) => `search/saved-search/rename?name=${name}&q=${jsonQuery}` as const, @@ -86,7 +86,7 @@ const ROUTES = { }, SEARCH_REPORT_VERIFY_ACCOUNT: { route: `search/view/:reportID/${VERIFY_ACCOUNT}`, - getRoute: (reportID: string) => `search/view/${reportID}/${VERIFY_ACCOUNT}`, + getRoute: (reportID: string) => `search/view/${reportID}/${VERIFY_ACCOUNT}` as const, }, SEARCH_MONEY_REQUEST_REPORT: { route: 'search/r/:reportID', @@ -99,7 +99,7 @@ const ROUTES = { }, SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT: { route: `search/r/:reportID/${VERIFY_ACCOUNT}`, - getRoute: (reportID: string) => `search/r/${reportID}/${VERIFY_ACCOUNT}`, + getRoute: (reportID: string) => `search/r/${reportID}/${VERIFY_ACCOUNT}` as const, }, SEARCH_MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS: { route: 'search/r/:reportID/hold', @@ -504,7 +504,7 @@ const ROUTES = { }, REPORT_VERIFY_ACCOUNT: { route: `r/:reportID/${VERIFY_ACCOUNT}`, - getRoute: (reportID: string) => `r/${reportID}/${VERIFY_ACCOUNT}`, + getRoute: (reportID: string) => `r/${reportID}/${VERIFY_ACCOUNT}` as const, }, REPORT_PARTICIPANTS: { route: 'r/:reportID/participants', @@ -712,7 +712,7 @@ const ROUTES = { MONEY_REQUEST_CREATE_VERIFY_ACCOUNT: { route: `:action/:iouType/start/:transactionID/:reportID/${VERIFY_ACCOUNT}`, getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => - `${action as string}/${iouType as string}/start/${transactionID}/${reportID}/${VERIFY_ACCOUNT}`, + `${action as string}/${iouType as string}/start/${transactionID}/${reportID}/${VERIFY_ACCOUNT}` as const, }, MONEY_REQUEST_STEP_SEND_FROM: { route: 'create/:iouType/from/:transactionID/:reportID', @@ -734,13 +734,13 @@ const ROUTES = { optionalRoutePart = `/${backToReport ?? ''}${participantsAutoAssigned ? '?participantsAutoAssigned=true' : ''}`; } // eslint-disable-next-line no-restricted-syntax -- Legacy route generation - return getUrlWithBackToParam(`${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}${optionalRoutePart}`, backTo); + return getUrlWithBackToParam(`${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}${optionalRoutePart}` as const, backTo); }, }, MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT: { route: `:action/:iouType/confirmation/:transactionID/:reportID/${VERIFY_ACCOUNT}`, getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => - `${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}/${VERIFY_ACCOUNT}`, + `${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}/${VERIFY_ACCOUNT}` as const, }, MONEY_REQUEST_STEP_AMOUNT: { route: ':action/:iouType/amount/:transactionID/:reportID/:reportActionID?/:pageIndex?/:backToReport?', From 56bbf8430921e960d612517bbd99b8b98dc96aab Mon Sep 17 00:00:00 2001 From: Olgierd Date: Fri, 26 Sep 2025 16:46:53 +0200 Subject: [PATCH 03/32] Fix backTo type error in SearchHoldReasonPage --- src/pages/Search/SearchHoldReasonPage.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/Search/SearchHoldReasonPage.tsx b/src/pages/Search/SearchHoldReasonPage.tsx index 8f483429c35e3..df244d25c5ecd 100644 --- a/src/pages/Search/SearchHoldReasonPage.tsx +++ b/src/pages/Search/SearchHoldReasonPage.tsx @@ -15,7 +15,9 @@ import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/MoneyRequestHoldReasonForm'; -function SearchHoldReasonPage({route}: PlatformStackScreenProps>) { +function SearchHoldReasonPage({ + route, +}: PlatformStackScreenProps) { const {translate} = useLocalize(); const {backTo = '', reportID} = route.params ?? {}; const context = useSearchContext(); From 96cfe3f98871bb42d6f011c95378f6dd781ec797 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Mon, 29 Sep 2025 17:49:14 +0200 Subject: [PATCH 04/32] Cleaned getRoutes code --- src/ROUTES.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 06a1ec3d87a69..a7937cf92279e 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -705,8 +705,10 @@ const ROUTES = { MONEY_REQUEST_CREATE: { route: ':action/:iouType/start/:transactionID/:reportID/:backToReport?', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string, backToReport?: string) => { - const optionalRoutePart = backToReport !== undefined ? `/${backToReport}` : ''; - return `${action as string}/${iouType as string}/start/${transactionID}/${reportID}${optionalRoutePart}` as const; + if (backToReport) { + return `${action as string}/${iouType as string}/start/${transactionID}/${reportID}/${backToReport}` as const; + } + return `${action as string}/${iouType as string}/start/${transactionID}/${reportID}` as const; }, }, MONEY_REQUEST_CREATE_VERIFY_ACCOUNT: { @@ -730,8 +732,11 @@ const ROUTES = { route: ':action/:iouType/confirmation/:transactionID/:reportID/:backToReport?', getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string | undefined, backToReport?: string, participantsAutoAssigned?: boolean, backTo?: string) => { let optionalRoutePart = ''; - if (backToReport !== undefined || participantsAutoAssigned !== undefined) { - optionalRoutePart = `/${backToReport ?? ''}${participantsAutoAssigned ? '?participantsAutoAssigned=true' : ''}`; + if (backToReport !== undefined) { + optionalRoutePart += `/${backToReport}`; + } + if (participantsAutoAssigned !== undefined) { + optionalRoutePart += '?participantsAutoAssigned=true'; } // eslint-disable-next-line no-restricted-syntax -- Legacy route generation return getUrlWithBackToParam(`${action as string}/${iouType as string}/confirmation/${transactionID}/${reportID}${optionalRoutePart}` as const, backTo); From b2894ba3d060a7b9ca4023e7dc6c622adf233761 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Mon, 29 Sep 2025 18:37:37 +0200 Subject: [PATCH 05/32] Create SettlementButtonUtils to separate logic and reduce number of if statements --- src/components/SettlementButton/index.tsx | 35 ++----------- src/libs/SettlementButtonUtils.ts | 62 +++++++++++++++++++++++ 2 files changed, 65 insertions(+), 32 deletions(-) create mode 100644 src/libs/SettlementButtonUtils.ts diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index a6e0184b09e6a..3116700fb84c3 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -30,6 +30,7 @@ import { isInvoiceReport as isInvoiceReportUtil, isIOUReport, } from '@libs/ReportUtils'; +import handleRouteVerification from '@libs/SettlementButtonUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {setPersonalBankAccountContinueKYCOnSuccess} from '@userActions/BankAccounts'; import {approveMoneyRequest} from '@userActions/IOU'; @@ -375,38 +376,8 @@ function SettlementButton({ const selectPaymentMethod = (event: KYCFlowEvent, triggerKYCFlow: TriggerKYCFlow, paymentMethod?: PaymentMethod, selectedPolicy?: Policy) => { if (!isUserValidated) { - const activeRoute = Navigation.getActiveRoute(); - - if (activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''}))) { - Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT); - return; - } - if (reportID && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID}))) { - Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); - return; - } - if (reportID && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID}))) { - Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); - return; - } - if (activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID))) { - Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)); - return; - } - if (reportID && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID))) { - Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)); - return; - } - if (activeRoute.includes(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID))) { - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), - ); - return; - } - if (activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID))) { - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)); - return; - } + handleRouteVerification(reportID ?? '', chatReportID); + return; } if (policy && shouldRestrictUserBillableActions(policy.id)) { diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts new file mode 100644 index 0000000000000..6fb4797ed38b7 --- /dev/null +++ b/src/libs/SettlementButtonUtils.ts @@ -0,0 +1,62 @@ +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; +import Navigation from './Navigation/Navigation'; + +type RouteMapping = { + /** Condition that determines if this route mapping applies to the current active route */ + check: (activeRoute: string) => boolean; + + /** Navigates to the appropriate verification route when the check condition is met */ + navigate: () => void; +}; + +const handleRouteVerification = (reportID: string, chatReportID: string): boolean => { + const routeMappings: RouteMapping[] = [ + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), + navigate: () => Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT), + }, + { + check: (activeRoute: string) => !!(reportID && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID}))), + navigate: () => Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + }, + { + check: (activeRoute: string) => !!(reportID && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID}))), + navigate: () => Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + }, + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)), + navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)), + }, + { + check: (activeRoute: string) => !!(reportID && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID))), + navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + }, + { + check: (activeRoute: string) => + activeRoute.includes(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + navigate: () => + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), + ), + }, + { + check: (activeRoute: string) => + activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + navigate: () => + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + }, + ]; + + const activeRoute = Navigation.getActiveRoute(); + const matchedRoute = routeMappings.find((mapping) => mapping.check(activeRoute)); + + if (matchedRoute) { + matchedRoute.navigate(); + return true; + } + + return false; +}; + +export default handleRouteVerification; From 5155579e6a75c53a89a290a4b7cc74f47b304408 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 10:54:41 +0200 Subject: [PATCH 06/32] Warn about navigation fail --- src/libs/SettlementButtonUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index 6fb4797ed38b7..96b7ae12b3a7c 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -1,5 +1,6 @@ import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; +import Log from './Log'; import Navigation from './Navigation/Navigation'; type RouteMapping = { @@ -10,7 +11,7 @@ type RouteMapping = { navigate: () => void; }; -const handleRouteVerification = (reportID: string, chatReportID: string): boolean => { +const handleRouteVerification = (reportID: string, chatReportID: string) => { const routeMappings: RouteMapping[] = [ { check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), @@ -53,10 +54,9 @@ const handleRouteVerification = (reportID: string, chatReportID: string): boolea if (matchedRoute) { matchedRoute.navigate(); - return true; + } else { + Log.warn('Failed to navigate to the correct path'); } - - return false; }; export default handleRouteVerification; From 42a6ab99c0d23b1ebd332c9b78d5f5d8af3f0656 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 10:59:44 +0200 Subject: [PATCH 07/32] Add props type SearchHoldReasonPageProps --- src/pages/Search/SearchHoldReasonPage.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pages/Search/SearchHoldReasonPage.tsx b/src/pages/Search/SearchHoldReasonPage.tsx index df244d25c5ecd..e5392f2209d29 100644 --- a/src/pages/Search/SearchHoldReasonPage.tsx +++ b/src/pages/Search/SearchHoldReasonPage.tsx @@ -15,9 +15,12 @@ import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/MoneyRequestHoldReasonForm'; -function SearchHoldReasonPage({ - route, -}: PlatformStackScreenProps) { +type SearchHoldReasonPageProps = PlatformStackScreenProps< + SearchReportParamList, + typeof SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP | typeof SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS +>; + +function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) { const {translate} = useLocalize(); const {backTo = '', reportID} = route.params ?? {}; const context = useSearchContext(); From 2ed0b49be702824ccb1ca2fb19e85115f6293792 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 11:07:03 +0200 Subject: [PATCH 08/32] Name props type correctly --- .../Search/SearchMoneyRequestReportVerifyAccountPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx b/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx index 965e6d2a1b364..73defbf18072b 100644 --- a/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx @@ -5,9 +5,9 @@ import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -type SearchMoneyRequestReportVerifyAccountPageParamList = PlatformStackScreenProps; +type SearchMoneyRequestReportVerifyAccountPageProps = PlatformStackScreenProps; -function SearchMoneyRequestReportVerifyAccountPage({route}: SearchMoneyRequestReportVerifyAccountPageParamList) { +function SearchMoneyRequestReportVerifyAccountPage({route}: SearchMoneyRequestReportVerifyAccountPageProps) { return ; } From 7787465e6dc32e44a4dad86d6ac71b0dd4870e21 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 11:07:59 +0200 Subject: [PATCH 09/32] Name props type correctly --- src/pages/Search/SearchReportVerifyAccountPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Search/SearchReportVerifyAccountPage.tsx b/src/pages/Search/SearchReportVerifyAccountPage.tsx index 2150f37ae25f1..5c4eb6fc7e203 100644 --- a/src/pages/Search/SearchReportVerifyAccountPage.tsx +++ b/src/pages/Search/SearchReportVerifyAccountPage.tsx @@ -5,9 +5,9 @@ import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -type SearchReportVerifyAccountPageParamList = PlatformStackScreenProps; +type SearchReportVerifyAccountPageProps = PlatformStackScreenProps; -function SearchReportVerifyAccountPage({route}: SearchReportVerifyAccountPageParamList) { +function SearchReportVerifyAccountPage({route}: SearchReportVerifyAccountPageProps) { return ; } From 27bba3d57ff8b10bcc5fedd664386a0d7614d789 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 11:10:30 +0200 Subject: [PATCH 10/32] Name props type correctly --- src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx | 4 ++-- .../step/MoneyRequestStepConfirmationVerifyAccountPage.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx b/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx index 9b4f66147306c..e6b3e696a3e84 100644 --- a/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx +++ b/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx @@ -5,9 +5,9 @@ import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -type MoneyRequestCreateVerifyAccountPageParamList = PlatformStackScreenProps; +type MoneyRequestCreateVerifyAccountPageProps = PlatformStackScreenProps; -function MoneyRequestCreateVerifyAccountPage({route}: MoneyRequestCreateVerifyAccountPageParamList) { +function MoneyRequestCreateVerifyAccountPage({route}: MoneyRequestCreateVerifyAccountPageProps) { return ; } diff --git a/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx b/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx index 69c650c83bff7..178e4d147672c 100644 --- a/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx +++ b/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx @@ -5,9 +5,9 @@ import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -type MoneyRequestStepConfirmationVerifyAccountPageParamList = PlatformStackScreenProps; +type MoneyRequestStepConfirmationVerifyAccountPageProps = PlatformStackScreenProps; -function MoneyRequestStepConfirmationVerifyAccountPage({route}: MoneyRequestStepConfirmationVerifyAccountPageParamList) { +function MoneyRequestStepConfirmationVerifyAccountPage({route}: MoneyRequestStepConfirmationVerifyAccountPageProps) { return ( Date: Wed, 1 Oct 2025 11:20:06 +0200 Subject: [PATCH 11/32] Remove unnecessary as const --- src/ROUTES.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index a7937cf92279e..35e79cd1877a4 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -60,7 +60,7 @@ const ROUTES = { return `search?q=${encodeURIComponent(query)}${name ? `&name=${name}` : ''}` as const; }, }, - SEARCH_ROOT_VERIFY_ACCOUNT: `search/${VERIFY_ACCOUNT}` as const, + SEARCH_ROOT_VERIFY_ACCOUNT: `search/${VERIFY_ACCOUNT}`, SEARCH_SAVED_SEARCH_RENAME: { route: 'search/saved-search/rename', getRoute: ({name, jsonQuery}: {name: string; jsonQuery: SearchQueryString}) => `search/saved-search/rename?name=${name}&q=${jsonQuery}` as const, From 4dd0cf31562eda3320a570b53316ea79e7d0016c Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 11:43:07 +0200 Subject: [PATCH 12/32] Make string reportID to not have a default --- src/libs/SettlementButtonUtils.ts | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index 96b7ae12b3a7c..3c5c75472a8fa 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -11,27 +11,42 @@ type RouteMapping = { navigate: () => void; }; -const handleRouteVerification = (reportID: string, chatReportID: string) => { +const handleRouteVerification = (reportID: string | undefined, chatReportID: string) => { const routeMappings: RouteMapping[] = [ { check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), navigate: () => Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT), }, { - check: (activeRoute: string) => !!(reportID && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID}))), - navigate: () => Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID})), + navigate: () => { + if (reportID === undefined) { + return; + } + Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + }, }, { - check: (activeRoute: string) => !!(reportID && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID}))), - navigate: () => Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID})), + navigate: () => { + if (reportID === undefined) { + return; + } + Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + }, }, { check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)), navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)), }, { - check: (activeRoute: string) => !!(reportID && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID))), - navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID)), + navigate: () => { + if (reportID === undefined) { + return; + } + Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + }, }, { check: (activeRoute: string) => From dadcb86fb18c4d55ef67ca30fbe74ce19a956ae8 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 11:43:26 +0200 Subject: [PATCH 13/32] Make string reportID to not have a default --- src/components/SettlementButton/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 3116700fb84c3..e6335d53a2cff 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -376,7 +376,7 @@ function SettlementButton({ const selectPaymentMethod = (event: KYCFlowEvent, triggerKYCFlow: TriggerKYCFlow, paymentMethod?: PaymentMethod, selectedPolicy?: Policy) => { if (!isUserValidated) { - handleRouteVerification(reportID ?? '', chatReportID); + handleRouteVerification(reportID, chatReportID); return; } From 057d05720bd4bb797acb5481dc967290093c88eb Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 12:18:53 +0200 Subject: [PATCH 14/32] Rename function for better description --- src/components/SettlementButton/index.tsx | 4 ++-- src/libs/SettlementButtonUtils.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index e6335d53a2cff..f1924fd46ca93 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -30,7 +30,7 @@ import { isInvoiceReport as isInvoiceReportUtil, isIOUReport, } from '@libs/ReportUtils'; -import handleRouteVerification from '@libs/SettlementButtonUtils'; +import handleUnvalidatedUserNavigation from '@libs/SettlementButtonUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {setPersonalBankAccountContinueKYCOnSuccess} from '@userActions/BankAccounts'; import {approveMoneyRequest} from '@userActions/IOU'; @@ -376,7 +376,7 @@ function SettlementButton({ const selectPaymentMethod = (event: KYCFlowEvent, triggerKYCFlow: TriggerKYCFlow, paymentMethod?: PaymentMethod, selectedPolicy?: Policy) => { if (!isUserValidated) { - handleRouteVerification(reportID, chatReportID); + handleUnvalidatedUserNavigation(reportID, chatReportID); return; } diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index 3c5c75472a8fa..86137ae4fea92 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -11,7 +11,7 @@ type RouteMapping = { navigate: () => void; }; -const handleRouteVerification = (reportID: string | undefined, chatReportID: string) => { +const handleUnvalidatedUserNavigation = (reportID: string | undefined, chatReportID: string) => { const routeMappings: RouteMapping[] = [ { check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), @@ -74,4 +74,4 @@ const handleRouteVerification = (reportID: string | undefined, chatReportID: str } }; -export default handleRouteVerification; +export default handleUnvalidatedUserNavigation; From 0d2be778e9de55d6a2a40fec06259a0bf50f7bf9 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 1 Oct 2025 16:01:05 +0200 Subject: [PATCH 15/32] Extract RouteMappings logic from handleUnvalidatedUserNavigation --- src/libs/SettlementButtonUtils.ts | 104 +++++++++++++++--------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index 86137ae4fea92..f5e208e043518 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -11,67 +11,67 @@ type RouteMapping = { navigate: () => void; }; -const handleUnvalidatedUserNavigation = (reportID: string | undefined, chatReportID: string) => { - const routeMappings: RouteMapping[] = [ - { - check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), - navigate: () => Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT), - }, - { - check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID})), - navigate: () => { - if (reportID === undefined) { - return; - } - Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); - }, - }, - { - check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID})), - navigate: () => { - if (reportID === undefined) { - return; - } - Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); - }, +const getRouteMappings = (reportID: string | undefined, chatReportID: string): RouteMapping[] => [ + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), + navigate: () => Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT), + }, + { + check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID})), + navigate: () => { + if (reportID === undefined) { + return; + } + Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); }, - { - check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)), - navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)), + }, + { + check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID})), + navigate: () => { + if (reportID === undefined) { + return; + } + Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); }, - { - check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID)), - navigate: () => { - if (reportID === undefined) { - return; - } - Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)); - }, + }, + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)), + navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)), + }, + { + check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID)), + navigate: () => { + if (reportID === undefined) { + return; + } + Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)); }, - { - check: (activeRoute: string) => - activeRoute.includes(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - navigate: () => - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), - ), - }, - { - check: (activeRoute: string) => - activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - navigate: () => - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - }, - ]; + }, + { + check: (activeRoute: string) => + activeRoute.includes(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + navigate: () => + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), + ), + }, + { + check: (activeRoute: string) => + activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + navigate: () => + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + }, +]; +const handleUnvalidatedUserNavigation = (reportID: string | undefined, chatReportID: string) => { const activeRoute = Navigation.getActiveRoute(); - const matchedRoute = routeMappings.find((mapping) => mapping.check(activeRoute)); + const matchedRoute = getRouteMappings(reportID, chatReportID).find((mapping) => mapping.check(activeRoute)); if (matchedRoute) { matchedRoute.navigate(); - } else { - Log.warn('Failed to navigate to the correct path'); + return; } + Log.warn('Failed to navigate to the correct path'); }; export default handleUnvalidatedUserNavigation; From e0334b985cfb0f22d96d19a0bdf7d318f8020d12 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Thu, 2 Oct 2025 10:09:10 +0200 Subject: [PATCH 16/32] Add SettlementButtonUtils tests --- tests/unit/SettlementButtonUtilsTest.ts | 107 ++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 tests/unit/SettlementButtonUtilsTest.ts diff --git a/tests/unit/SettlementButtonUtilsTest.ts b/tests/unit/SettlementButtonUtilsTest.ts new file mode 100644 index 0000000000000..6008f41aefea2 --- /dev/null +++ b/tests/unit/SettlementButtonUtilsTest.ts @@ -0,0 +1,107 @@ +import Navigation from '@libs/Navigation/Navigation'; +import handleUnvalidatedUserNavigation from '@libs/SettlementButtonUtils'; +import CONST from '@src/CONST'; +import ROUTES from '@src/ROUTES'; + +jest.mock('@libs/Navigation/Navigation'); + +describe('SettlementButtonUtils', () => { + const mockReportID = '123456789'; + const mockChatReportID = '987654321'; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('navigate to ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_ROOT', () => { + const mockActiveRoute = ROUTES.SEARCH_ROOT.getRoute({query: ''}); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT); + }); + + it('navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_REPORT', () => { + const mockActiveRoute = ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); + }); + + it('do not navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when reportID is undefined', () => { + const mockActiveRoute = ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(undefined, mockChatReportID); + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); + + it('navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_MONEY_REQUEST_REPORT', () => { + const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); + }); + + it('do not navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when reportID is undefined', () => { + const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(undefined, mockChatReportID); + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); + + it('match ROUTES.SEARCH_MONEY_REQUEST_REPORT over ROUTES.REPORT_WITH_ID', () => { + // Should match the first applicable route when multiple conditions could match + const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledTimes(1); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); + expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); + }); + + it('navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(chatReportID)', () => { + const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockChatReportID); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockChatReportID)); + expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); + }); + + it('navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID)', () => { + const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockReportID); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); + expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockChatReportID)); + }); + + it('do not navigate when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID) and reportID is undefined', () => { + const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockReportID); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(undefined, mockChatReportID); + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); + + it('navigate to ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_STEP_CONFIRMATION', () => { + const mockActiveRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledWith( + ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), + ); + }); + + it('navigate to ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_CREATE', () => { + const mockActiveRoute = ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID); + (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).toHaveBeenCalledWith( + ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), + ); + }); + + it('when no route mapping matches, user should not be navigated', () => { + (Navigation.getActiveRoute as jest.Mock).mockReturnValue('/just/unmatched/route'); + handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + expect(Navigation.navigate).not.toHaveBeenCalled(); + }); +}); From c2a4d8168cc4b3fe7c0d57614e21b7a370bec1e5 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Thu, 2 Oct 2025 19:54:48 +0200 Subject: [PATCH 17/32] Divide route mappings from getRouteMappings into nonReportId and reportId mappings --- src/libs/SettlementButtonUtils.ts | 92 +++++++++++++++---------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index f5e208e043518..4f26acd412add 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -11,57 +11,53 @@ type RouteMapping = { navigate: () => void; }; -const getRouteMappings = (reportID: string | undefined, chatReportID: string): RouteMapping[] => [ - { - check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), - navigate: () => Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT), - }, - { - check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID})), - navigate: () => { - if (reportID === undefined) { - return; - } - Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); +const getRouteMappings = (reportID: string | undefined, chatReportID: string): RouteMapping[] => { + const nonReportIdRouteMappings = [ + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), + navigate: () => Navigation.navigate(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT), }, - }, - { - check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID})), - navigate: () => { - if (reportID === undefined) { - return; - } - Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + { + check: (activeRoute: string) => + activeRoute.includes(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + navigate: () => + Navigation.navigate( + ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), + ), }, - }, - { - check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)), - navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)), - }, - { - check: (activeRoute: string) => reportID !== undefined && activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID)), - navigate: () => { - if (reportID === undefined) { - return; - } - Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)); + { + check: (activeRoute: string) => + activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), + navigate: () => + Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), }, - }, - { - check: (activeRoute: string) => - activeRoute.includes(ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - navigate: () => - Navigation.navigate( - ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), - ), - }, - { - check: (activeRoute: string) => - activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - navigate: () => - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - }, -]; + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)), + navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)), + }, + ]; + + if (reportID === undefined) { + return nonReportIdRouteMappings; + } + + const reportIdRouteMappings = [ + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID})), + navigate: () => Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + }, + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_REPORT.getRoute({reportID})), + navigate: () => Navigation.navigate(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + }, + { + check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(reportID)), + navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID)), + }, + ]; + + return [...nonReportIdRouteMappings, ...reportIdRouteMappings]; +}; const handleUnvalidatedUserNavigation = (reportID: string | undefined, chatReportID: string) => { const activeRoute = Navigation.getActiveRoute(); From 93fae1b0630f9b19fec59527f837d51918cf75b1 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Thu, 2 Oct 2025 20:06:35 +0200 Subject: [PATCH 18/32] Move optional props to the end of both handleUnvalidatedUserNavigation and getRouteMappings function declarations --- src/components/SettlementButton/index.tsx | 2 +- src/libs/SettlementButtonUtils.ts | 6 +++--- tests/unit/SettlementButtonUtilsTest.ts | 24 +++++++++++------------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index fdc5c21490ce9..d78a5c2396b78 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -376,7 +376,7 @@ function SettlementButton({ const selectPaymentMethod = (event: KYCFlowEvent, triggerKYCFlow: TriggerKYCFlow, paymentMethod?: PaymentMethod, selectedPolicy?: Policy) => { if (!isUserValidated) { - handleUnvalidatedUserNavigation(reportID, chatReportID); + handleUnvalidatedUserNavigation(chatReportID, reportID); return; } diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index 4f26acd412add..9f2f59b37cb82 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -11,7 +11,7 @@ type RouteMapping = { navigate: () => void; }; -const getRouteMappings = (reportID: string | undefined, chatReportID: string): RouteMapping[] => { +const getRouteMappings = (chatReportID: string, reportID?: string): RouteMapping[] => { const nonReportIdRouteMappings = [ { check: (activeRoute: string) => activeRoute.includes(ROUTES.SEARCH_ROOT.getRoute({query: ''})), @@ -59,9 +59,9 @@ const getRouteMappings = (reportID: string | undefined, chatReportID: string): R return [...nonReportIdRouteMappings, ...reportIdRouteMappings]; }; -const handleUnvalidatedUserNavigation = (reportID: string | undefined, chatReportID: string) => { +const handleUnvalidatedUserNavigation = (chatReportID: string, reportID?: string) => { const activeRoute = Navigation.getActiveRoute(); - const matchedRoute = getRouteMappings(reportID, chatReportID).find((mapping) => mapping.check(activeRoute)); + const matchedRoute = getRouteMappings(chatReportID, reportID).find((mapping) => mapping.check(activeRoute)); if (matchedRoute) { matchedRoute.navigate(); diff --git a/tests/unit/SettlementButtonUtilsTest.ts b/tests/unit/SettlementButtonUtilsTest.ts index 6008f41aefea2..c4aca575b9723 100644 --- a/tests/unit/SettlementButtonUtilsTest.ts +++ b/tests/unit/SettlementButtonUtilsTest.ts @@ -16,35 +16,35 @@ describe('SettlementButtonUtils', () => { it('navigate to ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_ROOT', () => { const mockActiveRoute = ROUTES.SEARCH_ROOT.getRoute({query: ''}); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT); }); it('navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_REPORT', () => { const mockActiveRoute = ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); }); it('do not navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when reportID is undefined', () => { const mockActiveRoute = ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(undefined, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID); expect(Navigation.navigate).not.toHaveBeenCalled(); }); it('navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_MONEY_REQUEST_REPORT', () => { const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); }); it('do not navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when reportID is undefined', () => { const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(undefined, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID); expect(Navigation.navigate).not.toHaveBeenCalled(); }); @@ -52,7 +52,7 @@ describe('SettlementButtonUtils', () => { // Should match the first applicable route when multiple conditions could match const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledTimes(1); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); @@ -61,7 +61,7 @@ describe('SettlementButtonUtils', () => { it('navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(chatReportID)', () => { const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockChatReportID); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockChatReportID)); expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); }); @@ -69,7 +69,7 @@ describe('SettlementButtonUtils', () => { it('navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID)', () => { const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockReportID); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockChatReportID)); }); @@ -77,14 +77,14 @@ describe('SettlementButtonUtils', () => { it('do not navigate when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID) and reportID is undefined', () => { const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockReportID); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(undefined, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID); expect(Navigation.navigate).not.toHaveBeenCalled(); }); it('navigate to ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_STEP_CONFIRMATION', () => { const mockActiveRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledWith( ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), ); @@ -93,7 +93,7 @@ describe('SettlementButtonUtils', () => { it('navigate to ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_CREATE', () => { const mockActiveRoute = ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).toHaveBeenCalledWith( ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), ); @@ -101,7 +101,7 @@ describe('SettlementButtonUtils', () => { it('when no route mapping matches, user should not be navigated', () => { (Navigation.getActiveRoute as jest.Mock).mockReturnValue('/just/unmatched/route'); - handleUnvalidatedUserNavigation(mockReportID, mockChatReportID); + handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); expect(Navigation.navigate).not.toHaveBeenCalled(); }); }); From a907e4f3babf1cbf9c1215d71e0ce62d160d3aa7 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Fri, 3 Oct 2025 14:05:15 +0200 Subject: [PATCH 19/32] Add a jsdoc comment for both getRouteMappings and handleUnvalidatedUserNavigation functions from SettlementButtonUtils --- src/libs/SettlementButtonUtils.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index 9f2f59b37cb82..1896267a9bfc7 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -11,6 +11,13 @@ type RouteMapping = { navigate: () => void; }; +/** + * Retrieves an array of available RouteMappings for an unvalidated user. + * Each mapping contains a `check` function that determines whether the activeRoute matches the given mapping and a `navigate` function that executes navigation to the corresponding route. + * @param chatReportID - The chat or workspace ID from which the unvalidated user makes a payment via SettlementButton + * @param reportID - The expense report ID that the user pays using SettlementButton (optional) + * @return An array of available RouteMappings suitable for an unvalidated user + */ const getRouteMappings = (chatReportID: string, reportID?: string): RouteMapping[] => { const nonReportIdRouteMappings = [ { @@ -59,6 +66,9 @@ const getRouteMappings = (chatReportID: string, reportID?: string): RouteMapping return [...nonReportIdRouteMappings, ...reportIdRouteMappings]; }; +/** + * Handles SettlementButton navigation for unvalidated users based on the active route and current chatID, reportID (optional). + */ const handleUnvalidatedUserNavigation = (chatReportID: string, reportID?: string) => { const activeRoute = Navigation.getActiveRoute(); const matchedRoute = getRouteMappings(chatReportID, reportID).find((mapping) => mapping.check(activeRoute)); From 6a519b73f44e8a9eeacc330706c0d7f1d670a887 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Fri, 3 Oct 2025 17:36:47 +0200 Subject: [PATCH 20/32] Reduce code repetitiveness by using test.each in SettlementButtonUtilsTest --- tests/unit/SettlementButtonUtilsTest.ts | 130 +++++++++++------------- 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/tests/unit/SettlementButtonUtilsTest.ts b/tests/unit/SettlementButtonUtilsTest.ts index c4aca575b9723..84ffab384c335 100644 --- a/tests/unit/SettlementButtonUtilsTest.ts +++ b/tests/unit/SettlementButtonUtilsTest.ts @@ -13,43 +13,77 @@ describe('SettlementButtonUtils', () => { jest.clearAllMocks(); }); - it('navigate to ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_ROOT', () => { - const mockActiveRoute = ROUTES.SEARCH_ROOT.getRoute({query: ''}); + // handleUnvalidatedUserNavigation navigates to the correct route + it.each([ + { + description: 'navigate to ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_ROOT', + mockActiveRoute: ROUTES.SEARCH_ROOT.getRoute({query: ''}), + expectedRouteToNavigate: ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT, + }, + { + description: 'navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_REPORT', + mockActiveRoute: ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}), + expectedRouteToNavigate: ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID), + }, + { + description: 'navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_MONEY_REQUEST_REPORT', + mockActiveRoute: ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}), + expectedRouteToNavigate: ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID), + }, + { + description: 'navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(chatReportID)', + mockActiveRoute: ROUTES.REPORT_WITH_ID.getRoute(mockChatReportID), + expectedRouteToNavigate: ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockChatReportID), + }, + { + description: 'navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID)', + mockActiveRoute: ROUTES.REPORT_WITH_ID.getRoute(mockReportID), + expectedRouteToNavigate: ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID), + }, + { + description: 'navigate to ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_STEP_CONFIRMATION', + mockActiveRoute: ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), + expectedRouteToNavigate: ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute( + CONST.IOU.ACTION.CREATE, + CONST.IOU.TYPE.PAY, + CONST.IOU.OPTIMISTIC_TRANSACTION_ID, + mockChatReportID, + ), + }, + { + description: 'navigate to ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_CREATE', + mockActiveRoute: ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), + expectedRouteToNavigate: ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), + }, + ])('$description', ({mockActiveRoute, expectedRouteToNavigate}) => { (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_ROOT_VERIFY_ACCOUNT); - }); - - it('navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_REPORT', () => { - const mockActiveRoute = ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); - }); - - it('do not navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when reportID is undefined', () => { - const mockActiveRoute = ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID); - expect(Navigation.navigate).not.toHaveBeenCalled(); - }); - - it('navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when active route is ROUTES.SEARCH_MONEY_REQUEST_REPORT', () => { - const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); + expect(Navigation.navigate).toHaveBeenCalledWith(expectedRouteToNavigate); + expect(Navigation.navigate).toHaveBeenCalledTimes(1); }); - it('do not navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when reportID is undefined', () => { - const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); + // handleUnvalidatedUserNavigation does not navigate to the route that require reportID, when reportID is undefined + it.each([ + { + description: 'do not navigate to ROUTES.SEARCH_MONEY_REQUEST_REPORT_VERIFY_ACCOUNT when reportID is undefined', + mockActiveRoute: ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}), + }, + { + description: 'do not navigate to ROUTES.SEARCH_REPORT_VERIFY_ACCOUNT when reportID is undefined', + mockActiveRoute: ROUTES.SEARCH_REPORT.getRoute({reportID: mockReportID}), + }, + { + description: 'do not navigate when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID) and reportID is undefined', + mockActiveRoute: ROUTES.REPORT_WITH_ID.getRoute(mockReportID), + }, + ])('$description', ({mockActiveRoute}) => { (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); handleUnvalidatedUserNavigation(mockChatReportID); expect(Navigation.navigate).not.toHaveBeenCalled(); }); + // handleUnvalidatedUserNavigation matches the first applicable route when multiple conditions could match it('match ROUTES.SEARCH_MONEY_REQUEST_REPORT over ROUTES.REPORT_WITH_ID', () => { - // Should match the first applicable route when multiple conditions could match const mockActiveRoute = ROUTES.SEARCH_MONEY_REQUEST_REPORT.getRoute({reportID: mockReportID}); (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); @@ -58,47 +92,7 @@ describe('SettlementButtonUtils', () => { expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); }); - it('navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(chatReportID)', () => { - const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockChatReportID); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockChatReportID)); - expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); - }); - - it('navigate to ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(reportID) when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID)', () => { - const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockReportID); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); - expect(Navigation.navigate).toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockReportID)); - expect(Navigation.navigate).not.toHaveBeenCalledWith(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(mockChatReportID)); - }); - - it('do not navigate when active route is ROUTES.REPORT_WITH_ID.getRoute(reportID) and reportID is undefined', () => { - const mockActiveRoute = ROUTES.REPORT_WITH_ID.getRoute(mockReportID); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID); - expect(Navigation.navigate).not.toHaveBeenCalled(); - }); - - it('navigate to ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_STEP_CONFIRMATION', () => { - const mockActiveRoute = ROUTES.MONEY_REQUEST_STEP_CONFIRMATION.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); - expect(Navigation.navigate).toHaveBeenCalledWith( - ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), - ); - }); - - it('navigate to ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_CREATE', () => { - const mockActiveRoute = ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID); - (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); - handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); - expect(Navigation.navigate).toHaveBeenCalledWith( - ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), - ); - }); - + // handleUnvalidatedUserNavigation does not navigate when no route mapping matches it('when no route mapping matches, user should not be navigated', () => { (Navigation.getActiveRoute as jest.Mock).mockReturnValue('/just/unmatched/route'); handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); From 45ee8ab45a5c8ce83b67b2b7a0f1be8a25a9aa26 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Mon, 6 Oct 2025 18:35:16 +0200 Subject: [PATCH 21/32] Move SettlementButton payment methods to SettlementButtonUtils for React Compiler compliance --- src/components/SettlementButton/index.tsx | 23 ++----------------- src/libs/SettlementButtonUtils.ts | 27 ++++++++++++++++++++++- tests/unit/SettlementButtonUtilsTest.ts | 2 +- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index d78a5c2396b78..349b6bcf2283f 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -30,7 +30,7 @@ import { isInvoiceReport as isInvoiceReportUtil, isIOUReport, } from '@libs/ReportUtils'; -import handleUnvalidatedUserNavigation from '@libs/SettlementButtonUtils'; +import {getSettlementButtonPaymentMethods, handleUnvalidatedUserNavigation} from '@libs/SettlementButtonUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; import {setPersonalBankAccountContinueKYCOnSuccess} from '@userActions/BankAccounts'; import {approveMoneyRequest} from '@userActions/IOU'; @@ -174,26 +174,7 @@ function SettlementButton({ const paymentButtonOptions = useMemo(() => { const buttonOptions = []; - const paymentMethods = { - [CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]: { - text: hasActivatedWallet ? translate('iou.settleWallet', {formattedAmount: ''}) : translate('iou.settlePersonal', {formattedAmount: ''}), - icon: Expensicons.User, - value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, - shouldUpdateSelectedIndex: false, - }, - [CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]: { - text: translate('iou.settleBusiness', {formattedAmount: ''}), - icon: Expensicons.Building, - value: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, - shouldUpdateSelectedIndex: false, - }, - [CONST.IOU.PAYMENT_TYPE.ELSEWHERE]: { - text: translate('iou.payElsewhere', {formattedAmount: ''}), - icon: Expensicons.CheckCircle, - value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - shouldUpdateSelectedIndex: false, - }, - }; + const paymentMethods = getSettlementButtonPaymentMethods(hasActivatedWallet, translate); const approveButtonOption = { text: translate('iou.approve', {formattedAmount}), diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index 1896267a9bfc7..cc7a77fa321a6 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -1,3 +1,5 @@ +import * as Expensicons from '@components/Icon/Expensicons'; +import type {LocaleContextProps} from '@components/LocaleContextProvider'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; import Log from './Log'; @@ -80,4 +82,27 @@ const handleUnvalidatedUserNavigation = (chatReportID: string, reportID?: string Log.warn('Failed to navigate to the correct path'); }; -export default handleUnvalidatedUserNavigation; +const getSettlementButtonPaymentMethods = (hasActivatedWallet: boolean, translate: LocaleContextProps['translate']) => { + return { + [CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]: { + text: hasActivatedWallet ? translate('iou.settleWallet', {formattedAmount: ''}) : translate('iou.settlePersonal', {formattedAmount: ''}), + icon: Expensicons.User, + value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, + shouldUpdateSelectedIndex: false, + }, + [CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]: { + text: translate('iou.settleBusiness', {formattedAmount: ''}), + icon: Expensicons.Building, + value: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, + shouldUpdateSelectedIndex: false, + }, + [CONST.IOU.PAYMENT_TYPE.ELSEWHERE]: { + text: translate('iou.payElsewhere', {formattedAmount: ''}), + icon: Expensicons.CheckCircle, + value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + shouldUpdateSelectedIndex: false, + }, + }; +}; + +export {handleUnvalidatedUserNavigation, getSettlementButtonPaymentMethods}; diff --git a/tests/unit/SettlementButtonUtilsTest.ts b/tests/unit/SettlementButtonUtilsTest.ts index 84ffab384c335..fe69201da0359 100644 --- a/tests/unit/SettlementButtonUtilsTest.ts +++ b/tests/unit/SettlementButtonUtilsTest.ts @@ -1,5 +1,5 @@ import Navigation from '@libs/Navigation/Navigation'; -import handleUnvalidatedUserNavigation from '@libs/SettlementButtonUtils'; +import {handleUnvalidatedUserNavigation} from '@libs/SettlementButtonUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; From f1d972758482105d8b137cc3cbd5225b390d39d4 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Tue, 7 Oct 2025 11:05:18 +0200 Subject: [PATCH 22/32] Add VERIFY_ACCOUNT screens to SEARCH_TO_RHP mapping for correct relation to the central search screen --- src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts index 2489d81fbf723..9dc84d5798b2f 100644 --- a/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts +++ b/src/libs/Navigation/linkingConfig/RELATIONS/SEARCH_TO_RHP.ts @@ -4,10 +4,12 @@ import SCREENS from '@src/SCREENS'; // This file is used to define RHP screens that are in relation to the search screen. const SEARCH_TO_RHP: Partial> = { [SCREENS.SEARCH.ROOT]: [ + SCREENS.SEARCH.ROOT_VERIFY_ACCOUNT, SCREENS.SEARCH.ADVANCED_FILTERS_TYPE_RHP, SCREENS.SEARCH.ADVANCED_FILTERS_GROUP_BY_RHP, SCREENS.SEARCH.ADVANCED_FILTERS_STATUS_RHP, SCREENS.SEARCH.REPORT_RHP, + SCREENS.SEARCH.REPORT_VERIFY_ACCOUNT, SCREENS.SEARCH.TRANSACTION_HOLD_REASON_RHP, SCREENS.SEARCH.TRANSACTIONS_CHANGE_REPORT_SEARCH_RHP, SCREENS.SEARCH.ADVANCED_FILTERS_RHP, From 1530f7897198ce1083d4cfd547e314033a1c1572 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 15 Oct 2025 12:34:19 +0200 Subject: [PATCH 23/32] Add jsdoc for getSettlementButtonPaymentMethods from SettlementButtonUtils --- src/libs/SettlementButtonUtils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index cc7a77fa321a6..fcbd79c3c91f2 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -82,6 +82,9 @@ const handleUnvalidatedUserNavigation = (chatReportID: string, reportID?: string Log.warn('Failed to navigate to the correct path'); }; +/** + * Retrieves SettlementButton payment methods. + */ const getSettlementButtonPaymentMethods = (hasActivatedWallet: boolean, translate: LocaleContextProps['translate']) => { return { [CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]: { From 44a2fca2b82f8f7c2f79e6cdb97a245987167684 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 15 Oct 2025 15:51:38 +0200 Subject: [PATCH 24/32] Delete MoneyRequestCreateVerifyAccountPage because of the change in SettlementButton after main merge --- src/ROUTES.ts | 5 ----- src/SCREENS.ts | 1 - .../AppNavigator/ModalStackNavigators/index.tsx | 1 - src/libs/Navigation/linkingConfig/config.ts | 1 - src/libs/Navigation/types.ts | 6 ------ src/libs/SettlementButtonUtils.ts | 6 ------ .../MoneyRequestCreateVerifyAccountPage.tsx | 14 -------------- tests/unit/SettlementButtonUtilsTest.ts | 5 ----- 8 files changed, 39 deletions(-) delete mode 100644 src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx diff --git a/src/ROUTES.ts b/src/ROUTES.ts index e1e7bbe187b65..f4bab61ebe347 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -742,11 +742,6 @@ const ROUTES = { return `${action as string}/${iouType as string}/start/${transactionID}/${reportID}` as const; }, }, - MONEY_REQUEST_CREATE_VERIFY_ACCOUNT: { - route: `:action/:iouType/start/:transactionID/:reportID/${VERIFY_ACCOUNT}`, - getRoute: (action: IOUAction, iouType: IOUType, transactionID: string, reportID: string) => - `${action as string}/${iouType as string}/start/${transactionID}/${reportID}/${VERIFY_ACCOUNT}` as const, - }, MONEY_REQUEST_STEP_SEND_FROM: { route: 'create/:iouType/from/:transactionID/:reportID', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 0c58853acd823..99459b6e0ca22 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -266,7 +266,6 @@ const SCREENS = { MONEY_REQUEST: { CREATE: 'Money_Request_Create', - CREATE_VERIFY_ACCOUNT: 'Create_Verify_Account', HOLD: 'Money_Request_Hold_Reason', REJECT: 'Money_Request_Reject_Reason', STEP_CONFIRMATION: 'Money_Request_Step_Confirmation', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 76770f8de7d47..0d50868981291 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -137,7 +137,6 @@ function createModalStackNavigator(screens: Scr const MoneyRequestModalStackNavigator = createModalStackNavigator({ [SCREENS.MONEY_REQUEST.START]: () => require('../../../../pages/iou/request/IOURequestRedirectToStartPage').default, [SCREENS.MONEY_REQUEST.CREATE]: () => require('../../../../pages/iou/request/IOURequestStartPage').default, - [SCREENS.MONEY_REQUEST.CREATE_VERIFY_ACCOUNT]: () => require('../../../../pages/iou/request/MoneyRequestCreateVerifyAccountPage').default, [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION]: () => require('../../../../pages/iou/request/step/IOURequestStepConfirmation').default, [SCREENS.MONEY_REQUEST.STEP_CONFIRMATION_VERIFY_ACCOUNT]: () => require('../../../../pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage').default, [SCREENS.MONEY_REQUEST.STEP_AMOUNT]: () => require('../../../../pages/iou/request/step/IOURequestStepAmount').default, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 38636230ff6ca..f108e817a4ac5 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1346,7 +1346,6 @@ const config: LinkingOptions['config'] = { }, }, }, - [SCREENS.MONEY_REQUEST.CREATE_VERIFY_ACCOUNT]: ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.route, [SCREENS.MONEY_REQUEST.DISTANCE_CREATE]: { path: ROUTES.DISTANCE_REQUEST_CREATE.route, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 8ae0efe2ef20c..7f986cf2c5db1 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1460,12 +1460,6 @@ type MoneyRequestNavigatorParamList = { backToReport?: string; reportActionID?: string; }; - [SCREENS.MONEY_REQUEST.CREATE_VERIFY_ACCOUNT]: { - action: IOUAction; - iouType: IOUType; - transactionID: string; - reportID: string; - }; [SCREENS.MONEY_REQUEST.START]: { iouType: IOUType; reportID: string; diff --git a/src/libs/SettlementButtonUtils.ts b/src/libs/SettlementButtonUtils.ts index fcbd79c3c91f2..110a74626847b 100644 --- a/src/libs/SettlementButtonUtils.ts +++ b/src/libs/SettlementButtonUtils.ts @@ -34,12 +34,6 @@ const getRouteMappings = (chatReportID: string, reportID?: string): RouteMapping ROUTES.MONEY_REQUEST_STEP_CONFIRMATION_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID), ), }, - { - check: (activeRoute: string) => - activeRoute.includes(ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - navigate: () => - Navigation.navigate(ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, chatReportID)), - }, { check: (activeRoute: string) => activeRoute.includes(ROUTES.REPORT_WITH_ID.getRoute(chatReportID)), navigate: () => Navigation.navigate(ROUTES.REPORT_VERIFY_ACCOUNT.getRoute(chatReportID)), diff --git a/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx b/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx deleted file mode 100644 index e6b3e696a3e84..0000000000000 --- a/src/pages/iou/request/MoneyRequestCreateVerifyAccountPage.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {MoneyRequestNavigatorParamList} from '@libs/Navigation/types'; -import VerifyAccountPageBase from '@pages/settings/VerifyAccountPageBase'; -import ROUTES from '@src/ROUTES'; -import type SCREENS from '@src/SCREENS'; - -type MoneyRequestCreateVerifyAccountPageProps = PlatformStackScreenProps; - -function MoneyRequestCreateVerifyAccountPage({route}: MoneyRequestCreateVerifyAccountPageProps) { - return ; -} - -export default MoneyRequestCreateVerifyAccountPage; diff --git a/tests/unit/SettlementButtonUtilsTest.ts b/tests/unit/SettlementButtonUtilsTest.ts index fe69201da0359..8b2b730f77ed5 100644 --- a/tests/unit/SettlementButtonUtilsTest.ts +++ b/tests/unit/SettlementButtonUtilsTest.ts @@ -50,11 +50,6 @@ describe('SettlementButtonUtils', () => { mockChatReportID, ), }, - { - description: 'navigate to ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT when active route is ROUTES.MONEY_REQUEST_CREATE', - mockActiveRoute: ROUTES.MONEY_REQUEST_CREATE.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), - expectedRouteToNavigate: ROUTES.MONEY_REQUEST_CREATE_VERIFY_ACCOUNT.getRoute(CONST.IOU.ACTION.CREATE, CONST.IOU.TYPE.PAY, CONST.IOU.OPTIMISTIC_TRANSACTION_ID, mockChatReportID), - }, ])('$description', ({mockActiveRoute, expectedRouteToNavigate}) => { (Navigation.getActiveRoute as jest.Mock).mockReturnValue(mockActiveRoute); handleUnvalidatedUserNavigation(mockChatReportID, mockReportID); From 32e12806fb9cd9c2168d0beda98a784ceffd0e8f Mon Sep 17 00:00:00 2001 From: Olgierd Date: Tue, 28 Oct 2025 12:16:44 +0100 Subject: [PATCH 25/32] Bring useCallback back as the 'checkForNecessaryAction' function makes the dependencies of parent useCallback hook --- src/components/SettlementButton/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index a96453e48f29d..3a6dc0e2b9e28 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -172,7 +172,7 @@ function SettlementButton({ return formattedPaymentMethods.filter((ba) => (ba.accountData as AccountData)?.type === CONST.BANK_ACCOUNT.TYPE.PERSONAL); } - const checkForNecessaryAction = () => { + const checkForNecessaryAction = useCallback(() => { if (isAccountLocked) { showLockedAccountModal(); return true; @@ -189,7 +189,7 @@ function SettlementButton({ } return false; - }; + }, [policy, isAccountLocked, isUserValidated, chatReportID, reportID]); const getPaymentSubitems = useCallback( (payAsBusiness: boolean) => { From 74fab076eab1a3701281f1058683c51ab4fbebe3 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Tue, 28 Oct 2025 12:32:01 +0100 Subject: [PATCH 26/32] Add useCallback missing dependency: 'showLockedAccountModal' --- src/components/SettlementButton/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 3a6dc0e2b9e28..47ef17d6adaae 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -189,7 +189,7 @@ function SettlementButton({ } return false; - }, [policy, isAccountLocked, isUserValidated, chatReportID, reportID]); + }, [policy, isAccountLocked, isUserValidated, chatReportID, reportID, showLockedAccountModal]); const getPaymentSubitems = useCallback( (payAsBusiness: boolean) => { From 0bd0139cd69f960a20b13d8f5e02dbff5ce2dd71 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Wed, 29 Oct 2025 12:09:50 +0100 Subject: [PATCH 27/32] Make ConstantPicker use new SelectionList --- src/pages/Debug/ConstantPicker.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/Debug/ConstantPicker.tsx b/src/pages/Debug/ConstantPicker.tsx index 00a6d388d5f42..32d6ce8cbd852 100644 --- a/src/pages/Debug/ConstantPicker.tsx +++ b/src/pages/Debug/ConstantPicker.tsx @@ -1,9 +1,10 @@ import isObject from 'lodash/isObject'; import React, {useMemo, useState} from 'react'; -import SelectionList from '@components/SelectionListWithSections'; -import RadioListItem from '@components/SelectionListWithSections/RadioListItem'; -import type {ListItem} from '@components/SelectionListWithSections/types'; +import SelectionList from '@components/SelectionList'; +import RadioListItem from '@components/SelectionList/ListItem/RadioListItem'; +import type {ListItem} from '@components/SelectionList/types'; import useLocalize from '@hooks/useLocalize'; +import {search} from '@libs/actions/Search'; import tokenizedSearch from '@libs/tokenizedSearch'; import type {DebugForms} from './const'; import {DETAILS_CONSTANT_FIELDS} from './const'; @@ -55,13 +56,12 @@ function ConstantPicker({formType, fieldName, fieldValue, onSubmit}: ConstantPic return ( ); From a0b340d3fcbad8cdf3234e067e5ee07e79964de7 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Thu, 30 Oct 2025 13:51:17 +0100 Subject: [PATCH 28/32] Add displayName prop to new VerifyAccountPages --- src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx | 2 ++ src/pages/Search/SearchReportVerifyAccountPage.tsx | 2 ++ src/pages/Search/SearchRootVerifyAccountPage.tsx | 2 ++ src/pages/home/report/ReportVerifyAccountPage.tsx | 2 ++ .../step/MoneyRequestStepConfirmationVerifyAccountPage.tsx | 2 ++ 5 files changed, 10 insertions(+) diff --git a/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx b/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx index 73defbf18072b..241b04324f902 100644 --- a/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx +++ b/src/pages/Search/SearchMoneyRequestReportVerifyAccountPage.tsx @@ -11,4 +11,6 @@ function SearchMoneyRequestReportVerifyAccountPage({route}: SearchMoneyRequestRe return ; } +SearchMoneyRequestReportVerifyAccountPage.displayName = 'SearchMoneyRequestReportVerifyAccountPage'; + export default SearchMoneyRequestReportVerifyAccountPage; diff --git a/src/pages/Search/SearchReportVerifyAccountPage.tsx b/src/pages/Search/SearchReportVerifyAccountPage.tsx index 5c4eb6fc7e203..820f674dc3b5b 100644 --- a/src/pages/Search/SearchReportVerifyAccountPage.tsx +++ b/src/pages/Search/SearchReportVerifyAccountPage.tsx @@ -11,4 +11,6 @@ function SearchReportVerifyAccountPage({route}: SearchReportVerifyAccountPagePro return ; } +SearchReportVerifyAccountPage.displayName = 'SearchReportVerifyAccountPage'; + export default SearchReportVerifyAccountPage; diff --git a/src/pages/Search/SearchRootVerifyAccountPage.tsx b/src/pages/Search/SearchRootVerifyAccountPage.tsx index 9b6fa120d4a5a..5d5ff472830e9 100644 --- a/src/pages/Search/SearchRootVerifyAccountPage.tsx +++ b/src/pages/Search/SearchRootVerifyAccountPage.tsx @@ -6,4 +6,6 @@ function SearchRootVerifyAccountPage() { return ; } +SearchRootVerifyAccountPage.displayName = 'SearchRootVerifyAccountPage'; + export default SearchRootVerifyAccountPage; diff --git a/src/pages/home/report/ReportVerifyAccountPage.tsx b/src/pages/home/report/ReportVerifyAccountPage.tsx index a905068635389..10c6f34cc6dc9 100644 --- a/src/pages/home/report/ReportVerifyAccountPage.tsx +++ b/src/pages/home/report/ReportVerifyAccountPage.tsx @@ -11,4 +11,6 @@ function ReportVerifyAccountPage({route}: ReportVerifyAccountPageProps) { return ; } +ReportVerifyAccountPage.displayName = 'ReportVerifyAccountPage'; + export default ReportVerifyAccountPage; diff --git a/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx b/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx index 178e4d147672c..0dcefabf56e0e 100644 --- a/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx +++ b/src/pages/iou/request/step/MoneyRequestStepConfirmationVerifyAccountPage.tsx @@ -15,4 +15,6 @@ function MoneyRequestStepConfirmationVerifyAccountPage({route}: MoneyRequestStep ); } +MoneyRequestStepConfirmationVerifyAccountPage.displayName = 'MoneyRequestStepConfirmationVerifyAccountPage'; + export default MoneyRequestStepConfirmationVerifyAccountPage; From 280830df03964dfe20901c0b394228ffc738b6cd Mon Sep 17 00:00:00 2001 From: Olgierd Date: Thu, 30 Oct 2025 14:00:14 +0100 Subject: [PATCH 29/32] Revert changes made to ConstantPicker --- src/pages/Debug/ConstantPicker.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/Debug/ConstantPicker.tsx b/src/pages/Debug/ConstantPicker.tsx index 32d6ce8cbd852..00a6d388d5f42 100644 --- a/src/pages/Debug/ConstantPicker.tsx +++ b/src/pages/Debug/ConstantPicker.tsx @@ -1,10 +1,9 @@ import isObject from 'lodash/isObject'; import React, {useMemo, useState} from 'react'; -import SelectionList from '@components/SelectionList'; -import RadioListItem from '@components/SelectionList/ListItem/RadioListItem'; -import type {ListItem} from '@components/SelectionList/types'; +import SelectionList from '@components/SelectionListWithSections'; +import RadioListItem from '@components/SelectionListWithSections/RadioListItem'; +import type {ListItem} from '@components/SelectionListWithSections/types'; import useLocalize from '@hooks/useLocalize'; -import {search} from '@libs/actions/Search'; import tokenizedSearch from '@libs/tokenizedSearch'; import type {DebugForms} from './const'; import {DETAILS_CONSTANT_FIELDS} from './const'; @@ -56,12 +55,13 @@ function ConstantPicker({formType, fieldName, fieldValue, onSubmit}: ConstantPic return ( ); From 2af58e12cb1c49eea9bb9c6c83fe4aa59bd40049 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Fri, 31 Oct 2025 12:09:02 +0100 Subject: [PATCH 30/32] Add getSettlementButtonPaymentMethods tests and rename handleUnvalidatedUserNavigation tests --- tests/unit/SettlementButtonUtilsTest.ts | 119 +++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/tests/unit/SettlementButtonUtilsTest.ts b/tests/unit/SettlementButtonUtilsTest.ts index 8b2b730f77ed5..3afeb777f72fd 100644 --- a/tests/unit/SettlementButtonUtilsTest.ts +++ b/tests/unit/SettlementButtonUtilsTest.ts @@ -1,11 +1,23 @@ +import * as Expensicons from '@components/Icon/Expensicons'; +import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; -import {handleUnvalidatedUserNavigation} from '@libs/SettlementButtonUtils'; +import {getSettlementButtonPaymentMethods, handleUnvalidatedUserNavigation} from '@libs/SettlementButtonUtils'; import CONST from '@src/CONST'; import ROUTES from '@src/ROUTES'; jest.mock('@libs/Navigation/Navigation'); -describe('SettlementButtonUtils', () => { +const mockTranslate = jest.fn((key: string) => key); + +jest.mock('@hooks/useLocalize', () => ({ + // eslint-disable-next-line @typescript-eslint/naming-convention + __esModule: true, + default: () => ({ + translate: mockTranslate, + }), +})); + +describe('handleUnvalidatedUserNavigation', () => { const mockReportID = '123456789'; const mockChatReportID = '987654321'; @@ -94,3 +106,106 @@ describe('SettlementButtonUtils', () => { expect(Navigation.navigate).not.toHaveBeenCalled(); }); }); + +describe('getSettlementButtonPaymentMethods', () => { + const {translate} = useLocalize(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return payment method with wallet for PERSONAL_BANK_ACCOUNT when hasActivatedWallet is true', () => { + const result = getSettlementButtonPaymentMethods(true, translate); + expect(result[CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]).toEqual({ + text: translate('iou.settleWallet', {formattedAmount: ''}), + icon: Expensicons.User, + value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, + }); + }); + + it('should return payment method with personal bank account for PERSONAL_BANK_ACCOUNT when hasActivatedWallet is false', () => { + const result = getSettlementButtonPaymentMethods(false, translate); + expect(result[CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]).toEqual({ + text: translate('iou.settlePersonal', {formattedAmount: ''}), + icon: Expensicons.User, + value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, + }); + }); + + it('should return payment method with business bank account for BUSINESS_BANK_ACCOUNT', () => { + const result = getSettlementButtonPaymentMethods(true, translate); + expect(result[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]).toEqual({ + text: translate('iou.settleBusiness', {formattedAmount: ''}), + icon: Expensicons.Building, + value: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, + }); + }); + + it('should return payment method elsewhere for ELSEWHERE', () => { + const result = getSettlementButtonPaymentMethods(true, translate); + expect(result[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]).toEqual({ + text: translate('iou.payElsewhere', {formattedAmount: ''}), + icon: Expensicons.CheckCircle, + value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + shouldUpdateSelectedIndex: false, + }); + }); + + it('should return all three payment methods', () => { + const result = getSettlementButtonPaymentMethods(true, translate); + expect(Object.keys(result)).toHaveLength(3); + expect(result).toHaveProperty(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT); + expect(result).toHaveProperty(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT); + expect(result).toHaveProperty(CONST.IOU.PAYMENT_TYPE.ELSEWHERE); + }); + + it.each([ + {hasActivatedWallet: true, expectedPersonalKey: 'iou.settleWallet'}, + {hasActivatedWallet: false, expectedPersonalKey: 'iou.settlePersonal'}, + ])('should use correct texts for each payment method when hasActivatedWallet is $hasActivatedWallet', ({hasActivatedWallet, expectedPersonalKey}) => { + const result = getSettlementButtonPaymentMethods(hasActivatedWallet, translate); + expect(translate).toHaveBeenCalledTimes(3); + expect(translate).toHaveBeenCalledWith(expectedPersonalKey, {formattedAmount: ''}); + expect(translate).toHaveBeenCalledWith('iou.settleBusiness', {formattedAmount: ''}); + expect(translate).toHaveBeenCalledWith('iou.payElsewhere', {formattedAmount: ''}); + expect(result[CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT].text).toBe(expectedPersonalKey); + expect(result[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT].text).toBe('iou.settleBusiness'); + expect(result[CONST.IOU.PAYMENT_TYPE.ELSEWHERE].text).toBe('iou.payElsewhere'); + }); + + it.each([ + { + method: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, + expectedIcon: Expensicons.User, + expectedValue: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, + description: 'PERSONAL_BANK_ACCOUNT', + }, + { + method: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, + expectedIcon: Expensicons.Building, + expectedValue: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, + description: 'BUSINESS_BANK_ACCOUNT', + }, + { + method: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + expectedIcon: Expensicons.CheckCircle, + expectedValue: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, + description: 'ELSEWHERE', + }, + ])('should use correct icon and value for $description regardless of hasActivatedWallet', ({method, expectedIcon, expectedValue}) => { + const resultWithWallet = getSettlementButtonPaymentMethods(true, translate); + const resultWithoutWallet = getSettlementButtonPaymentMethods(false, translate); + [resultWithWallet, resultWithoutWallet].forEach((result) => { + const paymentMethod = result[method]; + expect(paymentMethod.icon).toBe(expectedIcon); + expect(paymentMethod.value).toBe(expectedValue); + }); + }); + + it('should only set shouldUpdateSelectedIndex for elsewhere payment type', () => { + const result = getSettlementButtonPaymentMethods(true, translate); + expect(result[CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]).not.toHaveProperty('shouldUpdateSelectedIndex'); + expect(result[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]).not.toHaveProperty('shouldUpdateSelectedIndex'); + expect(result[CONST.IOU.PAYMENT_TYPE.ELSEWHERE].shouldUpdateSelectedIndex).toBe(false); + }); +}); From 11beaa43179befa6409359761f13fb33bd418080 Mon Sep 17 00:00:00 2001 From: Olgierd Date: Mon, 3 Nov 2025 16:32:22 +0100 Subject: [PATCH 31/32] Use getSettlementButtonPaymentMethods in useBulkPayOptions --- src/hooks/useBulkPayOptions.ts | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/hooks/useBulkPayOptions.ts b/src/hooks/useBulkPayOptions.ts index 11df01d894dea..12368653e3590 100644 --- a/src/hooks/useBulkPayOptions.ts +++ b/src/hooks/useBulkPayOptions.ts @@ -1,11 +1,12 @@ import truncate from 'lodash/truncate'; import {useMemo} from 'react'; -import {Bank, Building, CheckCircle, User, Wallet} from '@components/Icon/Expensicons'; +import {Bank, Building, Wallet} from '@components/Icon/Expensicons'; import type {PopoverMenuItem} from '@components/PopoverMenu'; import type {BankAccountMenuItem} from '@components/Search/types'; import {formatPaymentMethods} from '@libs/PaymentUtils'; import {hasRequestFromCurrentAccount} from '@libs/ReportActionsUtils'; import {isExpenseReport as isExpenseReportUtil, isInvoiceReport as isInvoiceReportUtil, isIOUReport as isIOUReportUtil} from '@libs/ReportUtils'; +import {getSettlementButtonPaymentMethods} from '@libs/SettlementButtonUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {AccountData, Policy} from '@src/types/onyx'; @@ -81,23 +82,7 @@ function useBulkPayOptions({selectedPolicyID, selectedReportID, activeAdminPolic const bulkPayButtonOptions = useMemo(() => { const buttonOptions = []; - const paymentMethods = { - [CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]: { - text: hasActivatedWallet ? translate('iou.settleWallet', {formattedAmount: ''}) : translate('iou.settlePersonal', {formattedAmount: ''}), - icon: User, - key: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT, - }, - [CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]: { - text: translate('iou.settleBusiness', {formattedAmount: ''}), - icon: Building, - key: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT, - }, - [CONST.IOU.PAYMENT_TYPE.ELSEWHERE]: { - text: translate('iou.payElsewhere', {formattedAmount: ''}), - icon: CheckCircle, - key: CONST.IOU.PAYMENT_TYPE.ELSEWHERE, - }, - }; + const paymentMethods = getSettlementButtonPaymentMethods(hasActivatedWallet, translate); if (!selectedReportID || !selectedPolicyID) { return undefined; From 0d38c7f2c6e550d95ce4a60cefc15551db29212a Mon Sep 17 00:00:00 2001 From: Olgierd Date: Mon, 3 Nov 2025 17:50:29 +0100 Subject: [PATCH 32/32] Add suitable SearchHoldReasonPage props type --- src/pages/Search/SearchHoldReasonPage.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/Search/SearchHoldReasonPage.tsx b/src/pages/Search/SearchHoldReasonPage.tsx index 8f483429c35e3..68c7345c6d18c 100644 --- a/src/pages/Search/SearchHoldReasonPage.tsx +++ b/src/pages/Search/SearchHoldReasonPage.tsx @@ -15,7 +15,11 @@ import ONYXKEYS from '@src/ONYXKEYS'; import SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/MoneyRequestHoldReasonForm'; -function SearchHoldReasonPage({route}: PlatformStackScreenProps>) { +type SearchHoldReasonPageProps = + | PlatformStackScreenProps + | PlatformStackScreenProps; + +function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) { const {translate} = useLocalize(); const {backTo = '', reportID} = route.params ?? {}; const context = useSearchContext();