From 2df7e2cb3e81587c453ee44afd8226eccf52adf2 Mon Sep 17 00:00:00 2001 From: Marcin Swornowski Date: Wed, 4 Oct 2023 11:31:04 +0200 Subject: [PATCH 1/5] feat: migrate KYCWall class component to functional --- src/components/KYCWall/BaseKYCWall.js | 220 ++++++++++++++------------ src/libs/actions/PaymentMethods.js | 4 +- 2 files changed, 125 insertions(+), 99 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index ebf115c5de59..09e188229978 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect, useState, useRef, useCallback} from 'react'; import {withOnyx} from 'react-native-onyx'; import {Dimensions} from 'react-native'; import lodashGet from 'lodash/get'; @@ -17,75 +17,92 @@ import * as ReportUtils from '../../libs/ReportUtils'; // This component allows us to block various actions by forcing the user to first add a default payment method and successfully make it through our Know Your Customer flow // before continuing to take whatever action they originally intended to take. It requires a button as a child and a native event so we can get the coordinates and use it // to render the AddPaymentMethodMenu in the correct location. -class KYCWall extends React.Component { - constructor(props) { - super(props); - - this.continue = this.continue.bind(this); - this.setMenuPosition = this.setMenuPosition.bind(this); - this.anchorRef = React.createRef(null); - - this.state = { - shouldShowAddPaymentMenu: false, - anchorPositionVertical: 0, - anchorPositionHorizontal: 0, - transferBalanceButton: null, - }; - } - - componentDidMount() { - PaymentMethods.kycWallRef.current = this; - if (this.props.shouldListenForResize) { - this.dimensionsSubscription = Dimensions.addEventListener('change', this.setMenuPosition); - } - Wallet.setKYCWallSourceChatReportID(this.props.chatReportID); - } +function KYCWall({ + shouldListenForResize, + chatReportID, + popoverPlacement, + iouReport, + fundList, + reimbursementAccount, + bankAccountList, + userWallet, + enablePaymentsRoute, + onSuccessfulKYC, + addBankAccountRoute, + addDebitCardRoute, + children, +}) { + const anchorRef = useRef(null); + const transferBalanceButtonRef = useRef(null); - componentWillUnmount() { - if (this.props.shouldListenForResize && this.dimensionsSubscription) { - this.dimensionsSubscription.remove(); - } - PaymentMethods.kycWallRef.current = null; - } - - setMenuPosition() { - if (!this.state.transferBalanceButton) { - return; - } - const buttonPosition = getClickedTargetLocation(this.state.transferBalanceButton); - const position = this.getAnchorPosition(buttonPosition); - this.setPositionAddPaymentMenu(position); - } + const [shouldShowAddPaymentMenu, setShouldShowAddPaymentMenu] = useState(false); + const [anchorPosition, setAnchorPosition] = useState({ + anchorPositionVertical: 0, + anchorPositionHorizontal: 0, + }); /** * @param {DOMRect} domRect * @returns {Object} */ - getAnchorPosition(domRect) { - if (this.props.popoverPlacement === 'bottom') { + const getAnchorPosition = useCallback( + (domRect) => { + if (popoverPlacement === 'bottom') { + return { + anchorPositionVertical: domRect.top + (domRect.height - 2), + anchorPositionHorizontal: domRect.left + 20, + }; + } + return { - anchorPositionVertical: domRect.top + (domRect.height - 2), - anchorPositionHorizontal: domRect.left + 20, + anchorPositionVertical: domRect.top - CONST.MODAL.POPOVER_MENU_PADDING, + anchorPositionHorizontal: domRect.left, }; - } - - return { - anchorPositionVertical: domRect.top - CONST.MODAL.POPOVER_MENU_PADDING, - anchorPositionHorizontal: domRect.left, - }; - } + }, + [popoverPlacement], + ); /** * Set position of the transfer payment menu * * @param {Object} position */ - setPositionAddPaymentMenu(position) { - this.setState({ - anchorPositionVertical: position.anchorPositionVertical, - anchorPositionHorizontal: position.anchorPositionHorizontal, + const setPositionAddPaymentMenu = ({anchorPositionVertical, anchorPositionHorizontal}) => { + setAnchorPosition({ + anchorPositionVertical, + anchorPositionHorizontal, }); - } + }; + + const setMenuPosition = useCallback(() => { + if (!transferBalanceButtonRef.current) { + return; + } + const buttonPosition = getClickedTargetLocation(transferBalanceButtonRef.current); + const position = getAnchorPosition(buttonPosition); + + setPositionAddPaymentMenu(position); + }, [getAnchorPosition]); + + useEffect(() => { + let dimensionsSubscription = null; + + PaymentMethods.kycWallRef.current = this; + + if (shouldListenForResize) { + dimensionsSubscription = Dimensions.addEventListener('change', setMenuPosition); + } + + Wallet.setKYCWallSourceChatReportID(chatReportID); + + return () => { + if (shouldListenForResize && dimensionsSubscription) { + dimensionsSubscription.remove(); + } + + PaymentMethods.kycWallRef.current = null; + }; + }, [chatReportID, setMenuPosition, shouldListenForResize]); /** * Take the position of the button that calls this method and show the Add Payment method menu when the user has no valid payment method. @@ -95,66 +112,75 @@ class KYCWall extends React.Component { * @param {Event} event * @param {String} iouPaymentType */ - continue(event, iouPaymentType) { - if (this.state.shouldShowAddPaymentMenu) { - this.setState({shouldShowAddPaymentMenu: false}); + const continueAction = (event, iouPaymentType) => { + if (shouldShowAddPaymentMenu) { + setShouldShowAddPaymentMenu(false); + return; } - this.setState({transferBalanceButton: event.nativeEvent.target}); - const isExpenseReport = ReportUtils.isExpenseReport(this.props.iouReport); - const paymentCardList = this.props.fundList || {}; + + transferBalanceButtonRef(event.nativeEvent.target); + + const isExpenseReport = ReportUtils.isExpenseReport(iouReport); + const paymentCardList = fundList || {}; // Check to see if user has a valid payment method on file and display the add payment popover if they don't if ( - (isExpenseReport && lodashGet(this.props.reimbursementAccount, 'achData.state', '') !== CONST.BANK_ACCOUNT.STATE.OPEN) || - (!isExpenseReport && !PaymentUtils.hasExpensifyPaymentMethod(paymentCardList, this.props.bankAccountList)) + (isExpenseReport && lodashGet(reimbursementAccount, 'achData.state', '') !== CONST.BANK_ACCOUNT.STATE.OPEN) || + (!isExpenseReport && !PaymentUtils.hasExpensifyPaymentMethod(paymentCardList, bankAccountList)) ) { Log.info('[KYC Wallet] User does not have valid payment method'); + const clickedElementLocation = getClickedTargetLocation(event.nativeEvent.target); - const position = this.getAnchorPosition(clickedElementLocation); - this.setPositionAddPaymentMenu(position); - this.setState({ - shouldShowAddPaymentMenu: true, - }); + const position = getAnchorPosition(clickedElementLocation); + + setPositionAddPaymentMenu(position); + setShouldShowAddPaymentMenu(true); + return; } + if (!isExpenseReport) { // 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 hasGoldWallet = this.props.userWallet.tierName && this.props.userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD; + const hasGoldWallet = userWallet.tierName && userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD; + if (!hasGoldWallet) { Log.info('[KYC Wallet] User does not have gold wallet'); - Navigation.navigate(this.props.enablePaymentsRoute); + Navigation.navigate(enablePaymentsRoute); + return; } } + Log.info('[KYC Wallet] User has valid payment method and passed KYC checks or did not need them'); - this.props.onSuccessfulKYC(iouPaymentType); - } - - render() { - return ( - <> - this.setState({shouldShowAddPaymentMenu: false})} - anchorRef={this.anchorRef} - anchorPosition={{ - vertical: this.state.anchorPositionVertical, - horizontal: this.state.anchorPositionHorizontal, - }} - onItemSelected={(item) => { - this.setState({shouldShowAddPaymentMenu: false}); - if (item === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - Navigation.navigate(this.props.addBankAccountRoute); - } else if (item === CONST.PAYMENT_METHODS.DEBIT_CARD) { - Navigation.navigate(this.props.addDebitCardRoute); - } - }} - /> - {this.props.children(this.continue, this.anchorRef)} - - ); - } + onSuccessfulKYC(iouPaymentType); + }; + + const handleItemSelected = (item) => { + setShouldShowAddPaymentMenu(false); + + if (item === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + Navigation.navigate(addBankAccountRoute); + } else if (item === CONST.PAYMENT_METHODS.DEBIT_CARD) { + Navigation.navigate(addDebitCardRoute); + } + }; + + return ( + <> + setShouldShowAddPaymentMenu(false)} + anchorRef={anchorRef} + anchorPosition={{ + vertical: anchorPosition.anchorPositionVertical, + horizontal: anchorPosition.anchorPositionHorizontal, + }} + onItemSelected={handleItemSelected} + /> + {children(continueAction, anchorRef)} + + ); } KYCWall.propTypes = propTypes; diff --git a/src/libs/actions/PaymentMethods.js b/src/libs/actions/PaymentMethods.js index 0ed6f8b036bb..a6f196f522ed 100644 --- a/src/libs/actions/PaymentMethods.js +++ b/src/libs/actions/PaymentMethods.js @@ -17,14 +17,14 @@ const kycWallRef = createRef(); * When we successfully add a payment method or pass the KYC checks we will continue with our setup action if we have one set. */ function continueSetup() { - if (!kycWallRef.current || !kycWallRef.current.continue) { + if (!kycWallRef.current || !kycWallRef.current.continueAction) { Navigation.goBack(ROUTES.HOME); return; } // Close the screen (Add Debit Card, Add Bank Account, or Enable Payments) on success and continue with setup Navigation.goBack(ROUTES.HOME); - kycWallRef.current.continue(); + kycWallRef.current.continueAction(); } function openWalletPage() { From 1390178a664451f1121a98dc8afc024fb3bb3c31 Mon Sep 17 00:00:00 2001 From: Marcin Swornowski Date: Wed, 4 Oct 2023 14:03:02 +0200 Subject: [PATCH 2/5] change: moved anchor position offsets to CONST file --- src/CONST.ts | 2 ++ src/components/KYCWall/BaseKYCWall.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 0a262d868de9..81a20ff39d52 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -648,6 +648,8 @@ const CONST = { RIGHT: 'right', }, POPOVER_MENU_PADDING: 8, + POPOVER_MENU_ANCHOR_POSITION_VERTICAL_OFFSET: 2, + POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET: 20, }, TIMING: { CALCULATE_MOST_RECENT_LAST_MODIFIED_ACTION: 'calc_most_recent_last_modified_action', diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 09e188229978..eccf6bb358e0 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -49,8 +49,8 @@ function KYCWall({ (domRect) => { if (popoverPlacement === 'bottom') { return { - anchorPositionVertical: domRect.top + (domRect.height - 2), - anchorPositionHorizontal: domRect.left + 20, + anchorPositionVertical: domRect.top + (domRect.height - CONST.MODAL.POPOVER_MENU_ANCHOR_POSITION_VERTICAL_OFFSET), + anchorPositionHorizontal: domRect.left + CONST.MODAL.POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET, }; } From b9814749960ae17790b91dfaf5a5d747e3cdb570 Mon Sep 17 00:00:00 2001 From: Marcin Swornowski Date: Wed, 4 Oct 2023 15:07:10 +0200 Subject: [PATCH 3/5] change: moved anchor position offsets back to KYCWall file before component declaration --- src/CONST.ts | 2 -- src/components/KYCWall/BaseKYCWall.js | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/CONST.ts b/src/CONST.ts index 81a20ff39d52..0a262d868de9 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -648,8 +648,6 @@ const CONST = { RIGHT: 'right', }, POPOVER_MENU_PADDING: 8, - POPOVER_MENU_ANCHOR_POSITION_VERTICAL_OFFSET: 2, - POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET: 20, }, TIMING: { CALCULATE_MOST_RECENT_LAST_MODIFIED_ACTION: 'calc_most_recent_last_modified_action', diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index eccf6bb358e0..deea43ba2820 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -14,6 +14,9 @@ import {propTypes, defaultProps} from './kycWallPropTypes'; import * as Wallet from '../../libs/actions/Wallet'; import * as ReportUtils from '../../libs/ReportUtils'; +const POPOVER_MENU_ANCHOR_POSITION_VERTICAL_OFFSET = 2; +const POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET = 20; + // This component allows us to block various actions by forcing the user to first add a default payment method and successfully make it through our Know Your Customer flow // before continuing to take whatever action they originally intended to take. It requires a button as a child and a native event so we can get the coordinates and use it // to render the AddPaymentMethodMenu in the correct location. @@ -49,8 +52,8 @@ function KYCWall({ (domRect) => { if (popoverPlacement === 'bottom') { return { - anchorPositionVertical: domRect.top + (domRect.height - CONST.MODAL.POPOVER_MENU_ANCHOR_POSITION_VERTICAL_OFFSET), - anchorPositionHorizontal: domRect.left + CONST.MODAL.POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET, + anchorPositionVertical: domRect.top + (domRect.height - POPOVER_MENU_ANCHOR_POSITION_VERTICAL_OFFSET), + anchorPositionHorizontal: domRect.left + POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET, }; } From 851b3ac9484b04348d713b68114c4d731411fc72 Mon Sep 17 00:00:00 2001 From: Marcin Swornowski Date: Wed, 4 Oct 2023 16:13:10 +0200 Subject: [PATCH 4/5] fix: assignment to transferBalanceButtonRef --- src/components/KYCWall/BaseKYCWall.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index deea43ba2820..db87be371d08 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -122,7 +122,7 @@ function KYCWall({ return; } - transferBalanceButtonRef(event.nativeEvent.target); + transferBalanceButtonRef.current = event.nativeEvent.target; const isExpenseReport = ReportUtils.isExpenseReport(iouReport); const paymentCardList = fundList || {}; From 52f43e4ca249f9d11fa628637d67da24aa1b1189 Mon Sep 17 00:00:00 2001 From: Marcin Swornowski Date: Thu, 12 Oct 2023 12:10:41 +0200 Subject: [PATCH 5/5] add: displayName for BaseKYCWall --- src/components/KYCWall/BaseKYCWall.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index f757e64bb1f5..a5790c58766e 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -190,6 +190,7 @@ function KYCWall({ KYCWall.propTypes = propTypes; KYCWall.defaultProps = defaultProps; +KYCWall.displayName = 'BaseKYCWall'; export default withOnyx({ userWallet: {