-
Notifications
You must be signed in to change notification settings - Fork 4k
feat: standardize pay button for expenses and IOUs #66790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
grgia
merged 27 commits into
Expensify:main
from
getusha:feat-standardize-pay-button-expense-n-ious-2
Aug 1, 2025
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
6c7a985
revert the revert and apply fixes
getusha 281239f
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha 3fea2cb
fix: lint
getusha cffc5e8
fix: more lint
getusha 8fd601d
fix: stale default payment method after workspace change
getusha 8ebd03f
fix: padding and add doc
getusha 6ecce23
fix: address comments and bugs
getusha cd2a7d5
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha af68fdf
fix: lint
getusha 4e6aa84
fix: lint
getusha e8f230c
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha f11ff9a
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha 17419c9
update type description
getusha db6eb4b
address comments
getusha 4b03b9f
fix: show amount on pay button and show correct payment options
getusha b6354e1
prettier
getusha e119dcb
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha 174ad4f
fix: subtle conflicts
getusha 54d6f93
fix: reduce lint warnings
getusha 3465fe2
fix: remove condition to show amount on mark as paid
getusha bfe7fb1
fix: lint
getusha 992af70
fix: bba option not working correctly
getusha 5389b13
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha 474abb4
fix: clear lastUsed and bankAccountID
getusha 773d038
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha 3e89c3a
Merge remote-tracking branch 'exfy/main' into feat-standardize-pay-bu…
getusha 0da978e
fix: lint
getusha File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,21 +3,23 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; | |
| import {Dimensions} from 'react-native'; | ||
| import type {EmitterSubscription, GestureResponderEvent, View} from 'react-native'; | ||
| import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useOnyx from '@hooks/useOnyx'; | ||
| import {openPersonalBankAccountSetupView} from '@libs/actions/BankAccounts'; | ||
| import {completePaymentOnboarding} from '@libs/actions/IOU'; | ||
| import {completePaymentOnboarding, savePreferredPaymentMethod} from '@libs/actions/IOU'; | ||
| import {moveIOUReportToPolicy, moveIOUReportToPolicyAndInviteSubmitter} from '@libs/actions/Report'; | ||
| import getClickedTargetLocation from '@libs/getClickedTargetLocation'; | ||
| import Log from '@libs/Log'; | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import {hasExpensifyPaymentMethod} from '@libs/PaymentUtils'; | ||
| import {getBankAccountRoute, isExpenseReport as isExpenseReportReportUtils, isIOUReport} from '@libs/ReportUtils'; | ||
| import {getBankAccountRoute, getPolicyExpenseChat, isExpenseReport as isExpenseReportReportUtils, isIOUReport} from '@libs/ReportUtils'; | ||
| import {kycWallRef} from '@userActions/PaymentMethods'; | ||
| import {createWorkspaceFromIOUPayment} from '@userActions/Policy/Policy'; | ||
| import {setKYCWallSource} from '@userActions/Wallet'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import ROUTES from '@src/ROUTES'; | ||
| import type {BankAccountList} from '@src/types/onyx'; | ||
| import type {BankAccountList, Policy} from '@src/types/onyx'; | ||
| import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; | ||
| import {getEmptyObject} from '@src/types/utils/EmptyObject'; | ||
| import viewRef from '@src/types/utils/viewRef'; | ||
|
|
@@ -54,6 +56,8 @@ function KYCWall({ | |
| const [reimbursementAccount] = useOnyx(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {canBeMissing: true}); | ||
| const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`, {canBeMissing: true}); | ||
|
|
||
| const {formatPhoneNumber} = useLocalize(); | ||
|
|
||
| const anchorRef = useRef<HTMLDivElement | View>(null); | ||
| const transferBalanceButtonRef = useRef<HTMLDivElement | View | null>(null); | ||
|
|
||
|
|
@@ -64,6 +68,8 @@ function KYCWall({ | |
| anchorPositionHorizontal: 0, | ||
| }); | ||
|
|
||
| const [lastPaymentMethod] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD, {canBeMissing: true}); | ||
|
|
||
| const getAnchorPosition = useCallback( | ||
| (domRect: DomRect): AnchorPosition => { | ||
| if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP) { | ||
|
|
@@ -103,31 +109,55 @@ function KYCWall({ | |
| }, [getAnchorPosition]); | ||
|
|
||
| const selectPaymentMethod = useCallback( | ||
| (paymentMethod: PaymentMethod) => { | ||
| onSelectPaymentMethod(paymentMethod); | ||
| (paymentMethod?: PaymentMethod, policy?: Policy) => { | ||
| if (paymentMethod) { | ||
| onSelectPaymentMethod(paymentMethod); | ||
| } | ||
|
|
||
| if (paymentMethod === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT) { | ||
| openPersonalBankAccountSetupView({shouldSetUpUSBankAccount: isIOUReport(iouReport)}); | ||
| } else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) { | ||
| Navigation.navigate(addDebitCardRoute ?? ROUTES.HOME); | ||
| } else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT) { | ||
| } else if (paymentMethod === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT || policy) { | ||
| if (iouReport && isIOUReport(iouReport)) { | ||
| if (policy) { | ||
| const policyExpenseChatReportID = getPolicyExpenseChat(iouReport.ownerAccountID, policy.id)?.reportID; | ||
| if (!policyExpenseChatReportID) { | ||
| const {policyExpenseChatReportID: newPolicyExpenseChatReportID} = moveIOUReportToPolicyAndInviteSubmitter(iouReport.reportID, policy.id, formatPhoneNumber) ?? {}; | ||
| savePreferredPaymentMethod(iouReport.policyID, policy.id, CONST.LAST_PAYMENT_METHOD.IOU, lastPaymentMethod?.[policy.id]); | ||
| Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(newPolicyExpenseChatReportID)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. {BZ CHECKLIST} there was a regression, the navigation occurs too soon—before the optimistic report from moveIOUReportToPolicyAndInviteSubmitter is fully merged into Onyx, this was fixed using |
||
| } else { | ||
| moveIOUReportToPolicy(iouReport.reportID, policy.id, true); | ||
| savePreferredPaymentMethod(iouReport.policyID, policy.id, CONST.LAST_PAYMENT_METHOD.IOU, lastPaymentMethod?.[policy.id]); | ||
| Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(policyExpenseChatReportID)); | ||
| } | ||
|
|
||
| if (policy?.achAccount) { | ||
| return; | ||
| } | ||
| // Navigate to the bank account set up flow for this specific policy | ||
| Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(policy.id)); | ||
| return; | ||
| } | ||
|
|
||
| const {policyID, workspaceChatReportID, reportPreviewReportActionID, adminsChatReportID} = createWorkspaceFromIOUPayment(iouReport) ?? {}; | ||
| if (policyID && iouReport?.policyID) { | ||
| savePreferredPaymentMethod(iouReport.policyID, policyID, CONST.LAST_PAYMENT_METHOD.IOU, lastPaymentMethod?.[iouReport?.policyID]); | ||
| } | ||
| completePaymentOnboarding(CONST.PAYMENT_SELECTED.BBA, adminsChatReportID, policyID); | ||
| if (workspaceChatReportID) { | ||
| Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(workspaceChatReportID, reportPreviewReportActionID)); | ||
| } | ||
|
|
||
| // Navigate to the bank account set up flow for this specific policy | ||
| Navigation.navigate(ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.getRoute(policyID)); | ||
|
|
||
| return; | ||
| } | ||
| const bankAccountRoute = addBankAccountRoute ?? getBankAccountRoute(chatReport); | ||
| Navigation.navigate(bankAccountRoute); | ||
| } | ||
| }, | ||
| [addBankAccountRoute, addDebitCardRoute, chatReport, iouReport, onSelectPaymentMethod], | ||
| [addBankAccountRoute, addDebitCardRoute, chatReport, iouReport, onSelectPaymentMethod, formatPhoneNumber, lastPaymentMethod], | ||
| ); | ||
|
|
||
| /** | ||
|
|
@@ -137,7 +167,7 @@ function KYCWall({ | |
| * | ||
| */ | ||
| const continueAction = useCallback( | ||
| (event?: GestureResponderEvent | KeyboardEvent, iouPaymentType?: PaymentMethodType) => { | ||
| (event?: GestureResponderEvent | KeyboardEvent, iouPaymentType?: PaymentMethodType, paymentMethod?: PaymentMethod, policy?: Policy) => { | ||
| const currentSource = walletTerms?.source ?? source; | ||
|
|
||
| /** | ||
|
|
@@ -171,6 +201,19 @@ function KYCWall({ | |
| return; | ||
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing | ||
| if (paymentMethod || policy) { | ||
| setShouldShowAddPaymentMenu(false); | ||
| selectPaymentMethod(paymentMethod, policy); | ||
| return; | ||
| } | ||
|
|
||
| if (iouPaymentType && isExpenseReport) { | ||
| setShouldShowAddPaymentMenu(false); | ||
| selectPaymentMethod(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT); | ||
| return; | ||
| } | ||
|
|
||
| const clickedElementLocation = getClickedTargetLocation(targetElement as HTMLDivElement); | ||
| const position = getAnchorPosition(clickedElementLocation); | ||
|
|
||
|
|
@@ -183,13 +226,20 @@ function KYCWall({ | |
| // Ask the user to upgrade to a gold wallet as this means they have not yet gone through our Know Your Customer (KYC) checks | ||
| const hasActivatedWallet = userWallet?.tierName && [CONST.WALLET.TIER_NAME.GOLD, CONST.WALLET.TIER_NAME.PLATINUM].some((name) => name === userWallet.tierName); | ||
|
|
||
| if (!hasActivatedWallet) { | ||
| if (!hasActivatedWallet && !policy) { | ||
| Log.info('[KYC Wallet] User does not have active wallet'); | ||
|
|
||
| Navigation.navigate(enablePaymentsRoute); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing | ||
| if (policy || (paymentMethod && (!hasActivatedWallet || paymentMethod !== CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT))) { | ||
| setShouldShowAddPaymentMenu(false); | ||
| selectPaymentMethod(paymentMethod, policy); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| Log.info('[KYC Wallet] User has valid payment method and passed KYC checks or did not need them'); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We neglected to allow truncation here, so we fixed it in #70837