From 5b6e0876cbd205061865ebd1d81d13af439c598e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Sosna?= <43684335+SosenWiosen@users.noreply.github.com> Date: Thu, 10 Aug 2023 15:24:40 +0200 Subject: [PATCH 1/3] setup most of the logic --- src/components/AddPlaidBankAccount.js | 244 +++++++++++++------------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index ff97c9be24a6..b2c56d7e24e5 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React from 'react'; +import React, {useEffect, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -68,164 +68,164 @@ const defaultProps = { bankAccountID: 0, }; -class AddPlaidBankAccount extends React.Component { - constructor(props) { - super(props); - - this.getPlaidLinkToken = this.getPlaidLinkToken.bind(this); - this.subscribedKeyboardShortcuts = []; - } - - componentDidMount() { - this.subscribeToNavigationShortcuts(); - - // If we're coming from Plaid OAuth flow then we need to reuse the existing plaidLinkToken - if (this.isAuthenticatedWithPlaid()) { - return; - } - - BankAccounts.openPlaidBankLogin(this.props.allowDebit, this.props.bankAccountID); - } - - componentDidUpdate(prevProps) { - if (!prevProps.network.isOffline || this.props.network.isOffline || this.isAuthenticatedWithPlaid()) { - return; - } - - // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid - BankAccounts.openPlaidBankLogin(this.props.allowDebit, this.props.bankAccountID); - } - - componentWillUnmount() { - this.unsubscribeToNavigationShortcuts(); - } +function AddPlaidBankAccount({ + plaidData, + selectedPlaidAccountID, + plaidLinkToken, + onExitPlaid, + onSelect, + text, + receivedRedirectURI, + plaidLinkOAuthToken, + bankAccountID, + allowDebit, + translate, + network, +}) { + const [subscribedKeyboardShortcuts, setSubscribedKeyboardShortcuts] = useState([]); /** * @returns {String} */ - getPlaidLinkToken() { - if (this.props.plaidLinkToken) { - return this.props.plaidLinkToken; + const getPlaidLinkToken = () => { + if (plaidLinkToken) { + return plaidLinkToken; } - if (this.props.receivedRedirectURI && this.props.plaidLinkOAuthToken) { - return this.props.plaidLinkOAuthToken; + if (receivedRedirectURI && plaidLinkOAuthToken) { + return plaidLinkOAuthToken; } - } + }; /** * @returns {Boolean} */ - isAuthenticatedWithPlaid() { - return ( - (this.props.receivedRedirectURI && this.props.plaidLinkOAuthToken) || - !_.isEmpty(lodashGet(this.props.plaidData, 'bankAccounts')) || - !_.isEmpty(lodashGet(this.props.plaidData, 'errors')) - ); - } + const isAuthenticatedWithPlaid = () => (receivedRedirectURI && plaidLinkOAuthToken) || !_.isEmpty(lodashGet(plaidData, 'bankAccounts')) || !_.isEmpty(lodashGet(plaidData, 'errors')); /** * Blocks the keyboard shortcuts that can navigate */ - subscribeToNavigationShortcuts() { + const subscribeToNavigationShortcuts = () => { // find and block the shortcuts const shortcutsToBlock = _.filter(CONST.KEYBOARD_SHORTCUTS, (x) => x.type === CONST.KEYBOARD_SHORTCUTS_TYPES.NAVIGATION_SHORTCUT); - this.subscribedKeyboardShortcuts = _.map(shortcutsToBlock, (shortcut) => - KeyboardShortcut.subscribe( - shortcut.shortcutKey, - () => {}, // do nothing - shortcut.descriptionKey, - shortcut.modifiers, - false, - () => lodashGet(this.props.plaidData, 'bankAccounts', []).length > 0, // start bubbling when there are bank accounts + setSubscribedKeyboardShortcuts( + _.map(shortcutsToBlock, (shortcut) => + KeyboardShortcut.subscribe( + shortcut.shortcutKey, + () => {}, // do nothing + shortcut.descriptionKey, + shortcut.modifiers, + false, + () => lodashGet(plaidData, 'bankAccounts', []).length > 0, // start bubbling when there are bank accounts + ), ), ); - } + }; /** * Unblocks the keyboard shortcuts that can navigate */ - unsubscribeToNavigationShortcuts() { - _.each(this.subscribedKeyboardShortcuts, (unsubscribe) => unsubscribe()); - this.subscribedKeyboardShortcuts = []; - } + const unsubscribeToNavigationShortcuts = () => { + _.each(subscribedKeyboardShortcuts, (unsubscribe) => unsubscribe()); + setSubscribedKeyboardShortcuts([]); + }; - render() { - const plaidBankAccounts = lodashGet(this.props.plaidData, 'bankAccounts') || []; - const token = this.getPlaidLinkToken(); - const options = _.map(plaidBankAccounts, (account) => ({ - value: account.plaidAccountID, - label: `${account.addressName} ${account.mask}`, - })); - const {icon, iconSize} = getBankIcon(); - const plaidErrors = lodashGet(this.props.plaidData, 'errors'); - const plaidDataErrorMessage = !_.isEmpty(plaidErrors) ? _.chain(plaidErrors).values().first().value() : ''; - const bankName = lodashGet(this.props.plaidData, 'bankName'); - - // Plaid Link view - if (!plaidBankAccounts.length) { - return ( - - {lodashGet(this.props.plaidData, 'isLoading') && ( - - - - )} - {Boolean(plaidDataErrorMessage) && {plaidDataErrorMessage}} - {Boolean(token) && !bankName && ( - { - Log.info('[PlaidLink] Success!'); - BankAccounts.openPlaidBankAccountSelector(publicToken, metadata.institution.name, this.props.allowDebit, this.props.bankAccountID); - }} - onError={(error) => { - Log.hmmm('[PlaidLink] Error: ', error.message); - }} - // User prematurely exited the Plaid flow - // eslint-disable-next-line react/jsx-props-no-multi-spaces - onExit={this.props.onExitPlaid} - receivedRedirectURI={this.props.receivedRedirectURI} - /> - )} - - ); + useEffect(() => { + subscribeToNavigationShortcuts(); + + // If we're coming from Plaid OAuth flow then we need to reuse the existing plaidLinkToken + if (isAuthenticatedWithPlaid()) { + return; } - // Plaid bank accounts view + BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); + return unsubscribeToNavigationShortcuts; + }); + + useEffect(() => { + if (!prevProps.network.isOffline || network.isOffline || isAuthenticatedWithPlaid()) { + return; + } + + // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid + BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); + }); + + const plaidBankAccounts = lodashGet(plaidData, 'bankAccounts') || []; + const token = getPlaidLinkToken(); + const options = _.map(plaidBankAccounts, (account) => ({ + value: account.plaidAccountID, + label: `${account.addressName} ${account.mask}`, + })); + const {icon, iconSize} = getBankIcon(); + const plaidErrors = lodashGet(plaidData, 'errors'); + const plaidDataErrorMessage = !_.isEmpty(plaidErrors) ? _.chain(plaidErrors).values().first().value() : ''; + const bankName = lodashGet(plaidData, 'bankName'); + + // Plaid Link view + if (!plaidBankAccounts.length) { return ( - {!_.isEmpty(this.props.text) && {this.props.text}} - - - {bankName} - - - + + + )} + {Boolean(plaidDataErrorMessage) && {plaidDataErrorMessage}} + {Boolean(token) && !bankName && ( + { + Log.info('[PlaidLink] Success!'); + BankAccounts.openPlaidBankAccountSelector(publicToken, metadata.institution.name, allowDebit, bankAccountID); }} - value={this.props.selectedPlaidAccountID} + onError={(error) => { + Log.hmmm('[PlaidLink] Error: ', error.message); + }} + // User prematurely exited the Plaid flow + // eslint-disable-next-line react/jsx-props-no-multi-spaces + onExit={onExitPlaid} + receivedRedirectURI={receivedRedirectURI} /> - + )} ); } + + // Plaid bank accounts view + return ( + + {!_.isEmpty(text) && {text}} + + + {bankName} + + + + + + ); } AddPlaidBankAccount.propTypes = propTypes; AddPlaidBankAccount.defaultProps = defaultProps; +AddPlaidBankAccount.displayName = 'AddPlaidBankAccount'; export default compose( withLocalize, From d3ea842a9271329ed61db3ecc7bf107a70d228b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Sosna?= <43684335+SosenWiosen@users.noreply.github.com> Date: Fri, 11 Aug 2023 11:17:06 +0200 Subject: [PATCH 2/3] finished migrated logic --- src/components/AddPlaidBankAccount.js | 93 ++++++++++++--------------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index b2c56d7e24e5..f95f670165e4 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -1,5 +1,5 @@ import _ from 'underscore'; -import React, {useEffect, useState} from 'react'; +import React, {useEffect, useRef, useCallback} from 'react'; import {ActivityIndicator, View} from 'react-native'; import PropTypes from 'prop-types'; import {withOnyx} from 'react-native-onyx'; @@ -10,17 +10,16 @@ import * as BankAccounts from '../libs/actions/BankAccounts'; import ONYXKEYS from '../ONYXKEYS'; import styles from '../styles/styles'; import themeColors from '../styles/themes/default'; -import compose from '../libs/compose'; -import withLocalize, {withLocalizePropTypes} from './withLocalize'; import Picker from './Picker'; import {plaidDataPropTypes} from '../pages/ReimbursementAccount/plaidDataPropTypes'; import Text from './Text'; import getBankIcon from './Icon/BankIcons'; import Icon from './Icon'; import FullPageOfflineBlockingView from './BlockingViews/FullPageOfflineBlockingView'; -import {withNetwork} from './OnyxProvider'; import CONST from '../CONST'; import KeyboardShortcut from '../libs/KeyboardShortcut'; +import useLocalize from '../hooks/useLocalize'; +import useNetwork from '../hooks/useNetwork'; const propTypes = { /** Contains plaid data */ @@ -52,8 +51,6 @@ const propTypes = { /** Are we adding a withdrawal account? */ allowDebit: PropTypes.bool, - - ...withLocalizePropTypes, }; const defaultProps = { @@ -68,21 +65,12 @@ const defaultProps = { bankAccountID: 0, }; -function AddPlaidBankAccount({ - plaidData, - selectedPlaidAccountID, - plaidLinkToken, - onExitPlaid, - onSelect, - text, - receivedRedirectURI, - plaidLinkOAuthToken, - bankAccountID, - allowDebit, - translate, - network, -}) { - const [subscribedKeyboardShortcuts, setSubscribedKeyboardShortcuts] = useState([]); +function AddPlaidBankAccount({plaidData, selectedPlaidAccountID, plaidLinkToken, onExitPlaid, onSelect, text, receivedRedirectURI, plaidLinkOAuthToken, bankAccountID, allowDebit}) { + const subscribedKeyboardShortcuts = useRef([]); + const previousNetworkState = useRef(); + + const {translate} = useLocalize(); + const {isOffline} = useNetwork(); /** * @returns {String} @@ -99,8 +87,12 @@ function AddPlaidBankAccount({ /** * @returns {Boolean} + * I'm using useCallback so the useEffect which uses this function doesn't run on every render. */ - const isAuthenticatedWithPlaid = () => (receivedRedirectURI && plaidLinkOAuthToken) || !_.isEmpty(lodashGet(plaidData, 'bankAccounts')) || !_.isEmpty(lodashGet(plaidData, 'errors')); + const isAuthenticatedWithPlaid = useCallback( + () => (receivedRedirectURI && plaidLinkOAuthToken) || !_.isEmpty(lodashGet(plaidData, 'bankAccounts')) || !_.isEmpty(lodashGet(plaidData, 'errors')), + [plaidData, plaidLinkOAuthToken, receivedRedirectURI], + ); /** * Blocks the keyboard shortcuts that can navigate @@ -108,16 +100,14 @@ function AddPlaidBankAccount({ const subscribeToNavigationShortcuts = () => { // find and block the shortcuts const shortcutsToBlock = _.filter(CONST.KEYBOARD_SHORTCUTS, (x) => x.type === CONST.KEYBOARD_SHORTCUTS_TYPES.NAVIGATION_SHORTCUT); - setSubscribedKeyboardShortcuts( - _.map(shortcutsToBlock, (shortcut) => - KeyboardShortcut.subscribe( - shortcut.shortcutKey, - () => {}, // do nothing - shortcut.descriptionKey, - shortcut.modifiers, - false, - () => lodashGet(plaidData, 'bankAccounts', []).length > 0, // start bubbling when there are bank accounts - ), + subscribedKeyboardShortcuts.current = _.map(shortcutsToBlock, (shortcut) => + KeyboardShortcut.subscribe( + shortcut.shortcutKey, + () => {}, // do nothing + shortcut.descriptionKey, + shortcut.modifiers, + false, + () => lodashGet(plaidData, 'bankAccounts', []).length > 0, // start bubbling when there are bank accounts ), ); }; @@ -127,29 +117,30 @@ function AddPlaidBankAccount({ */ const unsubscribeToNavigationShortcuts = () => { _.each(subscribedKeyboardShortcuts, (unsubscribe) => unsubscribe()); - setSubscribedKeyboardShortcuts([]); + subscribedKeyboardShortcuts.current = []; }; useEffect(() => { subscribeToNavigationShortcuts(); // If we're coming from Plaid OAuth flow then we need to reuse the existing plaidLinkToken - if (isAuthenticatedWithPlaid()) { - return; + if (!isAuthenticatedWithPlaid()) { + BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); } - BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); return unsubscribeToNavigationShortcuts; - }); + // disabling this rule, as we want this to run only on the first render + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); useEffect(() => { - if (!prevProps.network.isOffline || network.isOffline || isAuthenticatedWithPlaid()) { - return; + // previousNetworkState.current also makes sure that this doesn't run on the first render, as null is falsy. + if (previousNetworkState.current && !isOffline && !isAuthenticatedWithPlaid()) { + // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid + BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); } - - // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid - BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); - }); + previousNetworkState.current = isOffline; + }, [allowDebit, bankAccountID, isAuthenticatedWithPlaid, isOffline]); const plaidBankAccounts = lodashGet(plaidData, 'bankAccounts') || []; const token = getPlaidLinkToken(); @@ -227,13 +218,9 @@ AddPlaidBankAccount.propTypes = propTypes; AddPlaidBankAccount.defaultProps = defaultProps; AddPlaidBankAccount.displayName = 'AddPlaidBankAccount'; -export default compose( - withLocalize, - withNetwork(), - withOnyx({ - plaidLinkToken: { - key: ONYXKEYS.PLAID_LINK_TOKEN, - initWithStoredValues: false, - }, - }), -)(AddPlaidBankAccount); +export default withOnyx({ + plaidLinkToken: { + key: ONYXKEYS.PLAID_LINK_TOKEN, + initWithStoredValues: false, + }, +})(AddPlaidBankAccount); From 3d4897959189895c91c7751ebece76213617b290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Sosna?= <43684335+SosenWiosen@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:44:26 +0200 Subject: [PATCH 3/3] fix small errors --- src/components/AddPlaidBankAccount.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index f95f670165e4..d50fad0bd2f0 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -116,7 +116,7 @@ function AddPlaidBankAccount({plaidData, selectedPlaidAccountID, plaidLinkToken, * Unblocks the keyboard shortcuts that can navigate */ const unsubscribeToNavigationShortcuts = () => { - _.each(subscribedKeyboardShortcuts, (unsubscribe) => unsubscribe()); + _.each(subscribedKeyboardShortcuts.current, (unsubscribe) => unsubscribe()); subscribedKeyboardShortcuts.current = []; }; @@ -124,19 +124,20 @@ function AddPlaidBankAccount({plaidData, selectedPlaidAccountID, plaidLinkToken, subscribeToNavigationShortcuts(); // If we're coming from Plaid OAuth flow then we need to reuse the existing plaidLinkToken - if (!isAuthenticatedWithPlaid()) { - BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); + if (isAuthenticatedWithPlaid()) { + return unsubscribeToNavigationShortcuts; } - + BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); return unsubscribeToNavigationShortcuts; + // disabling this rule, as we want this to run only on the first render // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { - // previousNetworkState.current also makes sure that this doesn't run on the first render, as null is falsy. + // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid + // previousNetworkState.current also makes sure that this doesn't run on the first render. if (previousNetworkState.current && !isOffline && !isAuthenticatedWithPlaid()) { - // If we are coming back from offline and we haven't authenticated with Plaid yet, we need to re-run our call to kick off Plaid BankAccounts.openPlaidBankLogin(allowDebit, bankAccountID); } previousNetworkState.current = isOffline;