Skip to content
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

Revert "Revert "Consolidate options on settlement "Pay" button"" #48421

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/ButtonWithDropdownMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function ButtonWithDropdownMenu<IValueType>({
if ('measureInWindow' in dropdownAnchor.current) {
dropdownAnchor.current.measureInWindow((x, y, w, h) => {
setPopoverAnchorPosition({
horizontal: x + w,
horizontal: x + w + h,
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: 2 additions & 1 deletion src/components/ButtonWithDropdownMenu/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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>;
type PaymentType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE | PaymentMethodType>;

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

Expand Down
129 changes: 17 additions & 112 deletions src/components/KYCWall/BaseKYCWall.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {Dimensions} from 'react-native';
import type {EmitterSubscription, GestureResponderEvent, View} from 'react-native';
import React, {useCallback, useRef} from 'react';
import type {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 @@ -19,10 +15,7 @@ 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 {AnchorPosition, DomRect, KYCWallProps, PaymentMethod} from './types';

// This sets the Horizontal anchor position offset for POPOVER MENU.
const POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET = 20;
import type {KYCWallProps, PaymentMethod} from './types';

type BaseKYCWallOnyxProps = {
/** The user's wallet */
Expand All @@ -49,10 +42,6 @@ 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 @@ -63,60 +52,13 @@ 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 @@ -159,11 +101,6 @@ 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 @@ -184,11 +121,19 @@ function KYCWall({
return;
}

const clickedElementLocation = getClickedTargetLocation(targetElement as HTMLDivElement);
const position = getAnchorPosition(clickedElementLocation);

setPositionAddPaymentMenu(position);
setShouldShowAddPaymentMenu(true);
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;
}

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

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))}
</>
);
return <>{children(continueAction, viewRef(anchorRef))}</>;
}

KYCWall.displayName = 'BaseKYCWall';
Expand Down
1 change: 0 additions & 1 deletion src/components/MoneyRequestConfirmationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,6 @@ 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: 25 additions & 20 deletions src/components/SettlementButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,6 @@ 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 @@ -147,7 +144,6 @@ function SettlementButton({
shouldShowApproveButton = false,
shouldDisableApproveButton = false,
style,
shouldShowPersonalBankAccountOption = false,
enterKeyEventListenerPriority = 0,
confirmApproval,
policy,
Expand All @@ -170,25 +166,35 @@ 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 @@ -206,12 +212,10 @@ 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) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]);
}
if (isExpenseReport && shouldShowPaywithExpensifyOption) {
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]);
if (canUseWallet || (isExpenseReport && shouldShowPaywithExpensifyOption)) {
buttonOptions.push(paymentMethods[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]);
}

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

if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) {
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
) {
triggerKYCFlow(event, iouPaymentType);
BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS);
return;
Expand Down Expand Up @@ -305,18 +314,14 @@ 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: 3 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,9 @@ 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: 5 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,11 @@ 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: 1 addition & 0 deletions src/languages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ type RequestCountParams = {

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

type RequestAmountParams = {amount: string};
Expand Down
1 change: 0 additions & 1 deletion src/pages/iou/MoneyRequestAmountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,6 @@ 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>;
type PaymentMethodType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE | typeof CONST.WALLET.TRANSFER_METHOD_TYPE | typeof CONST.PAYMENT_METHODS>;

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