From 995005a4bbd7519e18e076593e4eeafaadaf93e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Musia=C5=82?= Date: Fri, 24 Jul 2026 18:27:52 +0200 Subject: [PATCH 1/2] Fix verify account resume payment action --- .../MoneyReportHeaderSecondaryActions.tsx | 61 ++++++++++--------- .../MoneyReportHeaderSelectionDropdown.tsx | 5 +- .../SelectionToolbar/index.tsx | 5 +- src/hooks/useSelectionModePayment.ts | 24 ++++---- src/hooks/useSelectionModeReportActions.ts | 5 +- 5 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx index d43242332657..acb313eb0c35 100644 --- a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx +++ b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx @@ -37,14 +37,13 @@ import useSearchShouldCalculateTotals from '@hooks/useSearchShouldCalculateTotal import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; +import useVerifyAccountAndResume from '@hooks/useVerifyAccountAndResume'; import {generateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import {search} from '@libs/actions/Search'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import getPlatform from '@libs/getPlatform'; import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; -import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; -import Navigation from '@libs/Navigation/Navigation'; import TransitionTracker from '@libs/Navigation/TransitionTracker'; import {isTrackOnboardingChoice} from '@libs/OnboardingUtils'; import type {KYCFlowEvent, TriggerKYCFlow, WorkspacePolicyPaymentOption} from '@libs/PaymentUtils'; @@ -69,13 +68,12 @@ import {canApproveIOU, canIOUBePaid as canIOUBePaidAction} from '@userActions/IO import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Route} from '@src/ROUTES'; -import {DYNAMIC_ROUTES} from '@src/ROUTES'; import {personalDetailsLoginSelector} from '@src/selectors/PersonalDetails'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import type {ValueOf} from 'type-fest'; -import {delegateEmailSelector, isUserValidatedSelector} from '@selectors/Account'; +import {delegateEmailSelector} from '@selectors/Account'; import {hasSeenTourSelector} from '@selectors/Onboarding'; import truncate from 'lodash/truncate'; import React, {useContext, useEffect} from 'react'; @@ -119,12 +117,12 @@ function MoneyReportHeaderSecondaryActionsInner({reportID, primaryAction, isRepo const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); - const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, { - selector: isUserValidatedSelector, - }); const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, { selector: delegateEmailSelector, }); + + // Store the pending payment and resume it after the user validates, instead of dropping it on the way to the magic-code screen. + const {isUserValidated, verifyAccountAndResume} = useVerifyAccountAndResume((retry) => retry?.()); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const [conciergeChat] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${conciergeReportID}`); @@ -427,32 +425,35 @@ function MoneyReportHeaderSecondaryActionsInner({reportID, primaryAction, isRepo const hasViolations = hasViolationsReportUtils(moneyRequestReport?.reportID, allTransactionViolations, accountID, email ?? ''); const onPaymentSelect = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => { + const runPaymentSelection = () => + selectPaymentType({ + event, + iouPaymentType, + triggerKYCFlow, + expenseReportPolicy: policy, + policy, + onPress: confirmPayment, + currentAccountID: accountID, + currentEmail: email ?? '', + hasViolations, + isASAPSubmitBetaEnabled, + confirmApproval: () => confirmApproval(), + iouReport: moneyRequestReport, + iouReportNextStep: nextStep, + betas, + userBillingGracePeriodEnds, + amountOwed, + ownerBillingGracePeriodEnd, + delegateEmail, + isTrackIntentUser, + ownerLogin: submitterLogin, + }); + if (!isUserValidated && iouPaymentType !== CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { - Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.VERIFY_ACCOUNT.path)); + verifyAccountAndResume(runPaymentSelection); return; } - selectPaymentType({ - event, - iouPaymentType, - triggerKYCFlow, - expenseReportPolicy: policy, - policy, - onPress: confirmPayment, - currentAccountID: accountID, - currentEmail: email ?? '', - hasViolations, - isASAPSubmitBetaEnabled, - confirmApproval: () => confirmApproval(), - iouReport: moneyRequestReport, - iouReportNextStep: nextStep, - betas, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - delegateEmail, - isTrackIntentUser, - ownerLogin: submitterLogin, - }); + runPaymentSelection(); }; if (!applicableSecondaryActions.length) { diff --git a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx index cddb34a778f5..048a162f2880 100644 --- a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx +++ b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx @@ -333,10 +333,11 @@ function MoneyReportHeaderSelectionDropdown({reportID, primaryAction, isReportIn iouReport={moneyRequestReport} onPaymentSelect={onSelectionModePaymentSelect} onWorkspacePolicySelect={(selectedPolicy, triggerKYCFlow) => { - if (shouldBlockAction(undefined, true)) { + const continueWithWorkspace = () => triggerKYCFlow({policy: selectedPolicy}); + if (shouldBlockAction(undefined, true, continueWithWorkspace)) { return; } - triggerKYCFlow({policy: selectedPolicy}); + continueWithWorkspace(); }} onSuccessfulKYC={selectionModeKYCSuccess} primaryAction={primaryAction} diff --git a/src/components/MoneyRequestReportView/SelectionToolbar/index.tsx b/src/components/MoneyRequestReportView/SelectionToolbar/index.tsx index 975c9d346e8c..bcd248d9acb0 100644 --- a/src/components/MoneyRequestReportView/SelectionToolbar/index.tsx +++ b/src/components/MoneyRequestReportView/SelectionToolbar/index.tsx @@ -262,10 +262,11 @@ function SelectionToolbar({reportID, transactions, reportActions}: SelectionTool onSelectionModePaymentSelect={onSelectionModePaymentSelect} selectionModeKYCSuccess={selectionModeKYCSuccess} onWorkspacePolicySelect={(selectedPolicy, triggerKYCFlow) => { - if (shouldBlockAction(undefined, true)) { + const continueWithWorkspace = () => triggerKYCFlow({policy: selectedPolicy}); + if (shouldBlockAction(undefined, true, continueWithWorkspace)) { return; } - triggerKYCFlow({policy: selectedPolicy}); + continueWithWorkspace(); }} primaryAction={primaryAction} selectedTransactionsOptions={selectedTransactionsOptions} diff --git a/src/hooks/useSelectionModePayment.ts b/src/hooks/useSelectionModePayment.ts index a8752cd9817d..0bfe0f734c50 100644 --- a/src/hooks/useSelectionModePayment.ts +++ b/src/hooks/useSelectionModePayment.ts @@ -10,8 +10,6 @@ import {payInvoice, payMoneyRequest} from '@libs/actions/IOU/PayMoneyRequest'; import {generateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import deferModalPresentationAfterPopoverDismiss from '@libs/deferModalPresentationAfterPopoverDismiss'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; -import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; -import Navigation from '@libs/Navigation/Navigation'; import {isTrackOnboardingChoice} from '@libs/OnboardingUtils'; import type {KYCFlowEvent, TriggerKYCFlow, WorkspacePolicyPaymentOption} from '@libs/PaymentUtils'; import {selectPaymentType} from '@libs/PaymentUtils'; @@ -22,11 +20,10 @@ import refreshSearchAfterReportAction from '@libs/SearchRefreshUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import {DYNAMIC_ROUTES} from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; -import {delegateEmailSelector, isUserValidatedSelector} from '@selectors/Account'; +import {delegateEmailSelector} from '@selectors/Account'; import {hasSeenTourSelector} from '@selectors/Onboarding'; import {personalDetailsLoginSelector} from '@selectors/PersonalDetails'; import truncate from 'lodash/truncate'; @@ -46,6 +43,7 @@ import usePaymentOptions from './usePaymentOptions'; import usePermissions from './usePermissions'; import usePolicy from './usePolicy'; import useSearchShouldCalculateTotals from './useSearchShouldCalculateTotals'; +import useVerifyAccountAndResume from './useVerifyAccountAndResume'; type HoldMenuOpenParams = { requestType: ActionHandledType; @@ -96,7 +94,6 @@ function useSelectionModePayment({ const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(moneyRequestReport?.chatReportID)}`); const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${getNonEmptyStringOnyxID(moneyRequestReport?.policyID)}`); const [session] = useOnyx(ONYXKEYS.SESSION); - const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isUserValidatedSelector}); const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, {selector: delegateEmailSelector}); const [allTransactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); const [nextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${getNonEmptyStringOnyxID(moneyRequestReport?.reportID)}`); @@ -124,6 +121,9 @@ function useSelectionModePayment({ const {showLockedAccountModal} = useLockedAccountActions(); const kycWallRef = useContext(KYCWallContext); + // Store the pending payment and resume it after the user validates, instead of dropping it on the way to the magic-code screen. + const {isUserValidated, verifyAccountAndResume} = useVerifyAccountAndResume((retry) => retry?.()); + const expensifyIcons = useMemoizedLazyExpensifyIcons(['Cash', 'ArrowRight', 'Building'] as const); const hasViolations = hasViolationsReportUtils(moneyRequestReport?.reportID, allTransactionViolations, accountID, email ?? ''); @@ -138,7 +138,7 @@ function useSelectionModePayment({ } }; - const shouldBlockAction = (paymentMethodType?: PaymentMethodType, deferBlockingPresentation = false) => { + const shouldBlockAction = (paymentMethodType?: PaymentMethodType, deferBlockingPresentation = false, retry?: () => void) => { if (isDelegateAccessRestricted) { presentBlockingAction(showDelegateNoAccessModal, deferBlockingPresentation); return true; @@ -148,7 +148,7 @@ function useSelectionModePayment({ return true; } if (!isUserValidated && paymentMethodType !== CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { - presentBlockingAction(() => Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.VERIFY_ACCOUNT.path)), deferBlockingPresentation); + presentBlockingAction(() => verifyAccountAndResume(retry), deferBlockingPresentation); return true; } return false; @@ -265,10 +265,11 @@ function useSelectionModePayment({ })(); const handleWorkspaceSelected = (wp: OnyxTypes.Policy) => { - if (shouldBlockAction(undefined, true)) { + const continueWithWorkspace = () => kycWallRef.current?.continueAction?.({policy: wp}); + if (shouldBlockAction(undefined, true, continueWithWorkspace)) { return; } - kycWallRef.current?.continueAction?.({policy: wp}); + continueWithWorkspace(); }; const paymentSubMenuItems: PopoverMenuItem[] = (() => { @@ -318,10 +319,11 @@ function useSelectionModePayment({ }; const onSelectionModePaymentSelect = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => { - if (shouldBlockAction(iouPaymentType, true)) { + const resumePaymentSelect = () => invokePaymentSelect(event, iouPaymentType, triggerKYCFlow); + if (shouldBlockAction(iouPaymentType, true, resumePaymentSelect)) { return; } - invokePaymentSelect(event, iouPaymentType, triggerKYCFlow); + resumePaymentSelect(); }; const selectionModeKYCSuccess = (type?: PaymentMethodType) => { diff --git a/src/hooks/useSelectionModeReportActions.ts b/src/hooks/useSelectionModeReportActions.ts index 230dec84e9ec..4d095b9fea75 100644 --- a/src/hooks/useSelectionModeReportActions.ts +++ b/src/hooks/useSelectionModeReportActions.ts @@ -209,10 +209,11 @@ function useSelectionModeReportActions({ const onSelectionModePaymentSelect = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => { TransitionTracker.runAfterTransitions({ callback: () => { - if (shouldBlockAction(iouPaymentType)) { + const resumePaymentSelect = () => invokePaymentSelect(event, iouPaymentType, triggerKYCFlow); + if (shouldBlockAction(iouPaymentType, false, resumePaymentSelect)) { return; } - invokePaymentSelect(event, iouPaymentType, triggerKYCFlow); + resumePaymentSelect(); }, waitForUpcomingTransition: true, }); From 676af88a4f7b9b1fb70b16b5ecd9789708274f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Musia=C5=82?= Date: Mon, 27 Jul 2026 11:21:57 +0200 Subject: [PATCH 2/2] bump test