-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Add payment dynamic #7566
Fix: Add payment dynamic #7566
Changes from 4 commits
056ae84
bf98fb6
789c3ec
9de73c1
2298ebc
62f1a74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,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'; | ||
|
||
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', | ||
}; | ||
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 | ||
|
@@ -57,6 +34,9 @@ class KYCWall extends React.Component { | |
} | ||
|
||
componentWillUnmount() { | ||
if (this.props.listenResize) { | ||
window.removeEventListener('resize', null); | ||
} | ||
PaymentMethods.kycWallRef.current = null; | ||
} | ||
|
||
|
@@ -78,6 +58,19 @@ class KYCWall extends React.Component { | |
}; | ||
} | ||
|
||
/** | ||
* 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. | ||
|
@@ -89,13 +82,19 @@ class KYCWall extends React.Component { | |
// 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); | ||
let clickedElementLocation = getClickedElementLocation(event.nativeEvent); | ||
let position = this.getAnchorPosition(clickedElementLocation); | ||
if (this.props.listenResize) { | ||
window.addEventListener('resize', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, hmm, this is gonna bind a new event listener each time. That can have unintended consequences. The best practice would be to set an event listener once on component init and then clean it up on unmount... Here's an example of what that looks like using a class component https://reactjs.org/docs/hooks-effect.html#example-using-classes-1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, we already added a remove listener when the modal close. The scenario is add listener when the modal appears and remove listener when the modal closes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please fix this. Here are the instructions...
Generally speaking even if we are focusing on the web side for consistency we should use the https://reactnative.dev/docs/0.66/dimensions#addeventlistener There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @phivh right now, we're adding a event listener each time the window is resized. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, let me take into this one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marcaaron @deetergp @rushatgabhane created follow-up PR for this one. Please take a look when you guys have a chance. |
||
clickedElementLocation = getClickedElementLocation(event.nativeEvent); | ||
position = this.getAnchorPosition(clickedElementLocation); | ||
this.setPositionAddPaymentMenu(position); | ||
}); | ||
} | ||
this.setState({ | ||
shouldShowAddPaymentMenu: true, | ||
anchorPositionTop, | ||
anchorPositionLeft, | ||
}); | ||
this.setPositionAddPaymentMenu(position); | ||
return; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import {propTypes, defaultProps} from './kycWallPropTypes'; | ||
import BaseKYCWall from './BaseKYCWall'; | ||
|
||
const KYCWall = props => ( | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
<BaseKYCWall {...props} listenResize /> | ||
); | ||
|
||
KYCWall.propTypes = propTypes; | ||
KYCWall.defaultProps = defaultProps; | ||
KYCWall.displayName = 'KYCWall'; | ||
|
||
export default KYCWall; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New line above |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import BaseKYCWall from './BaseKYCWall'; | ||
|
||
export default BaseKYCWall; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
|
||
/** Listen for window resize event on web and desktop */ | ||
listenResize: PropTypes.bool, | ||
|
||
...userWalletPropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
// eslint-disable-next-line react/default-props-match-prop-types | ||
userWallet: {}, | ||
popoverPlacement: 'top', | ||
listenResize: false, | ||
}; | ||
|
||
export {propTypes, defaultProps}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,35 @@ | ||
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 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'; | ||
|
||
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 { | ||
class BasePaymentsPage extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
|
@@ -94,6 +70,20 @@ class PaymentsPage extends React.Component { | |
} | ||
} | ||
|
||
/** | ||
* 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 | ||
* | ||
|
@@ -102,7 +92,13 @@ class PaymentsPage extends React.Component { | |
* @param {String} account | ||
*/ | ||
paymentMethodPressed(nativeEvent, accountType, account) { | ||
const position = getClickedElementLocation(nativeEvent); | ||
let position = getClickedElementLocation(nativeEvent); | ||
if (this.props.listenResize) { | ||
window.addEventListener('resize', () => { | ||
position = getClickedElementLocation(nativeEvent); | ||
this.setPositionAddPaymentMenu(position); | ||
}); | ||
} | ||
if (accountType) { | ||
let formattedSelectedPaymentMethod; | ||
if (accountType === CONST.PAYMENT_METHODS.PAYPAL) { | ||
|
@@ -131,21 +127,15 @@ class PaymentsPage extends React.Component { | |
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, | ||
}); | ||
this.setPositionAddPaymentMenu(position); | ||
return; | ||
} | ||
this.setState({ | ||
shouldShowAddPaymentMenu: true, | ||
}); | ||
this.setPositionAddPaymentMenu(position); | ||
} | ||
|
||
/** | ||
|
@@ -178,13 +168,19 @@ class PaymentsPage extends React.Component { | |
* Hide the add payment modal | ||
*/ | ||
hideAddPaymentMenu() { | ||
if (this.props.listenResize) { | ||
window.removeEventListener('resize', null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove eventListener in |
||
} | ||
this.setState({shouldShowAddPaymentMenu: false}); | ||
} | ||
|
||
/** | ||
* Hide the default / delete modal | ||
*/ | ||
hideDefaultDeleteMenu() { | ||
if (this.props.listenResize) { | ||
window.removeEventListener('resize', null); | ||
} | ||
this.setState({shouldShowDefaultDeleteMenu: false}); | ||
} | ||
|
||
|
@@ -390,8 +386,8 @@ class PaymentsPage extends React.Component { | |
} | ||
} | ||
|
||
PaymentsPage.propTypes = propTypes; | ||
PaymentsPage.defaultProps = defaultProps; | ||
BasePaymentsPage.propTypes = propTypes; | ||
BasePaymentsPage.defaultProps = defaultProps; | ||
|
||
export default compose( | ||
withWindowDimensions, | ||
|
@@ -408,4 +404,4 @@ export default compose( | |
initWithStoredValues: false, | ||
}, | ||
}), | ||
)(PaymentsPage); | ||
)(BasePaymentsPage); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React from 'react'; | ||
import BasePaymentsPage from './BasePaymentsPage'; | ||
|
||
const PaymentsPage = () => ( | ||
<BasePaymentsPage listenResize /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not going to leave a full review here, but please see this draft PR which explains proper usage of boolean props to enable or disable features... @rushatgabhane please 👀 and thank you 🙇 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And applied this would be changed to something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Let's change it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I agree! |
||
); | ||
|
||
PaymentsPage.displayName = 'PaymentsPage'; | ||
|
||
export default PaymentsPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import BasePaymentsPage from './BasePaymentsPage'; | ||
|
||
export default BasePaymentsPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
|
||
/** Listen for window resize event on web and desktop. */ | ||
listenResize: PropTypes.bool, | ||
|
||
...withLocalizePropTypes, | ||
|
||
...windowDimensionsPropTypes, | ||
}; | ||
|
||
const defaultProps = { | ||
walletTransfer: { | ||
shouldShowConfirmModal: false, | ||
}, | ||
betas: [], | ||
isLoadingPaymentMethods: true, | ||
listenResize: false, | ||
}; | ||
|
||
export {propTypes, defaultProps}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove line break