Skip to content

Commit

Permalink
Merge pull request #48639 from Expensify/revert-48421-revert-48117-re…
Browse files Browse the repository at this point in the history
…vert-37174-36301-clean-up-payment-options

[CP Staging] Revert "Revert "Revert "Consolidate options on settlement "Pay" button"""

(cherry picked from commit dcb94e7)

(CP triggered by mountiny)
  • Loading branch information
mountiny authored and OSBotify committed Sep 5, 2024
1 parent 6fa35f5 commit 4745fc0
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 55 deletions.
2 changes: 1 addition & 1 deletion src/components/ButtonWithDropdownMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function ButtonWithDropdownMenu<IValueType>({
if ('measureInWindow' in dropdownAnchor.current) {
dropdownAnchor.current.measureInWindow((x, y, w, h) => {
setPopoverAnchorPosition({
horizontal: x + w + h,
horizontal: x + w,
vertical:
anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP
? y + h + CONST.MODAL.POPOVER_MENU_PADDING // if vertical anchorAlignment is TOP, menu will open below the button and we need to add the height of button and padding
Expand Down
3 changes: 1 addition & 2 deletions src/components/ButtonWithDropdownMenu/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type {RefObject} from 'react';
import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
import type {ValueOf} from 'type-fest';
import type {PaymentMethodType} from '@components/KYCWall/types';
import type CONST from '@src/CONST';
import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import type IconAsset from '@src/types/utils/IconAsset';

type PaymentType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE | PaymentMethodType>;
type PaymentType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE>;

type WorkspaceMemberBulkActionType = DeepValueOf<typeof CONST.POLICY.MEMBERS_BULK_ACTION_TYPES>;

Expand Down
129 changes: 112 additions & 17 deletions src/components/KYCWall/BaseKYCWall.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import React, {useCallback, useRef} from 'react';
import type {GestureResponderEvent, View} from 'react-native';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {Dimensions} from 'react-native';
import type {EmitterSubscription, GestureResponderEvent, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu';
import * as BankAccounts from '@libs/actions/BankAccounts';
import getClickedTargetLocation from '@libs/getClickedTargetLocation';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import * as PaymentUtils from '@libs/PaymentUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as PaymentMethods from '@userActions/PaymentMethods';
import * as Policy from '@userActions/Policy/Policy';
import * as Wallet from '@userActions/Wallet';
import CONST from '@src/CONST';
Expand All @@ -15,7 +19,10 @@ import ROUTES from '@src/ROUTES';
import type {BankAccountList, FundList, ReimbursementAccount, UserWallet, WalletTerms} from '@src/types/onyx';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import viewRef from '@src/types/utils/viewRef';
import type {KYCWallProps, PaymentMethod} from './types';
import type {AnchorPosition, DomRect, KYCWallProps, PaymentMethod} from './types';

// This sets the Horizontal anchor position offset for POPOVER MENU.
const POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET = 20;

type BaseKYCWallOnyxProps = {
/** The user's wallet */
Expand All @@ -42,6 +49,10 @@ type BaseKYCWallProps = KYCWallProps & BaseKYCWallOnyxProps;
function KYCWall({
addBankAccountRoute,
addDebitCardRoute,
anchorAlignment = {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
},
bankAccountList = {},
chatReportID = '',
children,
Expand All @@ -52,13 +63,60 @@ function KYCWall({
onSuccessfulKYC,
reimbursementAccount,
shouldIncludeDebitCard = true,
shouldListenForResize = false,
source,
userWallet,
walletTerms,
shouldShowPersonalBankAccountOption = false,
}: BaseKYCWallProps) {
const anchorRef = useRef<HTMLDivElement | View>(null);
const transferBalanceButtonRef = useRef<HTMLDivElement | View | null>(null);

const [shouldShowAddPaymentMenu, setShouldShowAddPaymentMenu] = useState(false);

const [anchorPosition, setAnchorPosition] = useState({
anchorPositionVertical: 0,
anchorPositionHorizontal: 0,
});

const getAnchorPosition = useCallback(
(domRect: DomRect): AnchorPosition => {
if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP) {
return {
anchorPositionVertical: domRect.top + domRect.height + CONST.MODAL.POPOVER_MENU_PADDING,
anchorPositionHorizontal: domRect.left + POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET,
};
}

return {
anchorPositionVertical: domRect.top - CONST.MODAL.POPOVER_MENU_PADDING,
anchorPositionHorizontal: domRect.left,
};
},
[anchorAlignment.vertical],
);

/**
* Set position of the transfer payment menu
*/
const setPositionAddPaymentMenu = ({anchorPositionVertical, anchorPositionHorizontal}: AnchorPosition) => {
setAnchorPosition({
anchorPositionVertical,
anchorPositionHorizontal,
});
};

const setMenuPosition = useCallback(() => {
if (!transferBalanceButtonRef.current) {
return;
}

const buttonPosition = getClickedTargetLocation(transferBalanceButtonRef.current as HTMLDivElement);
const position = getAnchorPosition(buttonPosition);

setPositionAddPaymentMenu(position);
}, [getAnchorPosition]);

const selectPaymentMethod = useCallback(
(paymentMethod: PaymentMethod) => {
onSelectPaymentMethod(paymentMethod);
Expand Down Expand Up @@ -101,6 +159,11 @@ function KYCWall({
*/
Wallet.setKYCWallSource(source, chatReportID);

if (shouldShowAddPaymentMenu) {
setShouldShowAddPaymentMenu(false);
return;
}

// Use event target as fallback if anchorRef is null for safety
const targetElement = anchorRef.current ?? (event?.currentTarget as HTMLDivElement);

Expand All @@ -121,19 +184,11 @@ function KYCWall({
return;
}

switch (iouPaymentType) {
case CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT:
selectPaymentMethod(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT);
break;
case CONST.PAYMENT_METHODS.DEBIT_CARD:
selectPaymentMethod(CONST.PAYMENT_METHODS.DEBIT_CARD);
break;
case CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT:
selectPaymentMethod(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT);
break;
default:
break;
}
const clickedElementLocation = getClickedTargetLocation(targetElement as HTMLDivElement);
const position = getAnchorPosition(clickedElementLocation);

setPositionAddPaymentMenu(position);
setShouldShowAddPaymentMenu(true);

return;
}
Expand All @@ -159,18 +214,58 @@ function KYCWall({
chatReportID,
enablePaymentsRoute,
fundList,
getAnchorPosition,
iouReport,
onSuccessfulKYC,
reimbursementAccount?.achData?.state,
selectPaymentMethod,
shouldIncludeDebitCard,
shouldShowAddPaymentMenu,
source,
userWallet?.tierName,
walletTerms?.source,
],
);

return <>{children(continueAction, viewRef(anchorRef))}</>;
useEffect(() => {
let dimensionsSubscription: EmitterSubscription | null = null;

PaymentMethods.kycWallRef.current = {continueAction};

if (shouldListenForResize) {
dimensionsSubscription = Dimensions.addEventListener('change', setMenuPosition);
}

return () => {
if (shouldListenForResize && dimensionsSubscription) {
dimensionsSubscription.remove();
}

PaymentMethods.kycWallRef.current = null;
};
}, [chatReportID, setMenuPosition, shouldListenForResize, continueAction]);

return (
<>
<AddPaymentMethodMenu
isVisible={shouldShowAddPaymentMenu}
iouReport={iouReport}
onClose={() => setShouldShowAddPaymentMenu(false)}
anchorRef={anchorRef}
anchorPosition={{
vertical: anchorPosition.anchorPositionVertical,
horizontal: anchorPosition.anchorPositionHorizontal,
}}
anchorAlignment={anchorAlignment}
onItemSelected={(item: PaymentMethod) => {
setShouldShowAddPaymentMenu(false);
selectPaymentMethod(item);
}}
shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption}
/>
{children(continueAction, viewRef(anchorRef))}
</>
);
}

KYCWall.displayName = 'BaseKYCWall';
Expand Down
1 change: 1 addition & 0 deletions src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,7 @@ function MoneyRequestConfirmationList({
onPress={confirm}
enablePaymentsRoute={ROUTES.IOU_SEND_ENABLE_PAYMENTS}
addBankAccountRoute={bankAccountRoute}
shouldShowPersonalBankAccountOption
currency={iouCurrencyCode}
policyID={policyID}
buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE}
Expand Down
45 changes: 20 additions & 25 deletions src/components/SettlementButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ type SettlementButtonProps = SettlementButtonOnyxProps & {
/** The anchor alignment of the popover menu for KYC wall popover */
kycWallAnchorAlignment?: AnchorAlignment;

/** Whether the personal bank account option should be shown */
shouldShowPersonalBankAccountOption?: boolean;

/** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */
enterKeyEventListenerPriority?: number;

Expand Down Expand Up @@ -144,6 +147,7 @@ function SettlementButton({
shouldShowApproveButton = false,
shouldDisableApproveButton = false,
style,
shouldShowPersonalBankAccountOption = false,
enterKeyEventListenerPriority = 0,
confirmApproval,
policy,
Expand All @@ -166,35 +170,25 @@ function SettlementButton({
(!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport) && policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL);
const shouldShowPayElsewhereOption = (!isPaidGroupPolicy || policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL) && !isInvoiceReport;
const paymentButtonOptions = useMemo(() => {
const buttonOptions = [];
const isExpenseReport = ReportUtils.isExpenseReport(iouReport);
const paymentMethods = {
[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]: {
text: translate('iou.settleExpensify', {formattedAmount}),
icon: Expensicons.Wallet,
value: CONST.IOU.PAYMENT_TYPE.EXPENSIFY,
},
[CONST.IOU.PAYMENT_TYPE.VBBA]: {
text: translate('iou.settleExpensify', {formattedAmount}),
icon: Expensicons.Wallet,
value: CONST.IOU.PAYMENT_TYPE.VBBA,
},
[CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]: {
text: translate('iou.settlePersonalBank', {formattedAmount}),
icon: Expensicons.Bank,
value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT,
},
[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]: {
text: translate('iou.settleBusinessBank', {formattedAmount}),
icon: Expensicons.Bank,
value: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT,
},
[CONST.PAYMENT_METHODS.DEBIT_CARD]: {
text: translate('iou.settleDebitCard', {formattedAmount}),
icon: Expensicons.CreditCard,
value: CONST.PAYMENT_METHODS.DEBIT_CARD,
},
[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]: {
text: translate('iou.payElsewhere', {formattedAmount}),
icon: Expensicons.Cash,
value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
},
};
const buttonOptions = [];
const approveButtonOption = {
text: translate('iou.approve'),
icon: Expensicons.ThumbsUp,
Expand All @@ -212,10 +206,12 @@ function SettlementButton({
// If the user has previously chosen a specific payment option or paid for some expense,
// let's use the last payment method or use default.
const paymentMethod = nvpLastPaymentMethod?.[policyID] ?? '-1';
if (canUseWallet || (isExpenseReport && shouldShowPaywithExpensifyOption)) {
buttonOptions.push(paymentMethods[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]);
if (canUseWallet) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]);
}
if (isExpenseReport && shouldShowPaywithExpensifyOption) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]);
}

if (shouldShowPayElsewhereOption) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]);
}
Expand Down Expand Up @@ -275,12 +271,7 @@ function SettlementButton({
return;
}

if (
iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA ||
iouPaymentType === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT ||
iouPaymentType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ||
iouPaymentType === CONST.PAYMENT_METHODS.DEBIT_CARD
) {
if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) {
triggerKYCFlow(event, iouPaymentType);
BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS);
return;
Expand Down Expand Up @@ -314,14 +305,18 @@ function SettlementButton({
chatReportID={chatReportID}
iouReport={iouReport}
anchorAlignment={kycWallAnchorAlignment}
shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption}
>
{(triggerKYCFlow, buttonRef) => (
<ButtonWithDropdownMenu<PaymentType>
success
onOptionsMenuShow={onPaymentOptionsShow}
onOptionsMenuHide={onPaymentOptionsHide}
buttonRef={buttonRef}
shouldAlwaysShowDropdownMenu={isInvoiceReport}
customText={isInvoiceReport ? translate('iou.settlePayment', {formattedAmount}) : undefined}
menuHeaderText={isInvoiceReport ? translate('workspace.invoices.paymentMethods.chooseInvoiceMethod') : undefined}
isSplitButton={!isInvoiceReport}
isDisabled={isDisabled}
isLoading={isLoading}
onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)}
Expand Down
3 changes: 0 additions & 3 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -803,9 +803,6 @@ export default {
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pay ${formattedAmount}`,
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} as a business` : `Pay as a business`),
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} elsewhere` : `Pay elsewhere`),
settlePersonalBank: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with personal bank account` : `Pay with personal bank account`),
settleBusinessBank: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with business bank account` : `Pay with business bank account`),
settleDebitCard: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with debit card` : `Pay with debit card`),
nextStep: 'Next steps',
finished: 'Finished',
sendInvoice: ({amount}: RequestAmountParams) => `Send ${amount} invoice`,
Expand Down
5 changes: 0 additions & 5 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -796,11 +796,6 @@ export default {
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pagar ${formattedAmount}`,
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} como negocio` : `Pagar como empresa`),
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} de otra forma` : `Pagar de otra forma`),
settlePersonalBank: ({formattedAmount}: SettleExpensifyCardParams) =>
formattedAmount ? `Pagar ${formattedAmount} con cuenta bancaria personal` : `Pagar con cuenta bancaria personal`,
settleBusinessBank: ({formattedAmount}: SettleExpensifyCardParams) =>
formattedAmount ? `Pagar ${formattedAmount} con cuenta bancaria comercial` : `Pagar con cuenta bancaria comercial`,
settleDebitCard: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} con tarjeta de débito` : `Pagar con tarjeta de débito`),
nextStep: 'Pasos siguientes',
finished: 'Finalizado',
sendInvoice: ({amount}: RequestAmountParams) => `Enviar factura de ${amount}`,
Expand Down
1 change: 0 additions & 1 deletion src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ type RequestCountParams = {

type SettleExpensifyCardParams = {
formattedAmount: string;
available?: boolean;
};

type RequestAmountParams = {amount: string};
Expand Down
1 change: 1 addition & 0 deletions src/pages/iou/MoneyRequestAmountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ function MoneyRequestAmountForm(
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
}}
shouldShowPersonalBankAccountOption
enterKeyEventListenerPriority={1}
/>
) : (
Expand Down
2 changes: 1 addition & 1 deletion src/types/onyx/OriginalMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type ReportActionName from './ReportActionName';
type JoinWorkspaceResolution = ValueOf<typeof CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION>;

/** Types of payments methods */
type PaymentMethodType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE | typeof CONST.WALLET.TRANSFER_METHOD_TYPE | typeof CONST.PAYMENT_METHODS>;
type PaymentMethodType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE | typeof CONST.WALLET.TRANSFER_METHOD_TYPE>;

/** Types of sources of original message */
type OriginalMessageSource = 'Chronos' | 'email' | 'ios' | 'android' | 'web' | '';
Expand Down

0 comments on commit 4745fc0

Please sign in to comment.