Skip to content

Commit 4745fc0

Browse files
mountinyOSBotify
authored andcommitted
Merge pull request #48639 from Expensify/revert-48421-revert-48117-revert-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)
1 parent 6fa35f5 commit 4745fc0

File tree

10 files changed

+137
-55
lines changed

10 files changed

+137
-55
lines changed

src/components/ButtonWithDropdownMenu/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ function ButtonWithDropdownMenu<IValueType>({
6666
if ('measureInWindow' in dropdownAnchor.current) {
6767
dropdownAnchor.current.measureInWindow((x, y, w, h) => {
6868
setPopoverAnchorPosition({
69-
horizontal: x + w + h,
69+
horizontal: x + w,
7070
vertical:
7171
anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP
7272
? 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

src/components/ButtonWithDropdownMenu/types.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import type {RefObject} from 'react';
22
import type {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native';
33
import type {ValueOf} from 'type-fest';
4-
import type {PaymentMethodType} from '@components/KYCWall/types';
54
import type CONST from '@src/CONST';
65
import type AnchorAlignment from '@src/types/utils/AnchorAlignment';
76
import type DeepValueOf from '@src/types/utils/DeepValueOf';
87
import type IconAsset from '@src/types/utils/IconAsset';
98

10-
type PaymentType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE | PaymentMethodType>;
9+
type PaymentType = DeepValueOf<typeof CONST.IOU.PAYMENT_TYPE | typeof CONST.IOU.REPORT_ACTION_TYPE>;
1110

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

src/components/KYCWall/BaseKYCWall.tsx

+112-17
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
import React, {useCallback, useRef} from 'react';
2-
import type {GestureResponderEvent, View} from 'react-native';
1+
import React, {useCallback, useEffect, useRef, useState} from 'react';
2+
import {Dimensions} from 'react-native';
3+
import type {EmitterSubscription, GestureResponderEvent, View} from 'react-native';
34
import {withOnyx} from 'react-native-onyx';
45
import type {OnyxEntry} from 'react-native-onyx';
6+
import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu';
57
import * as BankAccounts from '@libs/actions/BankAccounts';
8+
import getClickedTargetLocation from '@libs/getClickedTargetLocation';
69
import Log from '@libs/Log';
710
import Navigation from '@libs/Navigation/Navigation';
811
import * as PaymentUtils from '@libs/PaymentUtils';
912
import * as ReportUtils from '@libs/ReportUtils';
13+
import * as PaymentMethods from '@userActions/PaymentMethods';
1014
import * as Policy from '@userActions/Policy/Policy';
1115
import * as Wallet from '@userActions/Wallet';
1216
import CONST from '@src/CONST';
@@ -15,7 +19,10 @@ import ROUTES from '@src/ROUTES';
1519
import type {BankAccountList, FundList, ReimbursementAccount, UserWallet, WalletTerms} from '@src/types/onyx';
1620
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
1721
import viewRef from '@src/types/utils/viewRef';
18-
import type {KYCWallProps, PaymentMethod} from './types';
22+
import type {AnchorPosition, DomRect, KYCWallProps, PaymentMethod} from './types';
23+
24+
// This sets the Horizontal anchor position offset for POPOVER MENU.
25+
const POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET = 20;
1926

2027
type BaseKYCWallOnyxProps = {
2128
/** The user's wallet */
@@ -42,6 +49,10 @@ type BaseKYCWallProps = KYCWallProps & BaseKYCWallOnyxProps;
4249
function KYCWall({
4350
addBankAccountRoute,
4451
addDebitCardRoute,
52+
anchorAlignment = {
53+
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
54+
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
55+
},
4556
bankAccountList = {},
4657
chatReportID = '',
4758
children,
@@ -52,13 +63,60 @@ function KYCWall({
5263
onSuccessfulKYC,
5364
reimbursementAccount,
5465
shouldIncludeDebitCard = true,
66+
shouldListenForResize = false,
5567
source,
5668
userWallet,
5769
walletTerms,
70+
shouldShowPersonalBankAccountOption = false,
5871
}: BaseKYCWallProps) {
5972
const anchorRef = useRef<HTMLDivElement | View>(null);
6073
const transferBalanceButtonRef = useRef<HTMLDivElement | View | null>(null);
6174

75+
const [shouldShowAddPaymentMenu, setShouldShowAddPaymentMenu] = useState(false);
76+
77+
const [anchorPosition, setAnchorPosition] = useState({
78+
anchorPositionVertical: 0,
79+
anchorPositionHorizontal: 0,
80+
});
81+
82+
const getAnchorPosition = useCallback(
83+
(domRect: DomRect): AnchorPosition => {
84+
if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP) {
85+
return {
86+
anchorPositionVertical: domRect.top + domRect.height + CONST.MODAL.POPOVER_MENU_PADDING,
87+
anchorPositionHorizontal: domRect.left + POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET,
88+
};
89+
}
90+
91+
return {
92+
anchorPositionVertical: domRect.top - CONST.MODAL.POPOVER_MENU_PADDING,
93+
anchorPositionHorizontal: domRect.left,
94+
};
95+
},
96+
[anchorAlignment.vertical],
97+
);
98+
99+
/**
100+
* Set position of the transfer payment menu
101+
*/
102+
const setPositionAddPaymentMenu = ({anchorPositionVertical, anchorPositionHorizontal}: AnchorPosition) => {
103+
setAnchorPosition({
104+
anchorPositionVertical,
105+
anchorPositionHorizontal,
106+
});
107+
};
108+
109+
const setMenuPosition = useCallback(() => {
110+
if (!transferBalanceButtonRef.current) {
111+
return;
112+
}
113+
114+
const buttonPosition = getClickedTargetLocation(transferBalanceButtonRef.current as HTMLDivElement);
115+
const position = getAnchorPosition(buttonPosition);
116+
117+
setPositionAddPaymentMenu(position);
118+
}, [getAnchorPosition]);
119+
62120
const selectPaymentMethod = useCallback(
63121
(paymentMethod: PaymentMethod) => {
64122
onSelectPaymentMethod(paymentMethod);
@@ -101,6 +159,11 @@ function KYCWall({
101159
*/
102160
Wallet.setKYCWallSource(source, chatReportID);
103161

162+
if (shouldShowAddPaymentMenu) {
163+
setShouldShowAddPaymentMenu(false);
164+
return;
165+
}
166+
104167
// Use event target as fallback if anchorRef is null for safety
105168
const targetElement = anchorRef.current ?? (event?.currentTarget as HTMLDivElement);
106169

@@ -121,19 +184,11 @@ function KYCWall({
121184
return;
122185
}
123186

124-
switch (iouPaymentType) {
125-
case CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT:
126-
selectPaymentMethod(CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT);
127-
break;
128-
case CONST.PAYMENT_METHODS.DEBIT_CARD:
129-
selectPaymentMethod(CONST.PAYMENT_METHODS.DEBIT_CARD);
130-
break;
131-
case CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT:
132-
selectPaymentMethod(CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT);
133-
break;
134-
default:
135-
break;
136-
}
187+
const clickedElementLocation = getClickedTargetLocation(targetElement as HTMLDivElement);
188+
const position = getAnchorPosition(clickedElementLocation);
189+
190+
setPositionAddPaymentMenu(position);
191+
setShouldShowAddPaymentMenu(true);
137192

138193
return;
139194
}
@@ -159,18 +214,58 @@ function KYCWall({
159214
chatReportID,
160215
enablePaymentsRoute,
161216
fundList,
217+
getAnchorPosition,
162218
iouReport,
163219
onSuccessfulKYC,
164220
reimbursementAccount?.achData?.state,
165221
selectPaymentMethod,
166222
shouldIncludeDebitCard,
223+
shouldShowAddPaymentMenu,
167224
source,
168225
userWallet?.tierName,
169226
walletTerms?.source,
170227
],
171228
);
172229

173-
return <>{children(continueAction, viewRef(anchorRef))}</>;
230+
useEffect(() => {
231+
let dimensionsSubscription: EmitterSubscription | null = null;
232+
233+
PaymentMethods.kycWallRef.current = {continueAction};
234+
235+
if (shouldListenForResize) {
236+
dimensionsSubscription = Dimensions.addEventListener('change', setMenuPosition);
237+
}
238+
239+
return () => {
240+
if (shouldListenForResize && dimensionsSubscription) {
241+
dimensionsSubscription.remove();
242+
}
243+
244+
PaymentMethods.kycWallRef.current = null;
245+
};
246+
}, [chatReportID, setMenuPosition, shouldListenForResize, continueAction]);
247+
248+
return (
249+
<>
250+
<AddPaymentMethodMenu
251+
isVisible={shouldShowAddPaymentMenu}
252+
iouReport={iouReport}
253+
onClose={() => setShouldShowAddPaymentMenu(false)}
254+
anchorRef={anchorRef}
255+
anchorPosition={{
256+
vertical: anchorPosition.anchorPositionVertical,
257+
horizontal: anchorPosition.anchorPositionHorizontal,
258+
}}
259+
anchorAlignment={anchorAlignment}
260+
onItemSelected={(item: PaymentMethod) => {
261+
setShouldShowAddPaymentMenu(false);
262+
selectPaymentMethod(item);
263+
}}
264+
shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption}
265+
/>
266+
{children(continueAction, viewRef(anchorRef))}
267+
</>
268+
);
174269
}
175270

176271
KYCWall.displayName = 'BaseKYCWall';

src/components/MoneyRequestConfirmationList.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ function MoneyRequestConfirmationList({
837837
onPress={confirm}
838838
enablePaymentsRoute={ROUTES.IOU_SEND_ENABLE_PAYMENTS}
839839
addBankAccountRoute={bankAccountRoute}
840+
shouldShowPersonalBankAccountOption
840841
currency={iouCurrencyCode}
841842
policyID={policyID}
842843
buttonSize={CONST.DROPDOWN_BUTTON_SIZE.LARGE}

src/components/SettlementButton.tsx

+20-25
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ type SettlementButtonProps = SettlementButtonOnyxProps & {
105105
/** The anchor alignment of the popover menu for KYC wall popover */
106106
kycWallAnchorAlignment?: AnchorAlignment;
107107

108+
/** Whether the personal bank account option should be shown */
109+
shouldShowPersonalBankAccountOption?: boolean;
110+
108111
/** The priority to assign the enter key event listener to buttons. 0 is the highest priority. */
109112
enterKeyEventListenerPriority?: number;
110113

@@ -144,6 +147,7 @@ function SettlementButton({
144147
shouldShowApproveButton = false,
145148
shouldDisableApproveButton = false,
146149
style,
150+
shouldShowPersonalBankAccountOption = false,
147151
enterKeyEventListenerPriority = 0,
148152
confirmApproval,
149153
policy,
@@ -166,35 +170,25 @@ function SettlementButton({
166170
(!shouldHidePaymentOptions && ReportUtils.isPayer(session, iouReport) && policy?.reimbursementChoice !== CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL);
167171
const shouldShowPayElsewhereOption = (!isPaidGroupPolicy || policy?.reimbursementChoice === CONST.POLICY.REIMBURSEMENT_CHOICES.REIMBURSEMENT_MANUAL) && !isInvoiceReport;
168172
const paymentButtonOptions = useMemo(() => {
173+
const buttonOptions = [];
169174
const isExpenseReport = ReportUtils.isExpenseReport(iouReport);
170175
const paymentMethods = {
176+
[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]: {
177+
text: translate('iou.settleExpensify', {formattedAmount}),
178+
icon: Expensicons.Wallet,
179+
value: CONST.IOU.PAYMENT_TYPE.EXPENSIFY,
180+
},
171181
[CONST.IOU.PAYMENT_TYPE.VBBA]: {
172182
text: translate('iou.settleExpensify', {formattedAmount}),
173183
icon: Expensicons.Wallet,
174184
value: CONST.IOU.PAYMENT_TYPE.VBBA,
175185
},
176-
[CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT]: {
177-
text: translate('iou.settlePersonalBank', {formattedAmount}),
178-
icon: Expensicons.Bank,
179-
value: CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT,
180-
},
181-
[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]: {
182-
text: translate('iou.settleBusinessBank', {formattedAmount}),
183-
icon: Expensicons.Bank,
184-
value: CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT,
185-
},
186-
[CONST.PAYMENT_METHODS.DEBIT_CARD]: {
187-
text: translate('iou.settleDebitCard', {formattedAmount}),
188-
icon: Expensicons.CreditCard,
189-
value: CONST.PAYMENT_METHODS.DEBIT_CARD,
190-
},
191186
[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]: {
192187
text: translate('iou.payElsewhere', {formattedAmount}),
193188
icon: Expensicons.Cash,
194189
value: CONST.IOU.PAYMENT_TYPE.ELSEWHERE,
195190
},
196191
};
197-
const buttonOptions = [];
198192
const approveButtonOption = {
199193
text: translate('iou.approve'),
200194
icon: Expensicons.ThumbsUp,
@@ -212,10 +206,12 @@ function SettlementButton({
212206
// If the user has previously chosen a specific payment option or paid for some expense,
213207
// let's use the last payment method or use default.
214208
const paymentMethod = nvpLastPaymentMethod?.[policyID] ?? '-1';
215-
if (canUseWallet || (isExpenseReport && shouldShowPaywithExpensifyOption)) {
216-
buttonOptions.push(paymentMethods[CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT]);
209+
if (canUseWallet) {
210+
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.EXPENSIFY]);
211+
}
212+
if (isExpenseReport && shouldShowPaywithExpensifyOption) {
213+
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.VBBA]);
217214
}
218-
219215
if (shouldShowPayElsewhereOption) {
220216
buttonOptions.push(paymentMethods[CONST.IOU.PAYMENT_TYPE.ELSEWHERE]);
221217
}
@@ -275,12 +271,7 @@ function SettlementButton({
275271
return;
276272
}
277273

278-
if (
279-
iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA ||
280-
iouPaymentType === CONST.PAYMENT_METHODS.BUSINESS_BANK_ACCOUNT ||
281-
iouPaymentType === CONST.PAYMENT_METHODS.PERSONAL_BANK_ACCOUNT ||
282-
iouPaymentType === CONST.PAYMENT_METHODS.DEBIT_CARD
283-
) {
274+
if (iouPaymentType === CONST.IOU.PAYMENT_TYPE.EXPENSIFY || iouPaymentType === CONST.IOU.PAYMENT_TYPE.VBBA) {
284275
triggerKYCFlow(event, iouPaymentType);
285276
BankAccounts.setPersonalBankAccountContinueKYCOnSuccess(ROUTES.ENABLE_PAYMENTS);
286277
return;
@@ -314,14 +305,18 @@ function SettlementButton({
314305
chatReportID={chatReportID}
315306
iouReport={iouReport}
316307
anchorAlignment={kycWallAnchorAlignment}
308+
shouldShowPersonalBankAccountOption={shouldShowPersonalBankAccountOption}
317309
>
318310
{(triggerKYCFlow, buttonRef) => (
319311
<ButtonWithDropdownMenu<PaymentType>
320312
success
321313
onOptionsMenuShow={onPaymentOptionsShow}
322314
onOptionsMenuHide={onPaymentOptionsHide}
323315
buttonRef={buttonRef}
316+
shouldAlwaysShowDropdownMenu={isInvoiceReport}
317+
customText={isInvoiceReport ? translate('iou.settlePayment', {formattedAmount}) : undefined}
324318
menuHeaderText={isInvoiceReport ? translate('workspace.invoices.paymentMethods.chooseInvoiceMethod') : undefined}
319+
isSplitButton={!isInvoiceReport}
325320
isDisabled={isDisabled}
326321
isLoading={isLoading}
327322
onPress={(event, iouPaymentType) => selectPaymentType(event, iouPaymentType, triggerKYCFlow)}

src/languages/en.ts

-3
Original file line numberDiff line numberDiff line change
@@ -803,9 +803,6 @@ export default {
803803
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pay ${formattedAmount}`,
804804
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} as a business` : `Pay as a business`),
805805
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} elsewhere` : `Pay elsewhere`),
806-
settlePersonalBank: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with personal bank account` : `Pay with personal bank account`),
807-
settleBusinessBank: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with business bank account` : `Pay with business bank account`),
808-
settleDebitCard: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pay ${formattedAmount} with debit card` : `Pay with debit card`),
809806
nextStep: 'Next steps',
810807
finished: 'Finished',
811808
sendInvoice: ({amount}: RequestAmountParams) => `Send ${amount} invoice`,

src/languages/es.ts

-5
Original file line numberDiff line numberDiff line change
@@ -796,11 +796,6 @@ export default {
796796
settlePayment: ({formattedAmount}: SettleExpensifyCardParams) => `Pagar ${formattedAmount}`,
797797
settleBusiness: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} como negocio` : `Pagar como empresa`),
798798
payElsewhere: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} de otra forma` : `Pagar de otra forma`),
799-
settlePersonalBank: ({formattedAmount}: SettleExpensifyCardParams) =>
800-
formattedAmount ? `Pagar ${formattedAmount} con cuenta bancaria personal` : `Pagar con cuenta bancaria personal`,
801-
settleBusinessBank: ({formattedAmount}: SettleExpensifyCardParams) =>
802-
formattedAmount ? `Pagar ${formattedAmount} con cuenta bancaria comercial` : `Pagar con cuenta bancaria comercial`,
803-
settleDebitCard: ({formattedAmount}: SettleExpensifyCardParams) => (formattedAmount ? `Pagar ${formattedAmount} con tarjeta de débito` : `Pagar con tarjeta de débito`),
804799
nextStep: 'Pasos siguientes',
805800
finished: 'Finalizado',
806801
sendInvoice: ({amount}: RequestAmountParams) => `Enviar factura de ${amount}`,

src/languages/types.ts

-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ type RequestCountParams = {
103103

104104
type SettleExpensifyCardParams = {
105105
formattedAmount: string;
106-
available?: boolean;
107106
};
108107

109108
type RequestAmountParams = {amount: string};

src/pages/iou/MoneyRequestAmountForm.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ function MoneyRequestAmountForm(
323323
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
324324
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
325325
}}
326+
shouldShowPersonalBankAccountOption
326327
enterKeyEventListenerPriority={1}
327328
/>
328329
) : (

src/types/onyx/OriginalMessage.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type ReportActionName from './ReportActionName';
88
type JoinWorkspaceResolution = ValueOf<typeof CONST.REPORT.ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION>;
99

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

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

0 commit comments

Comments
 (0)