-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Add pay to secondary actions in MoneyReportHeader #62107
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
lakchote
merged 19 commits into
Expensify:main
from
software-mansion-labs:korytko/add-pay-secondary-action
May 27, 2025
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
df61ff9
Add Pay as a secondary action in header
JakubKorytko 0d36c6c
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko 72417ca
Fixes after merge
JakubKorytko 96e09b1
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko 5e9f4e6
Correct the style of back button
JakubKorytko a0f40a2
Re-run check
JakubKorytko 922119d
Fix remove hold button
JakubKorytko fb7d993
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko 15f7958
Hide hold button when report is not submitted
JakubKorytko da67d0c
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko 03140c5
Temporary fix for report preview actions
JakubKorytko f32083b
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko d017376
Fix typo in useSelectedTransactionsActions
JakubKorytko 4a09156
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko efadd45
Remove duplicated backButtonText
JakubKorytko 9ed291e
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko 6d787d1
Fix for iOS
JakubKorytko 3f0cef3
Merge branch 'main' into korytko/add-pay-secondary-action
JakubKorytko d75e82b
Use camelCase for onSubItemSelected
JakubKorytko 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import useLocalize from '@hooks/useLocalize'; | |
| import useMobileSelectionMode from '@hooks/useMobileSelectionMode'; | ||
| import useNetwork from '@hooks/useNetwork'; | ||
| import usePaymentAnimations from '@hooks/usePaymentAnimations'; | ||
| import usePaymentOptions from '@hooks/usePaymentOptions'; | ||
| import usePermissions from '@hooks/usePermissions'; | ||
| import useReportIsArchived from '@hooks/useReportIsArchived'; | ||
| import useResponsiveLayout from '@hooks/useResponsiveLayout'; | ||
|
|
@@ -20,9 +21,11 @@ import {deleteAppReport, downloadReportPDF, exportReportToCSV, exportReportToPDF | |
| import {getThreadReportIDsForTransactions, getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import {buildOptimisticNextStepForPreventSelfApprovalsEnabled} from '@libs/NextStepUtils'; | ||
| import {isSecondaryActionAPaymentOption, selectPaymentType} from '@libs/PaymentUtils'; | ||
| import type {KYCFlowEvent, TriggerKYCFlow} from '@libs/PaymentUtils'; | ||
| import {getValidConnectedIntegration} from '@libs/PolicyUtils'; | ||
| import {getOriginalMessage, getReportAction, isMoneyRequestAction} from '@libs/ReportActionsUtils'; | ||
| import {getReportPrimaryAction} from '@libs/ReportPrimaryActionUtils'; | ||
| import {getAllExpensesToHoldIfApplicable, getReportPrimaryAction} from '@libs/ReportPrimaryActionUtils'; | ||
| import {getSecondaryReportActions} from '@libs/ReportSecondaryActionUtils'; | ||
| import { | ||
| changeMoneyRequestHoldStatus, | ||
|
|
@@ -95,6 +98,7 @@ import Header from './Header'; | |
| import HeaderWithBackButton from './HeaderWithBackButton'; | ||
| import Icon from './Icon'; | ||
| import * as Expensicons from './Icon/Expensicons'; | ||
| import KYCWall from './KYCWall'; | ||
| import type {PaymentMethod} from './KYCWall/types'; | ||
| import LoadingBar from './LoadingBar'; | ||
| import Modal from './Modal'; | ||
|
|
@@ -103,6 +107,7 @@ import MoneyReportHeaderStatusBarSkeleton from './MoneyReportHeaderStatusBarSkel | |
| import type {MoneyRequestHeaderStatusBarProps} from './MoneyRequestHeaderStatusBar'; | ||
| import MoneyRequestHeaderStatusBar from './MoneyRequestHeaderStatusBar'; | ||
| import {useMoneyRequestReportContext} from './MoneyRequestReportView/MoneyRequestReportContext'; | ||
| import type {PopoverMenuItem} from './PopoverMenu'; | ||
| import type {ActionHandledType} from './ProcessMoneyReportHoldMenu'; | ||
| import ProcessMoneyReportHoldMenu from './ProcessMoneyReportHoldMenu'; | ||
| import AnimatedSettlementButton from './SettlementButton/AnimatedSettlementButton'; | ||
|
|
@@ -151,6 +156,7 @@ function MoneyReportHeader({ | |
| const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${moneyRequestReport?.chatReportID}`, {canBeMissing: true}); | ||
| // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing | ||
| const [nextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${moneyRequestReport?.reportID}`, {canBeMissing: true}); | ||
| const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: (account) => account?.validated, canBeMissing: true}); | ||
| const [transactionThreadReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${transactionThreadReportID}`, {canBeMissing: true}); | ||
| const [reportPDFFilename] = useOnyx(`${ONYXKEYS.COLLECTION.NVP_EXPENSIFY_REPORT_PDF_FILENAME}${moneyRequestReport?.reportID}`, {canBeMissing: true}) ?? null; | ||
| const [download] = useOnyx(`${ONYXKEYS.COLLECTION.DOWNLOAD}${reportPDFFilename}`, {canBeMissing: true}); | ||
|
|
@@ -316,7 +322,7 @@ function MoneyReportHeader({ | |
| if (isDelegateAccessRestricted) { | ||
| setIsNoDelegateAccessMenuVisible(true); | ||
| } else if (isAnyTransactionOnHold) { | ||
| setIsHoldMenuVisible(true); | ||
| InteractionManager.runAfterInteractions(() => setIsHoldMenuVisible(true)); | ||
| } else if (isInvoiceReport) { | ||
| startAnimation(); | ||
| payInvoice(type, chatReport, moneyRequestReport, payAsBusiness, methodID, paymentMethod); | ||
|
|
@@ -445,6 +451,27 @@ function MoneyReportHeader({ | |
| } | ||
| }, [connectedIntegration, exportModalStatus, moneyRequestReport?.reportID]); | ||
|
|
||
| const getAmount = (actionType: ValueOf<typeof CONST.REPORT.REPORT_PREVIEW_ACTIONS>) => ({ | ||
| formattedAmount: getTotalAmountForIOUReportPreviewButton(moneyRequestReport, policy, actionType), | ||
| }); | ||
|
|
||
| const {formattedAmount: payAmount} = getAmount(CONST.REPORT.PRIMARY_ACTIONS.PAY); | ||
| const {formattedAmount: totalAmount} = hasOnlyHeldExpenses ? getAmount(CONST.REPORT.REPORT_PREVIEW_ACTIONS.REVIEW) : getAmount(CONST.REPORT.PRIMARY_ACTIONS.PAY); | ||
|
|
||
| const paymentButtonOptions = usePaymentOptions({ | ||
| addBankAccountRoute: bankAccountRoute, | ||
| currency: moneyRequestReport?.currency, | ||
| iouReport: moneyRequestReport, | ||
| chatReportID: chatReport?.reportID, | ||
| formattedAmount: totalAmount, | ||
| policyID: moneyRequestReport?.policyID, | ||
| onPress: confirmPayment, | ||
| shouldHidePaymentOptions: !shouldShowPayButton, | ||
| shouldShowApproveButton, | ||
| shouldDisableApproveButton, | ||
| onlyShowPayElsewhere, | ||
| }); | ||
|
|
||
| const addExpenseDropdownOptions: Array<DropdownOption<ValueOf<typeof CONST.REPORT.ADD_EXPENSE_OPTIONS>>> = useMemo( | ||
| () => [ | ||
| { | ||
|
|
@@ -470,10 +497,6 @@ function MoneyReportHeader({ | |
| [moneyRequestReport?.reportID, translate], | ||
| ); | ||
|
|
||
| const getAmount = (actionType: ValueOf<typeof CONST.REPORT.REPORT_PREVIEW_ACTIONS>) => ({ | ||
| formattedAmount: getTotalAmountForIOUReportPreviewButton(moneyRequestReport, policy, actionType), | ||
| }); | ||
|
|
||
| const primaryActionsImplementation = { | ||
| [CONST.REPORT.PRIMARY_ACTIONS.SUBMIT]: ( | ||
| <Button | ||
|
|
@@ -512,7 +535,7 @@ function MoneyReportHeader({ | |
| shouldHidePaymentOptions={!shouldShowPayButton} | ||
| shouldShowApproveButton={shouldShowApproveButton} | ||
| shouldDisableApproveButton={shouldDisableApproveButton} | ||
| formattedAmount={getAmount(CONST.REPORT.PRIMARY_ACTIONS.PAY).formattedAmount} | ||
| formattedAmount={payAmount} | ||
| isDisabled={isOffline && !canAllowSettlement} | ||
| isLoading={!isOffline && !canAllowSettlement} | ||
| /> | ||
|
|
@@ -541,6 +564,13 @@ function MoneyReportHeader({ | |
| onPress={() => { | ||
| const parentReportAction = getReportAction(moneyRequestReport?.parentReportID, moneyRequestReport?.parentReportActionID); | ||
|
|
||
| const IOUActions = getAllExpensesToHoldIfApplicable(moneyRequestReport, reportActions); | ||
|
|
||
| if (IOUActions.length) { | ||
| IOUActions.forEach(changeMoneyRequestHoldStatus); | ||
| return; | ||
| } | ||
|
|
||
| const moneyRequestAction = transactionThreadReportID ? requestParentReportAction : parentReportAction; | ||
| if (!moneyRequestAction) { | ||
| return; | ||
|
|
@@ -595,7 +625,10 @@ function MoneyReportHeader({ | |
| return getSecondaryReportActions(moneyRequestReport, transactions, violations, policy, reportNameValuePairs, reportActions, canUseRetractNewDot, canUseTableReportView, policies); | ||
| }, [moneyRequestReport, transactions, violations, policy, reportNameValuePairs, reportActions, canUseRetractNewDot, canUseTableReportView, policies]); | ||
|
|
||
| const secondaryActionsImplementation: Record<ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>, DropdownOption<ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>>> = { | ||
| const secondaryActionsImplementation: Record< | ||
| ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>, | ||
| DropdownOption<ValueOf<typeof CONST.REPORT.SECONDARY_ACTIONS>> & Pick<PopoverMenuItem, 'backButtonText'> | ||
| > = { | ||
| [CONST.REPORT.SECONDARY_ACTIONS.VIEW_DETAILS]: { | ||
| value: CONST.REPORT.SECONDARY_ACTIONS.VIEW_DETAILS, | ||
| text: translate('iou.viewDetails'), | ||
|
|
@@ -773,9 +806,9 @@ function MoneyReportHeader({ | |
| }, | ||
| [CONST.REPORT.SECONDARY_ACTIONS.ADD_EXPENSE]: { | ||
| text: translate('iou.addExpense'), | ||
| backButtonText: translate('iou.addExpense'), | ||
| icon: Expensicons.Plus, | ||
| value: CONST.REPORT.SECONDARY_ACTIONS.ADD_EXPENSE, | ||
| backButtonText: translate('iou.addExpense'), | ||
| subMenuItems: addExpenseDropdownOptions, | ||
| onSelected: () => { | ||
| if (!moneyRequestReport?.reportID) { | ||
|
|
@@ -784,6 +817,13 @@ function MoneyReportHeader({ | |
| startMoneyRequest(CONST.IOU.TYPE.SUBMIT, moneyRequestReport?.reportID); | ||
| }, | ||
| }, | ||
| [CONST.REPORT.SECONDARY_ACTIONS.PAY]: { | ||
| text: translate('iou.settlePayment', {formattedAmount: totalAmount}), | ||
| icon: Expensicons.Cash, | ||
| value: CONST.REPORT.SECONDARY_ACTIONS.PAY, | ||
| backButtonText: translate('iou.settlePayment', {formattedAmount: totalAmount}), | ||
| subMenuItems: Object.values(paymentButtonOptions), | ||
| }, | ||
| }; | ||
|
|
||
| const applicableSecondaryActions = secondaryActions.map((action) => secondaryActionsImplementation[action]).filter((action) => action?.shouldShow !== false); | ||
|
|
@@ -833,6 +873,43 @@ function MoneyReportHeader({ | |
| <Text>{translate('iou.reopenExportedReportConfirmation', {connectionName: integrationNameFromExportMessage ?? ''})}</Text> | ||
| </Text> | ||
| ); | ||
| const onPaymentSelect = (event: KYCFlowEvent, iouPaymentType: PaymentMethodType, triggerKYCFlow: TriggerKYCFlow) => | ||
| selectPaymentType(event, iouPaymentType, triggerKYCFlow, policy, confirmPayment, isUserValidated, confirmApproval, moneyRequestReport); | ||
|
|
||
| const KYCMoreDropdown = ( | ||
|
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. |
||
| <KYCWall | ||
| onSuccessfulKYC={(payment) => confirmPayment(payment)} | ||
| enablePaymentsRoute={ROUTES.ENABLE_PAYMENTS} | ||
| addBankAccountRoute={bankAccountRoute} | ||
| isDisabled={isOffline} | ||
| source={CONST.KYC_WALL_SOURCE.REPORT} | ||
| chatReportID={chatReport?.reportID} | ||
| iouReport={moneyRequestReport} | ||
| anchorAlignment={{ | ||
| horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, // button is at left, so horizontal anchor is at LEFT | ||
| vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP, // we assume that popover menu opens below the button, anchor is at TOP | ||
| }} | ||
| > | ||
| {(triggerKYCFlow, buttonRef) => ( | ||
| <ButtonWithDropdownMenu | ||
| success={false} | ||
| onPress={() => {}} | ||
| onSubItemSelected={(item, index, event) => { | ||
| if (!isSecondaryActionAPaymentOption(item)) { | ||
| return; | ||
| } | ||
| onPaymentSelect(event, item.value, triggerKYCFlow); | ||
| }} | ||
| buttonRef={buttonRef} | ||
| shouldAlwaysShowDropdownMenu | ||
| customText={translate('common.more')} | ||
| options={applicableSecondaryActions} | ||
| isSplitButton={false} | ||
| wrapperStyle={shouldDisplayNarrowVersion && [!primaryAction && styles.flex1]} | ||
| /> | ||
| )} | ||
| </KYCWall> | ||
| ); | ||
|
|
||
| return ( | ||
| <View style={[styles.pt0, styles.borderBottom]}> | ||
|
|
@@ -850,16 +927,7 @@ function MoneyReportHeader({ | |
| {!shouldDisplayNarrowVersion && ( | ||
| <View style={[styles.flexRow, styles.gap2]}> | ||
| {!!primaryAction && !shouldShowSelectedTransactionsButton && primaryActionsImplementation[primaryAction]} | ||
| {!!applicableSecondaryActions.length && !shouldShowSelectedTransactionsButton && ( | ||
| <ButtonWithDropdownMenu | ||
| success={false} | ||
| onPress={() => {}} | ||
| shouldAlwaysShowDropdownMenu | ||
| customText={translate('common.more')} | ||
| options={applicableSecondaryActions} | ||
| isSplitButton={false} | ||
| /> | ||
| )} | ||
| {!!applicableSecondaryActions.length && !shouldShowSelectedTransactionsButton && KYCMoreDropdown} | ||
| {shouldShowSelectedTransactionsButton && ( | ||
| <View> | ||
| <ButtonWithDropdownMenu | ||
|
|
@@ -877,17 +945,7 @@ function MoneyReportHeader({ | |
| {shouldDisplayNarrowVersion && !shouldShowSelectedTransactionsButton && ( | ||
| <View style={[styles.flexRow, styles.gap2, styles.pb3, styles.ph5, styles.w100, styles.alignItemsCenter, styles.justifyContentCenter]}> | ||
| {!!primaryAction && <View style={[styles.flex1]}>{primaryActionsImplementation[primaryAction]}</View>} | ||
| {!!applicableSecondaryActions.length && ( | ||
| <ButtonWithDropdownMenu | ||
| success={false} | ||
| onPress={() => {}} | ||
| shouldAlwaysShowDropdownMenu | ||
| customText={translate('common.more')} | ||
| options={applicableSecondaryActions} | ||
| isSplitButton={false} | ||
| wrapperStyle={[!primaryAction && styles.flex1]} | ||
| /> | ||
| )} | ||
| {!!applicableSecondaryActions.length && KYCMoreDropdown} | ||
| </View> | ||
| )} | ||
| {isMoreContentShown && ( | ||
|
|
||
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
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.
Uh oh!
There was an error while loading. Please reload this page.