From 056ae8482a63ac88b6a634f8c074293fb9514cbb Mon Sep 17 00:00:00 2001 From: phivh Date: Fri, 4 Feb 2022 22:39:18 +0700 Subject: [PATCH 1/4] Fix add payment position dynamic --- src/components/KYCWall/index.js | 177 ++++++++ .../{KYCWall.js => KYCWall/index.native.js} | 20 +- .../settings/Payments/PaymentsPage/index.js | 428 ++++++++++++++++++ .../index.native.js} | 54 +-- 4 files changed, 642 insertions(+), 37 deletions(-) create mode 100644 src/components/KYCWall/index.js rename src/components/{KYCWall.js => KYCWall/index.native.js} (90%) create mode 100644 src/pages/settings/Payments/PaymentsPage/index.js rename src/pages/settings/Payments/{PaymentsPage.js => PaymentsPage/index.native.js} (91%) diff --git a/src/components/KYCWall/index.js b/src/components/KYCWall/index.js new file mode 100644 index 000000000000..9fecb6cfbc79 --- /dev/null +++ b/src/components/KYCWall/index.js @@ -0,0 +1,177 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {withOnyx} from 'react-native-onyx'; +import {ActivityIndicator} from 'react-native'; +import themeColors from '../../styles/themes/default'; +import CONST from '../../CONST'; +import Navigation from '../../libs/Navigation/Navigation'; +import AddPaymentMethodMenu from '../AddPaymentMethodMenu'; +import getClickedElementLocation from '../../libs/getClickedElementLocation'; +import * as PaymentUtils from '../../libs/PaymentUtils'; +import * as PaymentMethods from '../../libs/actions/PaymentMethods'; +import ONYXKEYS from '../../ONYXKEYS'; +import userWalletPropTypes from '../../pages/EnablePayments/userWalletPropTypes'; +import Log from '../../libs/Log'; + +const propTypes = { + /** Route for the Add Bank Account screen for a given navigation stack */ + addBankAccountRoute: PropTypes.string.isRequired, + + /** Route for the Add Debit Card screen for a given navigation stack */ + addDebitCardRoute: PropTypes.string.isRequired, + + /** Route for the KYC enable payments screen for a given navigation stack */ + enablePaymentsRoute: PropTypes.string.isRequired, + + /** Where to place the popover */ + popoverPlacement: PropTypes.string, + + ...userWalletPropTypes, +}; + +const defaultProps = { + // eslint-disable-next-line react/default-props-match-prop-types + userWallet: {}, + popoverPlacement: 'top', +}; + +// 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.state = { + shouldShowAddPaymentMenu: false, + anchorPositionTop: 0, + anchorPositionLeft: 0, + }; + } + + componentDidMount() { + window.removeEventListener('resize', null); + PaymentMethods.getPaymentMethods(); + PaymentMethods.kycWallRef.current = this; + } + + componentWillUnmount() { + PaymentMethods.kycWallRef.current = null; + } + + /** + * @param {DOMRect} domRect + * @returns {Object} + */ + getAnchorPosition(domRect) { + if (this.props.popoverPlacement === 'bottom') { + return { + anchorPositionTop: domRect.top + (domRect.height - 2), + anchorPositionLeft: domRect.left + 20, + }; + } + + return { + anchorPositionTop: domRect.top - 150, + anchorPositionLeft: domRect.left, + }; + } + + /** + * Set position of the transfer payment menu + * + * @param {Object} position + */ + + setPositionAddPaymentMenu(position) { + this.setState({ + anchorPositionTop: position.anchorPositionTop, + anchorPositionLeft: position.anchorPositionLeft, + }); + } + + /** + * 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. + * If they do have a valid payment method they are navigated to the "enable payments" route to complete KYC checks. + * If they are already KYC'd we will continue whatever action is gated behind the KYC wall. + * + * @param {Event} event + */ + continue(event) { + // Check to see if user has a valid payment method on file and display the add payment popover if they don't + if (!PaymentUtils.hasExpensifyPaymentMethod(this.props.cardList, this.props.bankAccountList)) { + Log.info('[KYC Wallet] User does not have valid payment method'); + let clickedElementLocation = getClickedElementLocation(event.nativeEvent); + let position = this.getAnchorPosition(clickedElementLocation); + window.addEventListener('resize', () => { + clickedElementLocation = getClickedElementLocation(event.nativeEvent); + position = this.getAnchorPosition(clickedElementLocation); + this.setPositionAddPaymentMenu(position); + }); + this.setState({ + shouldShowAddPaymentMenu: true, + }); + this.setPositionAddPaymentMenu(position); + return; + } + + // Ask the user to upgrade to a gold wallet as this means they have not yet went through our Know Your Customer (KYC) checks + const hasGoldWallet = this.props.userWallet.tierName && this.props.userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD; + if (!hasGoldWallet) { + Log.info('[KYC Wallet] User does not have gold wallet'); + Navigation.navigate(this.props.enablePaymentsRoute); + return; + } + + Log.info('[KYC Wallet] User has valid payment method and passed KYC checks'); + this.props.onSuccessfulKYC(); + } + + render() { + return ( + <> + this.setState({shouldShowAddPaymentMenu: false})} + anchorPosition={{ + top: this.state.anchorPositionTop, + left: this.state.anchorPositionLeft, + }} + shouldShowPaypal={false} + 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.isLoadingPaymentMethods + ? () + : this.props.children(this.continue)} + + ); + } +} + +KYCWall.propTypes = propTypes; +KYCWall.defaultProps = defaultProps; + +export default withOnyx({ + userWallet: { + key: ONYXKEYS.USER_WALLET, + }, + cardList: { + key: ONYXKEYS.CARD_LIST, + }, + bankAccountList: { + key: ONYXKEYS.BANK_ACCOUNT_LIST, + }, + isLoadingPaymentMethods: { + key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, + initWithStoredValues: false, + }, +})(KYCWall); diff --git a/src/components/KYCWall.js b/src/components/KYCWall/index.native.js similarity index 90% rename from src/components/KYCWall.js rename to src/components/KYCWall/index.native.js index f0dc66ec99ee..a03761a37278 100644 --- a/src/components/KYCWall.js +++ b/src/components/KYCWall/index.native.js @@ -2,16 +2,16 @@ import React from 'react'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; import {ActivityIndicator} from 'react-native'; -import themeColors from '../styles/themes/default'; -import CONST from '../CONST'; -import Navigation from '../libs/Navigation/Navigation'; -import AddPaymentMethodMenu from './AddPaymentMethodMenu'; -import getClickedElementLocation from '../libs/getClickedElementLocation'; -import * as PaymentUtils from '../libs/PaymentUtils'; -import * as PaymentMethods from '../libs/actions/PaymentMethods'; -import ONYXKEYS from '../ONYXKEYS'; -import userWalletPropTypes from '../pages/EnablePayments/userWalletPropTypes'; -import Log from '../libs/Log'; +import themeColors from '../../styles/themes/default'; +import CONST from '../../CONST'; +import Navigation from '../../libs/Navigation/Navigation'; +import AddPaymentMethodMenu from '../AddPaymentMethodMenu'; +import getClickedElementLocation from '../../libs/getClickedElementLocation'; +import * as PaymentUtils from '../../libs/PaymentUtils'; +import * as PaymentMethods from '../../libs/actions/PaymentMethods'; +import ONYXKEYS from '../../ONYXKEYS'; +import userWalletPropTypes from '../../pages/EnablePayments/userWalletPropTypes'; +import Log from '../../libs/Log'; const propTypes = { /** Route for the Add Bank Account screen for a given navigation stack */ diff --git a/src/pages/settings/Payments/PaymentsPage/index.js b/src/pages/settings/Payments/PaymentsPage/index.js new file mode 100644 index 000000000000..842fff5900a2 --- /dev/null +++ b/src/pages/settings/Payments/PaymentsPage/index.js @@ -0,0 +1,428 @@ +import React from 'react'; +import {View, TouchableOpacity} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import PropTypes from 'prop-types'; +import PaymentMethodList from '../PaymentMethodList'; +import ROUTES from '../../../../ROUTES'; +import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton'; +import PasswordPopover from '../../../../components/PasswordPopover'; +import ScreenWrapper from '../../../../components/ScreenWrapper'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import styles from '../../../../styles/styles'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; +import KeyboardAvoidingView from '../../../../components/KeyboardAvoidingView/index'; +import * as BankAccounts from '../../../../libs/actions/BankAccounts'; +import Popover from '../../../../components/Popover'; +import MenuItem from '../../../../components/MenuItem'; +import Text from '../../../../components/Text'; +import * as PaymentMethods from '../../../../libs/actions/PaymentMethods'; +import getClickedElementLocation from '../../../../libs/getClickedElementLocation'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import CurrentWalletBalance from '../../../../components/CurrentWalletBalance'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import Permissions from '../../../../libs/Permissions'; +import ConfirmPopover from '../../../../components/ConfirmPopover'; +import AddPaymentMethodMenu from '../../../../components/AddPaymentMethodMenu'; +import CONST from '../../../../CONST'; +import * as Expensicons from '../../../../components/Icon/Expensicons'; +import walletTransferPropTypes from '../walletTransferPropTypes'; +import ConfirmModal from '../../../../components/ConfirmModal'; +import KYCWall from '../../../../components/KYCWall'; + +const propTypes = { + /** Wallet balance transfer props */ + walletTransfer: walletTransferPropTypes, + + /** List of betas available to current user */ + betas: PropTypes.arrayOf(PropTypes.string), + + /** Are we loading payment methods? */ + isLoadingPaymentMethods: PropTypes.bool, + + ...withLocalizePropTypes, + + ...windowDimensionsPropTypes, +}; + +const defaultProps = { + walletTransfer: { + shouldShowConfirmModal: false, + }, + betas: [], + isLoadingPaymentMethods: true, +}; + +class PaymentsPage extends React.Component { + constructor(props) { + super(props); + + this.state = { + shouldShowAddPaymentMenu: false, + shouldShowDefaultDeleteMenu: false, + shouldShowPasswordPrompt: false, + shouldShowConfirmPopover: false, + selectedPaymentMethod: {}, + formattedSelectedPaymentMethod: {}, + anchorPositionTop: 0, + anchorPositionLeft: 0, + }; + + this.paymentMethodPressed = this.paymentMethodPressed.bind(this); + this.addPaymentMethodTypePressed = this.addPaymentMethodTypePressed.bind(this); + this.hideAddPaymentMenu = this.hideAddPaymentMenu.bind(this); + this.hideDefaultDeleteMenu = this.hideDefaultDeleteMenu.bind(this); + this.makeDefaultPaymentMethod = this.makeDefaultPaymentMethod.bind(this); + this.deletePaymentMethod = this.deletePaymentMethod.bind(this); + this.hidePasswordPrompt = this.hidePasswordPrompt.bind(this); + this.navigateToTransferBalancePage = this.navigateToTransferBalancePage.bind(this); + } + + componentDidMount() { + PaymentMethods.getPaymentMethods(); + } + + getSelectedPaymentMethodID() { + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { + return CONST.PAYMENT_METHODS.PAYPAL; + } + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + return this.state.selectedPaymentMethod.bankAccountID; + } + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + return this.state.selectedPaymentMethod.fundID; + } + } + + /** + * Set position of the payment menu + * + * @param {Object} position + */ + + setPositionAddPaymentMenu(position) { + this.setState({ + anchorPositionTop: position.bottom, + + // We want the position to be 13px to the right of the left border + anchorPositionLeft: position.left + 13, + }); + } + + /** + * Display the delete/default menu, or the add payment method menu + * + * @param {Object} nativeEvent + * @param {String} accountType + * @param {String} account + */ + paymentMethodPressed(nativeEvent, accountType, account) { + let position = getClickedElementLocation(nativeEvent); + window.addEventListener('resize', () => { + position = getClickedElementLocation(nativeEvent); + this.setPositionAddPaymentMenu(position); + }); + if (accountType) { + let formattedSelectedPaymentMethod; + if (accountType === CONST.PAYMENT_METHODS.PAYPAL) { + formattedSelectedPaymentMethod = { + title: 'PayPal.me', + icon: account.icon, + description: account.username, + type: CONST.PAYMENT_METHODS.PAYPAL, + }; + } else if (accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + formattedSelectedPaymentMethod = { + title: account.addressName, + icon: account.icon, + description: `${this.props.translate('paymentMethodList.accountLastFour')} ${account.accountNumber.slice(-4)}`, + type: CONST.PAYMENT_METHODS.BANK_ACCOUNT, + }; + } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + formattedSelectedPaymentMethod = { + title: account.addressName, + icon: account.icon, + description: `${this.props.translate('paymentMethodList.cardLastFour')} ${account.cardNumber.slice(-4)}`, + type: CONST.PAYMENT_METHODS.DEBIT_CARD, + }; + } + this.setState({ + shouldShowDefaultDeleteMenu: true, + selectedPaymentMethod: account, + selectedPaymentMethodType: accountType, + anchorPositionTop: position.bottom, + + // We want the position to be 13px to the right of the left border + anchorPositionLeft: position.left + 13, + formattedSelectedPaymentMethod, + }); + } else { + this.setState({ + shouldShowAddPaymentMenu: true, + }); + this.setPositionAddPaymentMenu(position); + } + } + + /** + * Navigate to the appropriate payment type addition screen + * + * @param {String} paymentType + */ + addPaymentMethodTypePressed(paymentType) { + this.hideAddPaymentMenu(); + + if (paymentType === CONST.PAYMENT_METHODS.PAYPAL) { + Navigation.navigate(ROUTES.SETTINGS_ADD_PAYPAL_ME); + return; + } + + if (paymentType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + Navigation.navigate(ROUTES.SETTINGS_ADD_DEBIT_CARD); + return; + } + + if (paymentType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT); + return; + } + + throw new Error('Invalid payment method type selected'); + } + + /** + * Hide the add payment modal + */ + hideAddPaymentMenu() { + window.removeEventListener('resize', null); + this.setState({shouldShowAddPaymentMenu: false}); + } + + /** + * Hide the default / delete modal + */ + hideDefaultDeleteMenu() { + this.setState({shouldShowDefaultDeleteMenu: false}); + } + + hidePasswordPrompt() { + this.setState({shouldShowPasswordPrompt: false}); + } + + makeDefaultPaymentMethod(password) { + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + PaymentMethods.setWalletLinkedAccount(password, this.state.selectedPaymentMethod.bankAccountID, null); + } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + PaymentMethods.setWalletLinkedAccount(password, null, this.state.selectedPaymentMethod.fundID); + } + } + + deletePaymentMethod() { + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { + PaymentMethods.deletePayPalMe(); + } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + BankAccounts.deleteBankAccount(this.state.selectedPaymentMethod.bankAccountID); + } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + PaymentMethods.deleteDebitCard(this.state.selectedPaymentMethod.fundID); + } + } + + navigateToTransferBalancePage() { + Navigation.navigate(ROUTES.SETTINGS_PAYMENTS_TRANSFER_BALANCE); + } + + render() { + const isPayPalMeSelected = this.state.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.PAYPAL; + return ( + + + Navigation.navigate(ROUTES.SETTINGS)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + {Permissions.canUseWallet(this.props.betas) && ( + <> + + + + + {triggerKYCFlow => ( + + )} + + + )} + + {this.props.translate('paymentsPage.paymentMethodsTitle')} + + + + this.addPaymentMethodTypePressed(method)} + /> + + + {this.props.isSmallScreenWidth && ( + + )} + {Permissions.canUseWallet(this.props.betas) && ( + { + this.setState({ + shouldShowPasswordPrompt: true, + shouldShowDefaultDeleteMenu: false, + passwordButtonText: this.props.translate('paymentsPage.setDefaultConfirmation'), + }); + }} + style={[styles.button, isPayPalMeSelected && styles.buttonDisable, styles.alignSelfCenter, styles.w100]} + disabled={isPayPalMeSelected} + > + + {this.props.translate('paymentsPage.setDefaultConfirmation')} + + + )} + { + this.setState({ + shouldShowDefaultDeleteMenu: false, + shouldShowConfirmPopover: true, + }); + }} + style={[ + styles.button, + styles.buttonDanger, + Permissions.canUseWallet(this.props.betas) && styles.mt4, + styles.alignSelfCenter, + styles.w100, + ]} + > + + {this.props.translate('common.delete')} + + + + + { + this.hidePasswordPrompt(); + this.makeDefaultPaymentMethod(password); + }} + submitButtonText={this.state.passwordButtonText} + isDangerousAction + /> + { + this.setState({ + shouldShowConfirmPopover: false, + }); + this.deletePaymentMethod(); + }} + onCancel={() => { + this.setState({shouldShowConfirmPopover: false}); + }} + shouldShowCancelButton + danger + /> + + + + ); + } +} + +PaymentsPage.propTypes = propTypes; +PaymentsPage.defaultProps = defaultProps; + +export default compose( + withWindowDimensions, + withLocalize, + withOnyx({ + betas: { + key: ONYXKEYS.BETAS, + }, + walletTransfer: { + key: ONYXKEYS.WALLET_TRANSFER, + }, + isLoadingPaymentMethods: { + key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, + initWithStoredValues: false, + }, + }), +)(PaymentsPage); diff --git a/src/pages/settings/Payments/PaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/index.native.js similarity index 91% rename from src/pages/settings/Payments/PaymentsPage.js rename to src/pages/settings/Payments/PaymentsPage/index.native.js index 138c30938801..80ce7b98ca70 100644 --- a/src/pages/settings/Payments/PaymentsPage.js +++ b/src/pages/settings/Payments/PaymentsPage/index.native.js @@ -2,33 +2,33 @@ import React from 'react'; import {View, TouchableOpacity} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import PropTypes from 'prop-types'; -import PaymentMethodList from './PaymentMethodList'; -import ROUTES from '../../../ROUTES'; -import HeaderWithCloseButton from '../../../components/HeaderWithCloseButton'; -import PasswordPopover from '../../../components/PasswordPopover'; -import ScreenWrapper from '../../../components/ScreenWrapper'; -import Navigation from '../../../libs/Navigation/Navigation'; -import styles from '../../../styles/styles'; -import withLocalize, {withLocalizePropTypes} from '../../../components/withLocalize'; -import compose from '../../../libs/compose'; -import KeyboardAvoidingView from '../../../components/KeyboardAvoidingView/index'; -import * as BankAccounts from '../../../libs/actions/BankAccounts'; -import Popover from '../../../components/Popover'; -import MenuItem from '../../../components/MenuItem'; -import Text from '../../../components/Text'; -import * as PaymentMethods from '../../../libs/actions/PaymentMethods'; -import getClickedElementLocation from '../../../libs/getClickedElementLocation'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions'; -import CurrentWalletBalance from '../../../components/CurrentWalletBalance'; -import ONYXKEYS from '../../../ONYXKEYS'; -import Permissions from '../../../libs/Permissions'; -import ConfirmPopover from '../../../components/ConfirmPopover'; -import AddPaymentMethodMenu from '../../../components/AddPaymentMethodMenu'; -import CONST from '../../../CONST'; -import * as Expensicons from '../../../components/Icon/Expensicons'; -import walletTransferPropTypes from './walletTransferPropTypes'; -import ConfirmModal from '../../../components/ConfirmModal'; -import KYCWall from '../../../components/KYCWall'; +import PaymentMethodList from '../PaymentMethodList'; +import ROUTES from '../../../../ROUTES'; +import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton'; +import PasswordPopover from '../../../../components/PasswordPopover'; +import ScreenWrapper from '../../../../components/ScreenWrapper'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import styles from '../../../../styles/styles'; +import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; +import KeyboardAvoidingView from '../../../../components/KeyboardAvoidingView/index'; +import * as BankAccounts from '../../../../libs/actions/BankAccounts'; +import Popover from '../../../../components/Popover'; +import MenuItem from '../../../../components/MenuItem'; +import Text from '../../../../components/Text'; +import * as PaymentMethods from '../../../../libs/actions/PaymentMethods'; +import getClickedElementLocation from '../../../../libs/getClickedElementLocation'; +import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; +import CurrentWalletBalance from '../../../../components/CurrentWalletBalance'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import Permissions from '../../../../libs/Permissions'; +import ConfirmPopover from '../../../../components/ConfirmPopover'; +import AddPaymentMethodMenu from '../../../../components/AddPaymentMethodMenu'; +import CONST from '../../../../CONST'; +import * as Expensicons from '../../../../components/Icon/Expensicons'; +import walletTransferPropTypes from '../walletTransferPropTypes'; +import ConfirmModal from '../../../../components/ConfirmModal'; +import KYCWall from '../../../../components/KYCWall'; const propTypes = { /** Wallet balance transfer props */ From 789c3ec13f0916650e1703d6638193514a098e9d Mon Sep 17 00:00:00 2001 From: phivh Date: Sun, 6 Feb 2022 15:15:43 +0700 Subject: [PATCH 2/4] cleanup --- src/components/Form.js | 1 + src/components/KYCWall/BaseKYCWall.js | 158 +++++++ src/components/KYCWall/index.js | 180 +------- src/components/KYCWall/index.native.js | 160 +------ src/components/KYCWall/kycWallPropTypes.js | 30 ++ .../Payments/PaymentsPage/BasePaymentsPage.js | 405 ++++++++++++++++ .../settings/Payments/PaymentsPage/index.js | 431 +----------------- .../Payments/PaymentsPage/index.native.js | 412 +---------------- .../PaymentsPage/paymentsPagePropTypes.js | 33 ++ 9 files changed, 645 insertions(+), 1165 deletions(-) create mode 100644 src/components/KYCWall/BaseKYCWall.js create mode 100644 src/components/KYCWall/kycWallPropTypes.js create mode 100644 src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js create mode 100644 src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js diff --git a/src/components/Form.js b/src/components/Form.js index c169113f8097..7871b3561f15 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -122,6 +122,7 @@ class Form extends React.Component { * @returns {React.Component} */ childrenWrapperWithProps(children) { + // eslint-disable-next-line rulesdir/prefer-underscore-method return React.Children.map(children, (child) => { // Just render the child if it is not a valid React element, e.g. text within a component if (!React.isValidElement(child)) { diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js new file mode 100644 index 000000000000..87ec1b8755cf --- /dev/null +++ b/src/components/KYCWall/BaseKYCWall.js @@ -0,0 +1,158 @@ +import React from 'react'; +import {withOnyx} from 'react-native-onyx'; +import {ActivityIndicator} from 'react-native'; +import themeColors from '../../styles/themes/default'; +import CONST from '../../CONST'; +import Navigation from '../../libs/Navigation/Navigation'; +import AddPaymentMethodMenu from '../AddPaymentMethodMenu'; +import getClickedElementLocation from '../../libs/getClickedElementLocation'; +import * as PaymentUtils from '../../libs/PaymentUtils'; +import * as PaymentMethods from '../../libs/actions/PaymentMethods'; +import ONYXKEYS from '../../ONYXKEYS'; +import Log from '../../libs/Log'; +import {propTypes, defaultProps} from './kycWallPropTypes'; + +// 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.state = { + shouldShowAddPaymentMenu: false, + anchorPositionTop: 0, + anchorPositionLeft: 0, + }; + } + + componentDidMount() { + if (this.props.isResizeListen) { + window.removeEventListener('resize', null); + } + PaymentMethods.getPaymentMethods(); + PaymentMethods.kycWallRef.current = this; + } + + componentWillUnmount() { + PaymentMethods.kycWallRef.current = null; + } + + /** + * @param {DOMRect} domRect + * @returns {Object} + */ + getAnchorPosition(domRect) { + if (this.props.popoverPlacement === 'bottom') { + return { + anchorPositionTop: domRect.top + (domRect.height - 2), + anchorPositionLeft: domRect.left + 20, + }; + } + + return { + anchorPositionTop: domRect.top - 150, + anchorPositionLeft: domRect.left, + }; + } + + /** + * Set position of the transfer payment menu + * + * @param {Object} position + */ + + setPositionAddPaymentMenu(position) { + this.setState({ + anchorPositionTop: position.anchorPositionTop, + anchorPositionLeft: position.anchorPositionLeft, + }); + } + + /** + * 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. + * If they do have a valid payment method they are navigated to the "enable payments" route to complete KYC checks. + * If they are already KYC'd we will continue whatever action is gated behind the KYC wall. + * + * @param {Event} event + */ + continue(event) { + // Check to see if user has a valid payment method on file and display the add payment popover if they don't + if (!PaymentUtils.hasExpensifyPaymentMethod(this.props.cardList, this.props.bankAccountList)) { + Log.info('[KYC Wallet] User does not have valid payment method'); + let clickedElementLocation = getClickedElementLocation(event.nativeEvent); + let position = this.getAnchorPosition(clickedElementLocation); + if (this.props.isResizeListen) { + window.addEventListener('resize', () => { + clickedElementLocation = getClickedElementLocation(event.nativeEvent); + position = this.getAnchorPosition(clickedElementLocation); + this.setPositionAddPaymentMenu(position); + }); + } + this.setState({ + shouldShowAddPaymentMenu: true, + }); + this.setPositionAddPaymentMenu(position); + return; + } + + // Ask the user to upgrade to a gold wallet as this means they have not yet went through our Know Your Customer (KYC) checks + const hasGoldWallet = this.props.userWallet.tierName && this.props.userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD; + if (!hasGoldWallet) { + Log.info('[KYC Wallet] User does not have gold wallet'); + Navigation.navigate(this.props.enablePaymentsRoute); + return; + } + + Log.info('[KYC Wallet] User has valid payment method and passed KYC checks'); + this.props.onSuccessfulKYC(); + } + + render() { + return ( + <> + this.setState({shouldShowAddPaymentMenu: false})} + anchorPosition={{ + top: this.state.anchorPositionTop, + left: this.state.anchorPositionLeft, + }} + shouldShowPaypal={false} + 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.isLoadingPaymentMethods + ? () + : this.props.children(this.continue)} + + ); + } +} + +KYCWall.propTypes = propTypes; +KYCWall.defaultProps = defaultProps; + +export default withOnyx({ + userWallet: { + key: ONYXKEYS.USER_WALLET, + }, + cardList: { + key: ONYXKEYS.CARD_LIST, + }, + bankAccountList: { + key: ONYXKEYS.BANK_ACCOUNT_LIST, + }, + isLoadingPaymentMethods: { + key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, + initWithStoredValues: false, + }, +})(KYCWall); diff --git a/src/components/KYCWall/index.js b/src/components/KYCWall/index.js index 9fecb6cfbc79..170854474a31 100644 --- a/src/components/KYCWall/index.js +++ b/src/components/KYCWall/index.js @@ -1,177 +1,13 @@ import React from 'react'; -import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import {ActivityIndicator} from 'react-native'; -import themeColors from '../../styles/themes/default'; -import CONST from '../../CONST'; -import Navigation from '../../libs/Navigation/Navigation'; -import AddPaymentMethodMenu from '../AddPaymentMethodMenu'; -import getClickedElementLocation from '../../libs/getClickedElementLocation'; -import * as PaymentUtils from '../../libs/PaymentUtils'; -import * as PaymentMethods from '../../libs/actions/PaymentMethods'; -import ONYXKEYS from '../../ONYXKEYS'; -import userWalletPropTypes from '../../pages/EnablePayments/userWalletPropTypes'; -import Log from '../../libs/Log'; +import {propTypes, defaultProps} from './kycWallPropTypes'; +import BaseKYCWall from './BaseKYCWall'; -const propTypes = { - /** Route for the Add Bank Account screen for a given navigation stack */ - addBankAccountRoute: PropTypes.string.isRequired, - - /** Route for the Add Debit Card screen for a given navigation stack */ - addDebitCardRoute: PropTypes.string.isRequired, - - /** Route for the KYC enable payments screen for a given navigation stack */ - enablePaymentsRoute: PropTypes.string.isRequired, - - /** Where to place the popover */ - popoverPlacement: PropTypes.string, - - ...userWalletPropTypes, -}; - -const defaultProps = { - // eslint-disable-next-line react/default-props-match-prop-types - userWallet: {}, - popoverPlacement: 'top', -}; - -// 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.state = { - shouldShowAddPaymentMenu: false, - anchorPositionTop: 0, - anchorPositionLeft: 0, - }; - } - - componentDidMount() { - window.removeEventListener('resize', null); - PaymentMethods.getPaymentMethods(); - PaymentMethods.kycWallRef.current = this; - } - - componentWillUnmount() { - PaymentMethods.kycWallRef.current = null; - } - - /** - * @param {DOMRect} domRect - * @returns {Object} - */ - getAnchorPosition(domRect) { - if (this.props.popoverPlacement === 'bottom') { - return { - anchorPositionTop: domRect.top + (domRect.height - 2), - anchorPositionLeft: domRect.left + 20, - }; - } - - return { - anchorPositionTop: domRect.top - 150, - anchorPositionLeft: domRect.left, - }; - } - - /** - * Set position of the transfer payment menu - * - * @param {Object} position - */ - - setPositionAddPaymentMenu(position) { - this.setState({ - anchorPositionTop: position.anchorPositionTop, - anchorPositionLeft: position.anchorPositionLeft, - }); - } - - /** - * 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. - * If they do have a valid payment method they are navigated to the "enable payments" route to complete KYC checks. - * If they are already KYC'd we will continue whatever action is gated behind the KYC wall. - * - * @param {Event} event - */ - continue(event) { - // Check to see if user has a valid payment method on file and display the add payment popover if they don't - if (!PaymentUtils.hasExpensifyPaymentMethod(this.props.cardList, this.props.bankAccountList)) { - Log.info('[KYC Wallet] User does not have valid payment method'); - let clickedElementLocation = getClickedElementLocation(event.nativeEvent); - let position = this.getAnchorPosition(clickedElementLocation); - window.addEventListener('resize', () => { - clickedElementLocation = getClickedElementLocation(event.nativeEvent); - position = this.getAnchorPosition(clickedElementLocation); - this.setPositionAddPaymentMenu(position); - }); - this.setState({ - shouldShowAddPaymentMenu: true, - }); - this.setPositionAddPaymentMenu(position); - return; - } - - // Ask the user to upgrade to a gold wallet as this means they have not yet went through our Know Your Customer (KYC) checks - const hasGoldWallet = this.props.userWallet.tierName && this.props.userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD; - if (!hasGoldWallet) { - Log.info('[KYC Wallet] User does not have gold wallet'); - Navigation.navigate(this.props.enablePaymentsRoute); - return; - } - - Log.info('[KYC Wallet] User has valid payment method and passed KYC checks'); - this.props.onSuccessfulKYC(); - } - - render() { - return ( - <> - this.setState({shouldShowAddPaymentMenu: false})} - anchorPosition={{ - top: this.state.anchorPositionTop, - left: this.state.anchorPositionLeft, - }} - shouldShowPaypal={false} - 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.isLoadingPaymentMethods - ? () - : this.props.children(this.continue)} - - ); - } -} +const KYCWall = props => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +); KYCWall.propTypes = propTypes; KYCWall.defaultProps = defaultProps; - -export default withOnyx({ - userWallet: { - key: ONYXKEYS.USER_WALLET, - }, - cardList: { - key: ONYXKEYS.CARD_LIST, - }, - bankAccountList: { - key: ONYXKEYS.BANK_ACCOUNT_LIST, - }, - isLoadingPaymentMethods: { - key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, - initWithStoredValues: false, - }, -})(KYCWall); +KYCWall.displayName = 'KYCWall'; +export default KYCWall; diff --git a/src/components/KYCWall/index.native.js b/src/components/KYCWall/index.native.js index a03761a37278..86a1c54a55ce 100644 --- a/src/components/KYCWall/index.native.js +++ b/src/components/KYCWall/index.native.js @@ -1,159 +1,3 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import {withOnyx} from 'react-native-onyx'; -import {ActivityIndicator} from 'react-native'; -import themeColors from '../../styles/themes/default'; -import CONST from '../../CONST'; -import Navigation from '../../libs/Navigation/Navigation'; -import AddPaymentMethodMenu from '../AddPaymentMethodMenu'; -import getClickedElementLocation from '../../libs/getClickedElementLocation'; -import * as PaymentUtils from '../../libs/PaymentUtils'; -import * as PaymentMethods from '../../libs/actions/PaymentMethods'; -import ONYXKEYS from '../../ONYXKEYS'; -import userWalletPropTypes from '../../pages/EnablePayments/userWalletPropTypes'; -import Log from '../../libs/Log'; +import BaseKYCWall from './BaseKYCWall'; -const propTypes = { - /** Route for the Add Bank Account screen for a given navigation stack */ - addBankAccountRoute: PropTypes.string.isRequired, - - /** Route for the Add Debit Card screen for a given navigation stack */ - addDebitCardRoute: PropTypes.string.isRequired, - - /** Route for the KYC enable payments screen for a given navigation stack */ - enablePaymentsRoute: PropTypes.string.isRequired, - - /** Where to place the popover */ - popoverPlacement: PropTypes.string, - - ...userWalletPropTypes, -}; - -const defaultProps = { - // eslint-disable-next-line react/default-props-match-prop-types - userWallet: {}, - popoverPlacement: 'top', -}; - -// 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.state = { - shouldShowAddPaymentMenu: false, - anchorPositionTop: 0, - anchorPositionLeft: 0, - }; - } - - componentDidMount() { - PaymentMethods.getPaymentMethods(); - PaymentMethods.kycWallRef.current = this; - } - - componentWillUnmount() { - PaymentMethods.kycWallRef.current = null; - } - - /** - * @param {DOMRect} domRect - * @returns {Object} - */ - getAnchorPosition(domRect) { - if (this.props.popoverPlacement === 'bottom') { - return { - anchorPositionTop: domRect.top + (domRect.height - 2), - anchorPositionLeft: domRect.left + 20, - }; - } - - return { - anchorPositionTop: domRect.top - 150, - anchorPositionLeft: domRect.left, - }; - } - - /** - * 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. - * If they do have a valid payment method they are navigated to the "enable payments" route to complete KYC checks. - * If they are already KYC'd we will continue whatever action is gated behind the KYC wall. - * - * @param {Event} event - */ - continue(event) { - // Check to see if user has a valid payment method on file and display the add payment popover if they don't - if (!PaymentUtils.hasExpensifyPaymentMethod(this.props.cardList, this.props.bankAccountList)) { - Log.info('[KYC Wallet] User does not have valid payment method'); - const clickedElementLocation = getClickedElementLocation(event.nativeEvent); - const {anchorPositionTop, anchorPositionLeft} = this.getAnchorPosition(clickedElementLocation); - this.setState({ - shouldShowAddPaymentMenu: true, - anchorPositionTop, - anchorPositionLeft, - }); - return; - } - - // Ask the user to upgrade to a gold wallet as this means they have not yet went through our Know Your Customer (KYC) checks - const hasGoldWallet = this.props.userWallet.tierName && this.props.userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD; - if (!hasGoldWallet) { - Log.info('[KYC Wallet] User does not have gold wallet'); - Navigation.navigate(this.props.enablePaymentsRoute); - return; - } - - Log.info('[KYC Wallet] User has valid payment method and passed KYC checks'); - this.props.onSuccessfulKYC(); - } - - render() { - return ( - <> - this.setState({shouldShowAddPaymentMenu: false})} - anchorPosition={{ - top: this.state.anchorPositionTop, - left: this.state.anchorPositionLeft, - }} - shouldShowPaypal={false} - 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.isLoadingPaymentMethods - ? () - : this.props.children(this.continue)} - - ); - } -} - -KYCWall.propTypes = propTypes; -KYCWall.defaultProps = defaultProps; - -export default withOnyx({ - userWallet: { - key: ONYXKEYS.USER_WALLET, - }, - cardList: { - key: ONYXKEYS.CARD_LIST, - }, - bankAccountList: { - key: ONYXKEYS.BANK_ACCOUNT_LIST, - }, - isLoadingPaymentMethods: { - key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, - initWithStoredValues: false, - }, -})(KYCWall); +export default BaseKYCWall; diff --git a/src/components/KYCWall/kycWallPropTypes.js b/src/components/KYCWall/kycWallPropTypes.js new file mode 100644 index 000000000000..6c578f7b2956 --- /dev/null +++ b/src/components/KYCWall/kycWallPropTypes.js @@ -0,0 +1,30 @@ +import PropTypes from 'prop-types'; +import userWalletPropTypes from '../../pages/EnablePayments/userWalletPropTypes'; + +const propTypes = { + /** Route for the Add Bank Account screen for a given navigation stack */ + addBankAccountRoute: PropTypes.string.isRequired, + + /** Route for the Add Debit Card screen for a given navigation stack */ + addDebitCardRoute: PropTypes.string.isRequired, + + /** Route for the KYC enable payments screen for a given navigation stack */ + enablePaymentsRoute: PropTypes.string.isRequired, + + /** Where to place the popover */ + popoverPlacement: PropTypes.string, + + /** Is resize event happens on the web/desktop? */ + isResizeListen: PropTypes.bool, + + ...userWalletPropTypes, +}; + +const defaultProps = { + // eslint-disable-next-line react/default-props-match-prop-types + userWallet: {}, + popoverPlacement: 'top', + isResizeListen: false, +}; + +export {propTypes, defaultProps}; diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js new file mode 100644 index 000000000000..571f401f9e4a --- /dev/null +++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js @@ -0,0 +1,405 @@ +import React from 'react'; +import {View, TouchableOpacity} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import PaymentMethodList from '../PaymentMethodList'; +import ROUTES from '../../../../ROUTES'; +import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton'; +import PasswordPopover from '../../../../components/PasswordPopover'; +import ScreenWrapper from '../../../../components/ScreenWrapper'; +import Navigation from '../../../../libs/Navigation/Navigation'; +import styles from '../../../../styles/styles'; +import withLocalize from '../../../../components/withLocalize'; +import compose from '../../../../libs/compose'; +import KeyboardAvoidingView from '../../../../components/KeyboardAvoidingView/index'; +import * as BankAccounts from '../../../../libs/actions/BankAccounts'; +import Popover from '../../../../components/Popover'; +import MenuItem from '../../../../components/MenuItem'; +import Text from '../../../../components/Text'; +import * as PaymentMethods from '../../../../libs/actions/PaymentMethods'; +import getClickedElementLocation from '../../../../libs/getClickedElementLocation'; +import withWindowDimensions from '../../../../components/withWindowDimensions'; +import CurrentWalletBalance from '../../../../components/CurrentWalletBalance'; +import ONYXKEYS from '../../../../ONYXKEYS'; +import Permissions from '../../../../libs/Permissions'; +import ConfirmPopover from '../../../../components/ConfirmPopover'; +import AddPaymentMethodMenu from '../../../../components/AddPaymentMethodMenu'; +import CONST from '../../../../CONST'; +import * as Expensicons from '../../../../components/Icon/Expensicons'; +import ConfirmModal from '../../../../components/ConfirmModal'; +import KYCWall from '../../../../components/KYCWall'; +import {propTypes, defaultProps} from './paymentsPagePropTypes'; + +class BasePaymentsPage extends React.Component { + constructor(props) { + super(props); + + this.state = { + shouldShowAddPaymentMenu: false, + shouldShowDefaultDeleteMenu: false, + shouldShowPasswordPrompt: false, + shouldShowConfirmPopover: false, + selectedPaymentMethod: {}, + formattedSelectedPaymentMethod: {}, + anchorPositionTop: 0, + anchorPositionLeft: 0, + }; + + this.paymentMethodPressed = this.paymentMethodPressed.bind(this); + this.addPaymentMethodTypePressed = this.addPaymentMethodTypePressed.bind(this); + this.hideAddPaymentMenu = this.hideAddPaymentMenu.bind(this); + this.hideDefaultDeleteMenu = this.hideDefaultDeleteMenu.bind(this); + this.makeDefaultPaymentMethod = this.makeDefaultPaymentMethod.bind(this); + this.deletePaymentMethod = this.deletePaymentMethod.bind(this); + this.hidePasswordPrompt = this.hidePasswordPrompt.bind(this); + this.navigateToTransferBalancePage = this.navigateToTransferBalancePage.bind(this); + } + + componentDidMount() { + PaymentMethods.getPaymentMethods(); + } + + getSelectedPaymentMethodID() { + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { + return CONST.PAYMENT_METHODS.PAYPAL; + } + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + return this.state.selectedPaymentMethod.bankAccountID; + } + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + return this.state.selectedPaymentMethod.fundID; + } + } + + /** + * Set position of the payment menu + * + * @param {Object} position + */ + + setPositionAddPaymentMenu(position) { + this.setState({ + anchorPositionTop: position.bottom, + + // We want the position to be 13px to the right of the left border + anchorPositionLeft: position.left + 13, + }); + } + + /** + * Display the delete/default menu, or the add payment method menu + * + * @param {Object} nativeEvent + * @param {String} accountType + * @param {String} account + */ + paymentMethodPressed(nativeEvent, accountType, account) { + let position = getClickedElementLocation(nativeEvent); + if (this.props.isResizeListen) { + window.addEventListener('resize', () => { + position = getClickedElementLocation(nativeEvent); + this.setPositionAddPaymentMenu(position); + }); + } + if (accountType) { + let formattedSelectedPaymentMethod; + if (accountType === CONST.PAYMENT_METHODS.PAYPAL) { + formattedSelectedPaymentMethod = { + title: 'PayPal.me', + icon: account.icon, + description: account.username, + type: CONST.PAYMENT_METHODS.PAYPAL, + }; + } else if (accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + formattedSelectedPaymentMethod = { + title: account.addressName, + icon: account.icon, + description: `${this.props.translate('paymentMethodList.accountLastFour')} ${account.accountNumber.slice(-4)}`, + type: CONST.PAYMENT_METHODS.BANK_ACCOUNT, + }; + } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + formattedSelectedPaymentMethod = { + title: account.addressName, + icon: account.icon, + description: `${this.props.translate('paymentMethodList.cardLastFour')} ${account.cardNumber.slice(-4)}`, + type: CONST.PAYMENT_METHODS.DEBIT_CARD, + }; + } + this.setState({ + shouldShowDefaultDeleteMenu: true, + selectedPaymentMethod: account, + selectedPaymentMethodType: accountType, + formattedSelectedPaymentMethod, + }); + this.setPositionAddPaymentMenu(position); + } else { + this.setState({ + shouldShowAddPaymentMenu: true, + }); + this.setPositionAddPaymentMenu(position); + } + } + + /** + * Navigate to the appropriate payment type addition screen + * + * @param {String} paymentType + */ + addPaymentMethodTypePressed(paymentType) { + this.hideAddPaymentMenu(); + + if (paymentType === CONST.PAYMENT_METHODS.PAYPAL) { + Navigation.navigate(ROUTES.SETTINGS_ADD_PAYPAL_ME); + return; + } + + if (paymentType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + Navigation.navigate(ROUTES.SETTINGS_ADD_DEBIT_CARD); + return; + } + + if (paymentType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT); + return; + } + + throw new Error('Invalid payment method type selected'); + } + + /** + * Hide the add payment modal + */ + hideAddPaymentMenu() { + if (this.props.isResizeListen) { + window.removeEventListener('resize', null); + } + this.setState({shouldShowAddPaymentMenu: false}); + } + + /** + * Hide the default / delete modal + */ + hideDefaultDeleteMenu() { + this.setState({shouldShowDefaultDeleteMenu: false}); + } + + hidePasswordPrompt() { + this.setState({shouldShowPasswordPrompt: false}); + } + + makeDefaultPaymentMethod(password) { + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + PaymentMethods.setWalletLinkedAccount(password, this.state.selectedPaymentMethod.bankAccountID, null); + } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + PaymentMethods.setWalletLinkedAccount(password, null, this.state.selectedPaymentMethod.fundID); + } + } + + deletePaymentMethod() { + if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { + PaymentMethods.deletePayPalMe(); + } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { + BankAccounts.deleteBankAccount(this.state.selectedPaymentMethod.bankAccountID); + } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { + PaymentMethods.deleteDebitCard(this.state.selectedPaymentMethod.fundID); + } + } + + navigateToTransferBalancePage() { + Navigation.navigate(ROUTES.SETTINGS_PAYMENTS_TRANSFER_BALANCE); + } + + render() { + const isPayPalMeSelected = this.state.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.PAYPAL; + return ( + + + Navigation.navigate(ROUTES.SETTINGS)} + onCloseButtonPress={() => Navigation.dismissModal(true)} + /> + + {Permissions.canUseWallet(this.props.betas) && ( + <> + + + + + {triggerKYCFlow => ( + + )} + + + )} + + {this.props.translate('paymentsPage.paymentMethodsTitle')} + + + + this.addPaymentMethodTypePressed(method)} + /> + + + {this.props.isSmallScreenWidth && ( + + )} + {Permissions.canUseWallet(this.props.betas) && ( + { + this.setState({ + shouldShowPasswordPrompt: true, + shouldShowDefaultDeleteMenu: false, + passwordButtonText: this.props.translate('paymentsPage.setDefaultConfirmation'), + }); + }} + style={[styles.button, isPayPalMeSelected && styles.buttonDisable, styles.alignSelfCenter, styles.w100]} + disabled={isPayPalMeSelected} + > + + {this.props.translate('paymentsPage.setDefaultConfirmation')} + + + )} + { + this.setState({ + shouldShowDefaultDeleteMenu: false, + shouldShowConfirmPopover: true, + }); + }} + style={[ + styles.button, + styles.buttonDanger, + Permissions.canUseWallet(this.props.betas) && styles.mt4, + styles.alignSelfCenter, + styles.w100, + ]} + > + + {this.props.translate('common.delete')} + + + + + { + this.hidePasswordPrompt(); + this.makeDefaultPaymentMethod(password); + }} + submitButtonText={this.state.passwordButtonText} + isDangerousAction + /> + { + this.setState({ + shouldShowConfirmPopover: false, + }); + this.deletePaymentMethod(); + }} + onCancel={() => { + this.setState({shouldShowConfirmPopover: false}); + }} + shouldShowCancelButton + danger + /> + + + + ); + } +} + +BasePaymentsPage.propTypes = propTypes; +BasePaymentsPage.defaultProps = defaultProps; + +export default compose( + withWindowDimensions, + withLocalize, + withOnyx({ + betas: { + key: ONYXKEYS.BETAS, + }, + walletTransfer: { + key: ONYXKEYS.WALLET_TRANSFER, + }, + isLoadingPaymentMethods: { + key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, + initWithStoredValues: false, + }, + }), +)(BasePaymentsPage); diff --git a/src/pages/settings/Payments/PaymentsPage/index.js b/src/pages/settings/Payments/PaymentsPage/index.js index 842fff5900a2..a98058b95f23 100644 --- a/src/pages/settings/Payments/PaymentsPage/index.js +++ b/src/pages/settings/Payments/PaymentsPage/index.js @@ -1,428 +1,9 @@ import React from 'react'; -import {View, TouchableOpacity} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import PropTypes from 'prop-types'; -import PaymentMethodList from '../PaymentMethodList'; -import ROUTES from '../../../../ROUTES'; -import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton'; -import PasswordPopover from '../../../../components/PasswordPopover'; -import ScreenWrapper from '../../../../components/ScreenWrapper'; -import Navigation from '../../../../libs/Navigation/Navigation'; -import styles from '../../../../styles/styles'; -import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; -import compose from '../../../../libs/compose'; -import KeyboardAvoidingView from '../../../../components/KeyboardAvoidingView/index'; -import * as BankAccounts from '../../../../libs/actions/BankAccounts'; -import Popover from '../../../../components/Popover'; -import MenuItem from '../../../../components/MenuItem'; -import Text from '../../../../components/Text'; -import * as PaymentMethods from '../../../../libs/actions/PaymentMethods'; -import getClickedElementLocation from '../../../../libs/getClickedElementLocation'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; -import CurrentWalletBalance from '../../../../components/CurrentWalletBalance'; -import ONYXKEYS from '../../../../ONYXKEYS'; -import Permissions from '../../../../libs/Permissions'; -import ConfirmPopover from '../../../../components/ConfirmPopover'; -import AddPaymentMethodMenu from '../../../../components/AddPaymentMethodMenu'; -import CONST from '../../../../CONST'; -import * as Expensicons from '../../../../components/Icon/Expensicons'; -import walletTransferPropTypes from '../walletTransferPropTypes'; -import ConfirmModal from '../../../../components/ConfirmModal'; -import KYCWall from '../../../../components/KYCWall'; +import BasePaymentsPage from './BasePaymentsPage'; -const propTypes = { - /** Wallet balance transfer props */ - walletTransfer: walletTransferPropTypes, +const PaymentsPage = () => ( + +); - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), - - /** Are we loading payment methods? */ - isLoadingPaymentMethods: PropTypes.bool, - - ...withLocalizePropTypes, - - ...windowDimensionsPropTypes, -}; - -const defaultProps = { - walletTransfer: { - shouldShowConfirmModal: false, - }, - betas: [], - isLoadingPaymentMethods: true, -}; - -class PaymentsPage extends React.Component { - constructor(props) { - super(props); - - this.state = { - shouldShowAddPaymentMenu: false, - shouldShowDefaultDeleteMenu: false, - shouldShowPasswordPrompt: false, - shouldShowConfirmPopover: false, - selectedPaymentMethod: {}, - formattedSelectedPaymentMethod: {}, - anchorPositionTop: 0, - anchorPositionLeft: 0, - }; - - this.paymentMethodPressed = this.paymentMethodPressed.bind(this); - this.addPaymentMethodTypePressed = this.addPaymentMethodTypePressed.bind(this); - this.hideAddPaymentMenu = this.hideAddPaymentMenu.bind(this); - this.hideDefaultDeleteMenu = this.hideDefaultDeleteMenu.bind(this); - this.makeDefaultPaymentMethod = this.makeDefaultPaymentMethod.bind(this); - this.deletePaymentMethod = this.deletePaymentMethod.bind(this); - this.hidePasswordPrompt = this.hidePasswordPrompt.bind(this); - this.navigateToTransferBalancePage = this.navigateToTransferBalancePage.bind(this); - } - - componentDidMount() { - PaymentMethods.getPaymentMethods(); - } - - getSelectedPaymentMethodID() { - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { - return CONST.PAYMENT_METHODS.PAYPAL; - } - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - return this.state.selectedPaymentMethod.bankAccountID; - } - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - return this.state.selectedPaymentMethod.fundID; - } - } - - /** - * Set position of the payment menu - * - * @param {Object} position - */ - - setPositionAddPaymentMenu(position) { - this.setState({ - anchorPositionTop: position.bottom, - - // We want the position to be 13px to the right of the left border - anchorPositionLeft: position.left + 13, - }); - } - - /** - * Display the delete/default menu, or the add payment method menu - * - * @param {Object} nativeEvent - * @param {String} accountType - * @param {String} account - */ - paymentMethodPressed(nativeEvent, accountType, account) { - let position = getClickedElementLocation(nativeEvent); - window.addEventListener('resize', () => { - position = getClickedElementLocation(nativeEvent); - this.setPositionAddPaymentMenu(position); - }); - if (accountType) { - let formattedSelectedPaymentMethod; - if (accountType === CONST.PAYMENT_METHODS.PAYPAL) { - formattedSelectedPaymentMethod = { - title: 'PayPal.me', - icon: account.icon, - description: account.username, - type: CONST.PAYMENT_METHODS.PAYPAL, - }; - } else if (accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - formattedSelectedPaymentMethod = { - title: account.addressName, - icon: account.icon, - description: `${this.props.translate('paymentMethodList.accountLastFour')} ${account.accountNumber.slice(-4)}`, - type: CONST.PAYMENT_METHODS.BANK_ACCOUNT, - }; - } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - formattedSelectedPaymentMethod = { - title: account.addressName, - icon: account.icon, - description: `${this.props.translate('paymentMethodList.cardLastFour')} ${account.cardNumber.slice(-4)}`, - type: CONST.PAYMENT_METHODS.DEBIT_CARD, - }; - } - this.setState({ - shouldShowDefaultDeleteMenu: true, - selectedPaymentMethod: account, - selectedPaymentMethodType: accountType, - anchorPositionTop: position.bottom, - - // We want the position to be 13px to the right of the left border - anchorPositionLeft: position.left + 13, - formattedSelectedPaymentMethod, - }); - } else { - this.setState({ - shouldShowAddPaymentMenu: true, - }); - this.setPositionAddPaymentMenu(position); - } - } - - /** - * Navigate to the appropriate payment type addition screen - * - * @param {String} paymentType - */ - addPaymentMethodTypePressed(paymentType) { - this.hideAddPaymentMenu(); - - if (paymentType === CONST.PAYMENT_METHODS.PAYPAL) { - Navigation.navigate(ROUTES.SETTINGS_ADD_PAYPAL_ME); - return; - } - - if (paymentType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - Navigation.navigate(ROUTES.SETTINGS_ADD_DEBIT_CARD); - return; - } - - if (paymentType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT); - return; - } - - throw new Error('Invalid payment method type selected'); - } - - /** - * Hide the add payment modal - */ - hideAddPaymentMenu() { - window.removeEventListener('resize', null); - this.setState({shouldShowAddPaymentMenu: false}); - } - - /** - * Hide the default / delete modal - */ - hideDefaultDeleteMenu() { - this.setState({shouldShowDefaultDeleteMenu: false}); - } - - hidePasswordPrompt() { - this.setState({shouldShowPasswordPrompt: false}); - } - - makeDefaultPaymentMethod(password) { - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - PaymentMethods.setWalletLinkedAccount(password, this.state.selectedPaymentMethod.bankAccountID, null); - } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.setWalletLinkedAccount(password, null, this.state.selectedPaymentMethod.fundID); - } - } - - deletePaymentMethod() { - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { - PaymentMethods.deletePayPalMe(); - } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - BankAccounts.deleteBankAccount(this.state.selectedPaymentMethod.bankAccountID); - } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.deleteDebitCard(this.state.selectedPaymentMethod.fundID); - } - } - - navigateToTransferBalancePage() { - Navigation.navigate(ROUTES.SETTINGS_PAYMENTS_TRANSFER_BALANCE); - } - - render() { - const isPayPalMeSelected = this.state.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.PAYPAL; - return ( - - - Navigation.navigate(ROUTES.SETTINGS)} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> - - {Permissions.canUseWallet(this.props.betas) && ( - <> - - - - - {triggerKYCFlow => ( - - )} - - - )} - - {this.props.translate('paymentsPage.paymentMethodsTitle')} - - - - this.addPaymentMethodTypePressed(method)} - /> - - - {this.props.isSmallScreenWidth && ( - - )} - {Permissions.canUseWallet(this.props.betas) && ( - { - this.setState({ - shouldShowPasswordPrompt: true, - shouldShowDefaultDeleteMenu: false, - passwordButtonText: this.props.translate('paymentsPage.setDefaultConfirmation'), - }); - }} - style={[styles.button, isPayPalMeSelected && styles.buttonDisable, styles.alignSelfCenter, styles.w100]} - disabled={isPayPalMeSelected} - > - - {this.props.translate('paymentsPage.setDefaultConfirmation')} - - - )} - { - this.setState({ - shouldShowDefaultDeleteMenu: false, - shouldShowConfirmPopover: true, - }); - }} - style={[ - styles.button, - styles.buttonDanger, - Permissions.canUseWallet(this.props.betas) && styles.mt4, - styles.alignSelfCenter, - styles.w100, - ]} - > - - {this.props.translate('common.delete')} - - - - - { - this.hidePasswordPrompt(); - this.makeDefaultPaymentMethod(password); - }} - submitButtonText={this.state.passwordButtonText} - isDangerousAction - /> - { - this.setState({ - shouldShowConfirmPopover: false, - }); - this.deletePaymentMethod(); - }} - onCancel={() => { - this.setState({shouldShowConfirmPopover: false}); - }} - shouldShowCancelButton - danger - /> - - - - ); - } -} - -PaymentsPage.propTypes = propTypes; -PaymentsPage.defaultProps = defaultProps; - -export default compose( - withWindowDimensions, - withLocalize, - withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, - walletTransfer: { - key: ONYXKEYS.WALLET_TRANSFER, - }, - isLoadingPaymentMethods: { - key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, - initWithStoredValues: false, - }, - }), -)(PaymentsPage); +PaymentsPage.displayName = 'PaymentsPage'; +export default PaymentsPage; diff --git a/src/pages/settings/Payments/PaymentsPage/index.native.js b/src/pages/settings/Payments/PaymentsPage/index.native.js index 80ce7b98ca70..b0b43a8e6661 100644 --- a/src/pages/settings/Payments/PaymentsPage/index.native.js +++ b/src/pages/settings/Payments/PaymentsPage/index.native.js @@ -1,411 +1,3 @@ -import React from 'react'; -import {View, TouchableOpacity} from 'react-native'; -import {withOnyx} from 'react-native-onyx'; -import PropTypes from 'prop-types'; -import PaymentMethodList from '../PaymentMethodList'; -import ROUTES from '../../../../ROUTES'; -import HeaderWithCloseButton from '../../../../components/HeaderWithCloseButton'; -import PasswordPopover from '../../../../components/PasswordPopover'; -import ScreenWrapper from '../../../../components/ScreenWrapper'; -import Navigation from '../../../../libs/Navigation/Navigation'; -import styles from '../../../../styles/styles'; -import withLocalize, {withLocalizePropTypes} from '../../../../components/withLocalize'; -import compose from '../../../../libs/compose'; -import KeyboardAvoidingView from '../../../../components/KeyboardAvoidingView/index'; -import * as BankAccounts from '../../../../libs/actions/BankAccounts'; -import Popover from '../../../../components/Popover'; -import MenuItem from '../../../../components/MenuItem'; -import Text from '../../../../components/Text'; -import * as PaymentMethods from '../../../../libs/actions/PaymentMethods'; -import getClickedElementLocation from '../../../../libs/getClickedElementLocation'; -import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; -import CurrentWalletBalance from '../../../../components/CurrentWalletBalance'; -import ONYXKEYS from '../../../../ONYXKEYS'; -import Permissions from '../../../../libs/Permissions'; -import ConfirmPopover from '../../../../components/ConfirmPopover'; -import AddPaymentMethodMenu from '../../../../components/AddPaymentMethodMenu'; -import CONST from '../../../../CONST'; -import * as Expensicons from '../../../../components/Icon/Expensicons'; -import walletTransferPropTypes from '../walletTransferPropTypes'; -import ConfirmModal from '../../../../components/ConfirmModal'; -import KYCWall from '../../../../components/KYCWall'; +import BasePaymentsPage from './BasePaymentsPage'; -const propTypes = { - /** Wallet balance transfer props */ - walletTransfer: walletTransferPropTypes, - - /** List of betas available to current user */ - betas: PropTypes.arrayOf(PropTypes.string), - - /** Are we loading payment methods? */ - isLoadingPaymentMethods: PropTypes.bool, - - ...withLocalizePropTypes, - - ...windowDimensionsPropTypes, -}; - -const defaultProps = { - walletTransfer: { - shouldShowConfirmModal: false, - }, - betas: [], - isLoadingPaymentMethods: true, -}; - -class PaymentsPage extends React.Component { - constructor(props) { - super(props); - - this.state = { - shouldShowAddPaymentMenu: false, - shouldShowDefaultDeleteMenu: false, - shouldShowPasswordPrompt: false, - shouldShowConfirmPopover: false, - selectedPaymentMethod: {}, - formattedSelectedPaymentMethod: {}, - anchorPositionTop: 0, - anchorPositionLeft: 0, - }; - - this.paymentMethodPressed = this.paymentMethodPressed.bind(this); - this.addPaymentMethodTypePressed = this.addPaymentMethodTypePressed.bind(this); - this.hideAddPaymentMenu = this.hideAddPaymentMenu.bind(this); - this.hideDefaultDeleteMenu = this.hideDefaultDeleteMenu.bind(this); - this.makeDefaultPaymentMethod = this.makeDefaultPaymentMethod.bind(this); - this.deletePaymentMethod = this.deletePaymentMethod.bind(this); - this.hidePasswordPrompt = this.hidePasswordPrompt.bind(this); - this.navigateToTransferBalancePage = this.navigateToTransferBalancePage.bind(this); - } - - componentDidMount() { - PaymentMethods.getPaymentMethods(); - } - - getSelectedPaymentMethodID() { - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { - return CONST.PAYMENT_METHODS.PAYPAL; - } - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - return this.state.selectedPaymentMethod.bankAccountID; - } - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - return this.state.selectedPaymentMethod.fundID; - } - } - - /** - * Display the delete/default menu, or the add payment method menu - * - * @param {Object} nativeEvent - * @param {String} accountType - * @param {String} account - */ - paymentMethodPressed(nativeEvent, accountType, account) { - const position = getClickedElementLocation(nativeEvent); - if (accountType) { - let formattedSelectedPaymentMethod; - if (accountType === CONST.PAYMENT_METHODS.PAYPAL) { - formattedSelectedPaymentMethod = { - title: 'PayPal.me', - icon: account.icon, - description: account.username, - type: CONST.PAYMENT_METHODS.PAYPAL, - }; - } else if (accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - formattedSelectedPaymentMethod = { - title: account.addressName, - icon: account.icon, - description: `${this.props.translate('paymentMethodList.accountLastFour')} ${account.accountNumber.slice(-4)}`, - type: CONST.PAYMENT_METHODS.BANK_ACCOUNT, - }; - } else if (accountType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - formattedSelectedPaymentMethod = { - title: account.addressName, - icon: account.icon, - description: `${this.props.translate('paymentMethodList.cardLastFour')} ${account.cardNumber.slice(-4)}`, - type: CONST.PAYMENT_METHODS.DEBIT_CARD, - }; - } - this.setState({ - shouldShowDefaultDeleteMenu: true, - selectedPaymentMethod: account, - selectedPaymentMethodType: accountType, - anchorPositionTop: position.bottom, - - // We want the position to be 13px to the right of the left border - anchorPositionLeft: position.left + 13, - formattedSelectedPaymentMethod, - }); - } else { - this.setState({ - shouldShowAddPaymentMenu: true, - anchorPositionTop: position.bottom, - - // We want the position to be 13px to the right of the left border - anchorPositionLeft: position.left + 13, - }); - } - } - - /** - * Navigate to the appropriate payment type addition screen - * - * @param {String} paymentType - */ - addPaymentMethodTypePressed(paymentType) { - this.hideAddPaymentMenu(); - - if (paymentType === CONST.PAYMENT_METHODS.PAYPAL) { - Navigation.navigate(ROUTES.SETTINGS_ADD_PAYPAL_ME); - return; - } - - if (paymentType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - Navigation.navigate(ROUTES.SETTINGS_ADD_DEBIT_CARD); - return; - } - - if (paymentType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT); - return; - } - - throw new Error('Invalid payment method type selected'); - } - - /** - * Hide the add payment modal - */ - hideAddPaymentMenu() { - this.setState({shouldShowAddPaymentMenu: false}); - } - - /** - * Hide the default / delete modal - */ - hideDefaultDeleteMenu() { - this.setState({shouldShowDefaultDeleteMenu: false}); - } - - hidePasswordPrompt() { - this.setState({shouldShowPasswordPrompt: false}); - } - - makeDefaultPaymentMethod(password) { - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - PaymentMethods.setWalletLinkedAccount(password, this.state.selectedPaymentMethod.bankAccountID, null); - } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.setWalletLinkedAccount(password, null, this.state.selectedPaymentMethod.fundID); - } - } - - deletePaymentMethod() { - if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.PAYPAL) { - PaymentMethods.deletePayPalMe(); - } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT) { - BankAccounts.deleteBankAccount(this.state.selectedPaymentMethod.bankAccountID); - } else if (this.state.selectedPaymentMethodType === CONST.PAYMENT_METHODS.DEBIT_CARD) { - PaymentMethods.deleteDebitCard(this.state.selectedPaymentMethod.fundID); - } - } - - navigateToTransferBalancePage() { - Navigation.navigate(ROUTES.SETTINGS_PAYMENTS_TRANSFER_BALANCE); - } - - render() { - const isPayPalMeSelected = this.state.formattedSelectedPaymentMethod.type === CONST.PAYMENT_METHODS.PAYPAL; - return ( - - - Navigation.navigate(ROUTES.SETTINGS)} - onCloseButtonPress={() => Navigation.dismissModal(true)} - /> - - {Permissions.canUseWallet(this.props.betas) && ( - <> - - - - - {triggerKYCFlow => ( - - )} - - - )} - - {this.props.translate('paymentsPage.paymentMethodsTitle')} - - - - this.addPaymentMethodTypePressed(method)} - /> - - - {this.props.isSmallScreenWidth && ( - - )} - {Permissions.canUseWallet(this.props.betas) && ( - { - this.setState({ - shouldShowPasswordPrompt: true, - shouldShowDefaultDeleteMenu: false, - passwordButtonText: this.props.translate('paymentsPage.setDefaultConfirmation'), - }); - }} - style={[styles.button, isPayPalMeSelected && styles.buttonDisable, styles.alignSelfCenter, styles.w100]} - disabled={isPayPalMeSelected} - > - - {this.props.translate('paymentsPage.setDefaultConfirmation')} - - - )} - { - this.setState({ - shouldShowDefaultDeleteMenu: false, - shouldShowConfirmPopover: true, - }); - }} - style={[ - styles.button, - styles.buttonDanger, - Permissions.canUseWallet(this.props.betas) && styles.mt4, - styles.alignSelfCenter, - styles.w100, - ]} - > - - {this.props.translate('common.delete')} - - - - - { - this.hidePasswordPrompt(); - this.makeDefaultPaymentMethod(password); - }} - submitButtonText={this.state.passwordButtonText} - isDangerousAction - /> - { - this.setState({ - shouldShowConfirmPopover: false, - }); - this.deletePaymentMethod(); - }} - onCancel={() => { - this.setState({shouldShowConfirmPopover: false}); - }} - shouldShowCancelButton - danger - /> - - - - ); - } -} - -PaymentsPage.propTypes = propTypes; -PaymentsPage.defaultProps = defaultProps; - -export default compose( - withWindowDimensions, - withLocalize, - withOnyx({ - betas: { - key: ONYXKEYS.BETAS, - }, - walletTransfer: { - key: ONYXKEYS.WALLET_TRANSFER, - }, - isLoadingPaymentMethods: { - key: ONYXKEYS.IS_LOADING_PAYMENT_METHODS, - initWithStoredValues: false, - }, - }), -)(PaymentsPage); +export default BasePaymentsPage; diff --git a/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js b/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js new file mode 100644 index 000000000000..696785701259 --- /dev/null +++ b/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js @@ -0,0 +1,33 @@ +import PropTypes from 'prop-types'; +import walletTransferPropTypes from '../walletTransferPropTypes'; +import {withLocalizePropTypes} from '../../../../components/withLocalize'; +import {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions'; + +const propTypes = { + /** Wallet balance transfer props */ + walletTransfer: walletTransferPropTypes, + + /** List of betas available to current user */ + betas: PropTypes.arrayOf(PropTypes.string), + + /** Are we loading payment methods? */ + isLoadingPaymentMethods: PropTypes.bool, + + /** Is resize event happens on the web/desktop? */ + isResizeListen: PropTypes.bool, + + ...withLocalizePropTypes, + + ...windowDimensionsPropTypes, +}; + +const defaultProps = { + walletTransfer: { + shouldShowConfirmModal: false, + }, + betas: [], + isLoadingPaymentMethods: true, + isResizeListen: false, +}; + +export {propTypes, defaultProps}; From 9de73c1221cf2ab017eb2a24df9b8820924db274 Mon Sep 17 00:00:00 2001 From: phivh Date: Tue, 8 Feb 2022 00:01:15 +0700 Subject: [PATCH 3/4] cleanup --- src/components/Form.js | 1 - src/components/KYCWall/BaseKYCWall.js | 8 ++++---- src/components/KYCWall/index.js | 3 ++- src/components/KYCWall/kycWallPropTypes.js | 6 +++--- .../Payments/PaymentsPage/BasePaymentsPage.js | 18 ++++++++++-------- .../settings/Payments/PaymentsPage/index.js | 3 ++- .../PaymentsPage/paymentsPagePropTypes.js | 6 +++--- 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/Form.js b/src/components/Form.js index 7871b3561f15..c169113f8097 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -122,7 +122,6 @@ class Form extends React.Component { * @returns {React.Component} */ childrenWrapperWithProps(children) { - // eslint-disable-next-line rulesdir/prefer-underscore-method return React.Children.map(children, (child) => { // Just render the child if it is not a valid React element, e.g. text within a component if (!React.isValidElement(child)) { diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 87ec1b8755cf..2963d32bf6e0 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -29,14 +29,14 @@ class KYCWall extends React.Component { } componentDidMount() { - if (this.props.isResizeListen) { - window.removeEventListener('resize', null); - } PaymentMethods.getPaymentMethods(); PaymentMethods.kycWallRef.current = this; } componentWillUnmount() { + if (this.props.listenResize) { + window.removeEventListener('resize', null); + } PaymentMethods.kycWallRef.current = null; } @@ -84,7 +84,7 @@ class KYCWall extends React.Component { Log.info('[KYC Wallet] User does not have valid payment method'); let clickedElementLocation = getClickedElementLocation(event.nativeEvent); let position = this.getAnchorPosition(clickedElementLocation); - if (this.props.isResizeListen) { + if (this.props.listenResize) { window.addEventListener('resize', () => { clickedElementLocation = getClickedElementLocation(event.nativeEvent); position = this.getAnchorPosition(clickedElementLocation); diff --git a/src/components/KYCWall/index.js b/src/components/KYCWall/index.js index 170854474a31..75732743ac1e 100644 --- a/src/components/KYCWall/index.js +++ b/src/components/KYCWall/index.js @@ -4,10 +4,11 @@ import BaseKYCWall from './BaseKYCWall'; const KYCWall = props => ( // eslint-disable-next-line react/jsx-props-no-spreading - + ); KYCWall.propTypes = propTypes; KYCWall.defaultProps = defaultProps; KYCWall.displayName = 'KYCWall'; + export default KYCWall; diff --git a/src/components/KYCWall/kycWallPropTypes.js b/src/components/KYCWall/kycWallPropTypes.js index 6c578f7b2956..2de7db98e50d 100644 --- a/src/components/KYCWall/kycWallPropTypes.js +++ b/src/components/KYCWall/kycWallPropTypes.js @@ -14,8 +14,8 @@ const propTypes = { /** Where to place the popover */ popoverPlacement: PropTypes.string, - /** Is resize event happens on the web/desktop? */ - isResizeListen: PropTypes.bool, + /** Listen for window resize event on web and desktop */ + listenResize: PropTypes.bool, ...userWalletPropTypes, }; @@ -24,7 +24,7 @@ const defaultProps = { // eslint-disable-next-line react/default-props-match-prop-types userWallet: {}, popoverPlacement: 'top', - isResizeListen: false, + listenResize: false, }; export {propTypes, defaultProps}; diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js index 571f401f9e4a..077a7c0b4b8f 100644 --- a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js +++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js @@ -75,7 +75,6 @@ class BasePaymentsPage extends React.Component { * * @param {Object} position */ - setPositionAddPaymentMenu(position) { this.setState({ anchorPositionTop: position.bottom, @@ -94,7 +93,7 @@ class BasePaymentsPage extends React.Component { */ paymentMethodPressed(nativeEvent, accountType, account) { let position = getClickedElementLocation(nativeEvent); - if (this.props.isResizeListen) { + if (this.props.listenResize) { window.addEventListener('resize', () => { position = getClickedElementLocation(nativeEvent); this.setPositionAddPaymentMenu(position); @@ -131,12 +130,12 @@ class BasePaymentsPage extends React.Component { formattedSelectedPaymentMethod, }); this.setPositionAddPaymentMenu(position); - } else { - this.setState({ - shouldShowAddPaymentMenu: true, - }); - this.setPositionAddPaymentMenu(position); + return; } + this.setState({ + shouldShowAddPaymentMenu: true, + }); + this.setPositionAddPaymentMenu(position); } /** @@ -169,7 +168,7 @@ class BasePaymentsPage extends React.Component { * Hide the add payment modal */ hideAddPaymentMenu() { - if (this.props.isResizeListen) { + if (this.props.listenResize) { window.removeEventListener('resize', null); } this.setState({shouldShowAddPaymentMenu: false}); @@ -179,6 +178,9 @@ class BasePaymentsPage extends React.Component { * Hide the default / delete modal */ hideDefaultDeleteMenu() { + if (this.props.listenResize) { + window.removeEventListener('resize', null); + } this.setState({shouldShowDefaultDeleteMenu: false}); } diff --git a/src/pages/settings/Payments/PaymentsPage/index.js b/src/pages/settings/Payments/PaymentsPage/index.js index a98058b95f23..9a53fb9b8e1f 100644 --- a/src/pages/settings/Payments/PaymentsPage/index.js +++ b/src/pages/settings/Payments/PaymentsPage/index.js @@ -2,8 +2,9 @@ import React from 'react'; import BasePaymentsPage from './BasePaymentsPage'; const PaymentsPage = () => ( - + ); PaymentsPage.displayName = 'PaymentsPage'; + export default PaymentsPage; diff --git a/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js b/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js index 696785701259..fd83f5ade9ff 100644 --- a/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js +++ b/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js @@ -13,8 +13,8 @@ const propTypes = { /** Are we loading payment methods? */ isLoadingPaymentMethods: PropTypes.bool, - /** Is resize event happens on the web/desktop? */ - isResizeListen: PropTypes.bool, + /** Listen for window resize event on web and desktop. */ + listenResize: PropTypes.bool, ...withLocalizePropTypes, @@ -27,7 +27,7 @@ const defaultProps = { }, betas: [], isLoadingPaymentMethods: true, - isResizeListen: false, + listenResize: false, }; export {propTypes, defaultProps}; From 2298ebc829e69a576a1e5057e25864e12285d172 Mon Sep 17 00:00:00 2001 From: phivh Date: Tue, 8 Feb 2022 13:59:34 +0700 Subject: [PATCH 4/4] Change usage of boolean prop name --- src/components/KYCWall/BaseKYCWall.js | 5 ++--- src/components/KYCWall/index.js | 2 +- src/components/KYCWall/kycWallPropTypes.js | 4 ++-- .../settings/Payments/PaymentsPage/BasePaymentsPage.js | 6 +++--- src/pages/settings/Payments/PaymentsPage/index.js | 2 +- .../settings/Payments/PaymentsPage/paymentsPagePropTypes.js | 4 ++-- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.js b/src/components/KYCWall/BaseKYCWall.js index 2963d32bf6e0..dbcd0c39b062 100644 --- a/src/components/KYCWall/BaseKYCWall.js +++ b/src/components/KYCWall/BaseKYCWall.js @@ -34,7 +34,7 @@ class KYCWall extends React.Component { } componentWillUnmount() { - if (this.props.listenResize) { + if (this.props.shouldListenForResize) { window.removeEventListener('resize', null); } PaymentMethods.kycWallRef.current = null; @@ -63,7 +63,6 @@ class KYCWall extends React.Component { * * @param {Object} position */ - setPositionAddPaymentMenu(position) { this.setState({ anchorPositionTop: position.anchorPositionTop, @@ -84,7 +83,7 @@ class KYCWall extends React.Component { Log.info('[KYC Wallet] User does not have valid payment method'); let clickedElementLocation = getClickedElementLocation(event.nativeEvent); let position = this.getAnchorPosition(clickedElementLocation); - if (this.props.listenResize) { + if (this.props.shouldListenForResize) { window.addEventListener('resize', () => { clickedElementLocation = getClickedElementLocation(event.nativeEvent); position = this.getAnchorPosition(clickedElementLocation); diff --git a/src/components/KYCWall/index.js b/src/components/KYCWall/index.js index 75732743ac1e..287694560b17 100644 --- a/src/components/KYCWall/index.js +++ b/src/components/KYCWall/index.js @@ -4,7 +4,7 @@ import BaseKYCWall from './BaseKYCWall'; const KYCWall = props => ( // eslint-disable-next-line react/jsx-props-no-spreading - + ); KYCWall.propTypes = propTypes; diff --git a/src/components/KYCWall/kycWallPropTypes.js b/src/components/KYCWall/kycWallPropTypes.js index 2de7db98e50d..40403f120f03 100644 --- a/src/components/KYCWall/kycWallPropTypes.js +++ b/src/components/KYCWall/kycWallPropTypes.js @@ -15,7 +15,7 @@ const propTypes = { popoverPlacement: PropTypes.string, /** Listen for window resize event on web and desktop */ - listenResize: PropTypes.bool, + shouldListenForResize: PropTypes.bool, ...userWalletPropTypes, }; @@ -24,7 +24,7 @@ const defaultProps = { // eslint-disable-next-line react/default-props-match-prop-types userWallet: {}, popoverPlacement: 'top', - listenResize: false, + shouldListenForResize: false, }; export {propTypes, defaultProps}; diff --git a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js index 077a7c0b4b8f..0026d1cf0c72 100644 --- a/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js +++ b/src/pages/settings/Payments/PaymentsPage/BasePaymentsPage.js @@ -93,7 +93,7 @@ class BasePaymentsPage extends React.Component { */ paymentMethodPressed(nativeEvent, accountType, account) { let position = getClickedElementLocation(nativeEvent); - if (this.props.listenResize) { + if (this.props.shouldListenForResize) { window.addEventListener('resize', () => { position = getClickedElementLocation(nativeEvent); this.setPositionAddPaymentMenu(position); @@ -168,7 +168,7 @@ class BasePaymentsPage extends React.Component { * Hide the add payment modal */ hideAddPaymentMenu() { - if (this.props.listenResize) { + if (this.props.shouldListenForResize) { window.removeEventListener('resize', null); } this.setState({shouldShowAddPaymentMenu: false}); @@ -178,7 +178,7 @@ class BasePaymentsPage extends React.Component { * Hide the default / delete modal */ hideDefaultDeleteMenu() { - if (this.props.listenResize) { + if (this.props.shouldListenForResize) { window.removeEventListener('resize', null); } this.setState({shouldShowDefaultDeleteMenu: false}); diff --git a/src/pages/settings/Payments/PaymentsPage/index.js b/src/pages/settings/Payments/PaymentsPage/index.js index 9a53fb9b8e1f..8b819cd7edf6 100644 --- a/src/pages/settings/Payments/PaymentsPage/index.js +++ b/src/pages/settings/Payments/PaymentsPage/index.js @@ -2,7 +2,7 @@ import React from 'react'; import BasePaymentsPage from './BasePaymentsPage'; const PaymentsPage = () => ( - + ); PaymentsPage.displayName = 'PaymentsPage'; diff --git a/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js b/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js index fd83f5ade9ff..3c3d6a2d8d11 100644 --- a/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js +++ b/src/pages/settings/Payments/PaymentsPage/paymentsPagePropTypes.js @@ -14,7 +14,7 @@ const propTypes = { isLoadingPaymentMethods: PropTypes.bool, /** Listen for window resize event on web and desktop. */ - listenResize: PropTypes.bool, + shouldListenForResize: PropTypes.bool, ...withLocalizePropTypes, @@ -27,7 +27,7 @@ const defaultProps = { }, betas: [], isLoadingPaymentMethods: true, - listenResize: false, + shouldListenForResize: false, }; export {propTypes, defaultProps};