Skip to content

Commit

Permalink
Merge pull request #20194 from Expensify/francois-transferBalanceFunc…
Browse files Browse the repository at this point in the history
…tionalComponent

Refactor `TransferBalancePage` into a functional component
  • Loading branch information
marcaaron authored Jun 27, 2023
2 parents e029bb1 + 657ec67 commit 41eb9ba
Showing 1 changed file with 138 additions and 141 deletions.
279 changes: 138 additions & 141 deletions src/pages/settings/Payments/TransferBalancePage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _ from 'underscore';
import React from 'react';
import React, {useEffect} from 'react';
import {View, ScrollView} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
Expand Down Expand Up @@ -65,65 +65,50 @@ const defaultProps = {
walletTransfer: {},
};

class TransferBalancePage extends React.Component {
constructor(props) {
super(props);

this.paymentTypes = [
{
key: CONST.WALLET.TRANSFER_METHOD_TYPE.INSTANT,
title: this.props.translate('transferAmountPage.instant'),
description: this.props.translate('transferAmountPage.instantSummary', {
rate: this.props.numberFormat(CONST.WALLET.TRANSFER_METHOD_TYPE_FEE.INSTANT.RATE),
minAmount: CurrencyUtils.convertToDisplayString(CONST.WALLET.TRANSFER_METHOD_TYPE_FEE.INSTANT.MINIMUM_FEE),
}),
icon: Expensicons.Bolt,
type: CONST.PAYMENT_METHODS.DEBIT_CARD,
},
{
key: CONST.WALLET.TRANSFER_METHOD_TYPE.ACH,
title: this.props.translate('transferAmountPage.ach'),
description: this.props.translate('transferAmountPage.achSummary'),
icon: Expensicons.Bank,
type: CONST.PAYMENT_METHODS.BANK_ACCOUNT,
},
];

PaymentMethods.resetWalletTransferData();
const selectedAccount = this.getSelectedPaymentMethodAccount();

// Select the default payment account when page is opened,
// so that user can see that preselected on choose transfer account page
if (!selectedAccount || !selectedAccount.isDefault) {
return;
}

PaymentMethods.saveWalletTransferAccountTypeAndID(selectedAccount.accountType, selectedAccount.methodID);
}
function TransferBalancePage(props) {
const paymentTypes = [
{
key: CONST.WALLET.TRANSFER_METHOD_TYPE.INSTANT,
title: props.translate('transferAmountPage.instant'),
description: props.translate('transferAmountPage.instantSummary', {
rate: props.numberFormat(CONST.WALLET.TRANSFER_METHOD_TYPE_FEE.INSTANT.RATE),
minAmount: CurrencyUtils.convertToDisplayString(CONST.WALLET.TRANSFER_METHOD_TYPE_FEE.INSTANT.MINIMUM_FEE),
}),
icon: Expensicons.Bolt,
type: CONST.PAYMENT_METHODS.DEBIT_CARD,
},
{
key: CONST.WALLET.TRANSFER_METHOD_TYPE.ACH,
title: props.translate('transferAmountPage.ach'),
description: props.translate('transferAmountPage.achSummary'),
icon: Expensicons.Bank,
type: CONST.PAYMENT_METHODS.BANK_ACCOUNT,
},
];

/**
* Get the selected/default payment method account for wallet transfer
* @returns {Object|undefined}
*/
getSelectedPaymentMethodAccount() {
const paymentMethods = PaymentUtils.formatPaymentMethods(this.props.bankAccountList, this.props.cardList);
function getSelectedPaymentMethodAccount() {
const paymentMethods = PaymentUtils.formatPaymentMethods(props.bankAccountList, props.cardList);

const defaultAccount = _.find(paymentMethods, (method) => method.isDefault);
const selectedAccount = _.find(
paymentMethods,
(method) => method.accountType === this.props.walletTransfer.selectedAccountType && method.methodID === this.props.walletTransfer.selectedAccountID,
(method) => method.accountType === props.walletTransfer.selectedAccountType && method.methodID === props.walletTransfer.selectedAccountID,
);
return selectedAccount || defaultAccount;
}

/**
* @param {String} filterPaymentMethodType
*/
navigateToChooseTransferAccount(filterPaymentMethodType) {
function navigateToChooseTransferAccount(filterPaymentMethodType) {
PaymentMethods.saveWalletTransferMethodType(filterPaymentMethodType);

// If we only have a single option for the given paymentMethodType do not force the user to make a selection
const combinedPaymentMethods = PaymentUtils.formatPaymentMethods(this.props.bankAccountList, this.props.cardList);
const combinedPaymentMethods = PaymentUtils.formatPaymentMethods(props.bankAccountList, props.cardList);

const filteredMethods = _.filter(combinedPaymentMethods, (paymentMethod) => paymentMethod.accountType === filterPaymentMethodType);
if (filteredMethods.length === 1) {
Expand All @@ -135,120 +120,132 @@ class TransferBalancePage extends React.Component {
Navigation.navigate(ROUTES.SETTINGS_PAYMENTS_CHOOSE_TRANSFER_ACCOUNT);
}

render() {
if (this.props.walletTransfer.shouldShowSuccess && !this.props.walletTransfer.loading) {
return (
<ScreenWrapper>
<HeaderWithBackButton
title={this.props.translate('common.transferBalance')}
onBackButtonPress={PaymentMethods.dismissSuccessfulTransferBalancePage}
/>
<ConfirmationPage
heading={this.props.translate('transferAmountPage.transferSuccess')}
description={
this.props.walletTransfer.paymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT
? this.props.translate('transferAmountPage.transferDetailBankAccount')
: this.props.translate('transferAmountPage.transferDetailDebitCard')
}
shouldShowButton
buttonText={this.props.translate('common.done')}
onButtonPress={PaymentMethods.dismissSuccessfulTransferBalancePage}
/>
</ScreenWrapper>
);
}
const selectedAccount = this.getSelectedPaymentMethodAccount();
const selectedPaymentType =
selectedAccount && selectedAccount.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? CONST.WALLET.TRANSFER_METHOD_TYPE.ACH : CONST.WALLET.TRANSFER_METHOD_TYPE.INSTANT;
useEffect(() => {
// Reset to the default account when the page is opened
PaymentMethods.resetWalletTransferData();

const calculatedFee = PaymentUtils.calculateWalletTransferBalanceFee(this.props.userWallet.currentBalance, selectedPaymentType);
const transferAmount = this.props.userWallet.currentBalance - calculatedFee;
const isTransferable = transferAmount > 0;
const isButtonDisabled = !isTransferable || !selectedAccount;
const errorMessage = !_.isEmpty(this.props.walletTransfer.errors) ? _.chain(this.props.walletTransfer.errors).values().first().value() : '';
const selectedAccount = getSelectedPaymentMethodAccount();
if (!selectedAccount) {
return;
}

const shouldShowTransferView =
PaymentUtils.hasExpensifyPaymentMethod(this.props.cardList, this.props.bankAccountList) && this.props.userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD;
PaymentMethods.saveWalletTransferAccountTypeAndID(selectedAccount.accountType, selectedAccount.methodID);
// eslint-disable-next-line react-hooks/exhaustive-deps -- we only want this effect to run on initial render
}, []);

if (props.walletTransfer.shouldShowSuccess && !props.walletTransfer.loading) {
return (
<ScreenWrapper>
<FullPageNotFoundView
shouldShow={!shouldShowTransferView}
titleKey="notFound.pageNotFound"
subtitleKey="transferAmountPage.notHereSubTitle"
shouldShowLink
linkKey="transferAmountPage.goToPayment"
onLinkPress={() => Navigation.goBack(ROUTES.SETTINGS_PAYMENTS)}
<HeaderWithBackButton
title={props.translate('common.transferBalance')}
onBackButtonPress={PaymentMethods.dismissSuccessfulTransferBalancePage}
/>
<ConfirmationPage
heading={props.translate('transferAmountPage.transferSuccess')}
description={
props.walletTransfer.paymentMethodType === CONST.PAYMENT_METHODS.BANK_ACCOUNT
? props.translate('transferAmountPage.transferDetailBankAccount')
: props.translate('transferAmountPage.transferDetailDebitCard')
}
shouldShowButton
buttonText={props.translate('common.done')}
onButtonPress={PaymentMethods.dismissSuccessfulTransferBalancePage}
/>
</ScreenWrapper>
);
}

const selectedAccount = getSelectedPaymentMethodAccount();
const selectedPaymentType =
selectedAccount && selectedAccount.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? CONST.WALLET.TRANSFER_METHOD_TYPE.ACH : CONST.WALLET.TRANSFER_METHOD_TYPE.INSTANT;

const calculatedFee = PaymentUtils.calculateWalletTransferBalanceFee(props.userWallet.currentBalance, selectedPaymentType);
const transferAmount = props.userWallet.currentBalance - calculatedFee;
const isTransferable = transferAmount > 0;
const isButtonDisabled = !isTransferable || !selectedAccount;
const errorMessage = !_.isEmpty(props.walletTransfer.errors) ? _.chain(props.walletTransfer.errors).values().first().value() : '';

const shouldShowTransferView = PaymentUtils.hasExpensifyPaymentMethod(props.cardList, props.bankAccountList) && props.userWallet.tierName === CONST.WALLET.TIER_NAME.GOLD;

return (
<ScreenWrapper>
<FullPageNotFoundView
shouldShow={!shouldShowTransferView}
titleKey="notFound.pageNotFound"
subtitleKey="transferAmountPage.notHereSubTitle"
shouldShowLink
linkKey="transferAmountPage.goToPayment"
onLinkPress={() => Navigation.goBack(ROUTES.SETTINGS_PAYMENTS)}
>
<HeaderWithBackButton
title={props.translate('common.transferBalance')}
shouldShowBackButton
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_PAYMENTS)}
/>
<View style={[styles.flexGrow1, styles.flexShrink1, styles.flexBasisAuto, styles.justifyContentCenter]}>
<CurrentWalletBalance balanceStyles={[styles.transferBalanceBalance]} />
</View>
<ScrollView
style={styles.flexGrow0}
contentContainerStyle={styles.pv5}
>
<HeaderWithBackButton
title={this.props.translate('common.transferBalance')}
shouldShowBackButton
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_PAYMENTS)}
/>
<View style={[styles.flexGrow1, styles.flexShrink1, styles.flexBasisAuto, styles.justifyContentCenter]}>
<CurrentWalletBalance balanceStyles={[styles.transferBalanceBalance]} />
</View>
<ScrollView
style={styles.flexGrow0}
contentContainerStyle={styles.pv5}
>
<View style={styles.ph5}>
{_.map(this.paymentTypes, (paymentType) => (
<MenuItem
key={paymentType.key}
title={paymentType.title}
description={paymentType.description}
iconWidth={variables.iconSizeXLarge}
iconHeight={variables.iconSizeXLarge}
icon={paymentType.icon}
success={selectedPaymentType === paymentType.key}
wrapperStyle={{
...styles.mt3,
...styles.pv4,
...styles.transferBalancePayment,
...(selectedPaymentType === paymentType.key && styles.transferBalanceSelectedPayment),
}}
onPress={() => this.navigateToChooseTransferAccount(paymentType.type)}
/>
))}
</View>
<Text style={[styles.p5, styles.textLabelSupporting, styles.justifyContentStart]}>{this.props.translate('transferAmountPage.whichAccount')}</Text>
{Boolean(selectedAccount) && (
<View style={styles.ph5}>
{_.map(paymentTypes, (paymentType) => (
<MenuItem
title={selectedAccount.title}
description={selectedAccount.description}
shouldShowRightIcon
iconWidth={selectedAccount.iconSize}
iconHeight={selectedAccount.iconSize}
icon={selectedAccount.icon}
onPress={() => this.navigateToChooseTransferAccount(selectedAccount.accountType)}
key={paymentType.key}
title={paymentType.title}
description={paymentType.description}
iconWidth={variables.iconSizeXLarge}
iconHeight={variables.iconSizeXLarge}
icon={paymentType.icon}
success={selectedPaymentType === paymentType.key}
wrapperStyle={{
...styles.mt3,
...styles.pv4,
...styles.transferBalancePayment,
...(selectedPaymentType === paymentType.key && styles.transferBalanceSelectedPayment),
}}
onPress={() => navigateToChooseTransferAccount(paymentType.type)}
/>
)}
<View style={styles.ph5}>
<Text style={[styles.mt5, styles.mb3, styles.textLabelSupporting, styles.justifyContentStart]}>{this.props.translate('transferAmountPage.fee')}</Text>
<Text style={[styles.justifyContentStart]}>{CurrencyUtils.convertToDisplayString(calculatedFee)}</Text>
</View>
</ScrollView>
<View>
<FormAlertWithSubmitButton
buttonText={this.props.translate('transferAmountPage.transfer', {
amount: isTransferable ? CurrencyUtils.convertToDisplayString(transferAmount) : '',
})}
isLoading={this.props.walletTransfer.loading}
onSubmit={() => PaymentMethods.transferWalletBalance(selectedAccount)}
isDisabled={isButtonDisabled || this.props.network.isOffline}
message={errorMessage}
isAlertVisible={!_.isEmpty(errorMessage)}
))}
</View>
<Text style={[styles.p5, styles.textLabelSupporting, styles.justifyContentStart]}>{props.translate('transferAmountPage.whichAccount')}</Text>
{Boolean(selectedAccount) && (
<MenuItem
title={selectedAccount.title}
description={selectedAccount.description}
shouldShowRightIcon
iconWidth={selectedAccount.iconSize}
iconHeight={selectedAccount.iconSize}
icon={selectedAccount.icon}
onPress={() => navigateToChooseTransferAccount(selectedAccount.accountType)}
/>
)}
<View style={styles.ph5}>
<Text style={[styles.mt5, styles.mb3, styles.textLabelSupporting, styles.justifyContentStart]}>{props.translate('transferAmountPage.fee')}</Text>
<Text style={[styles.justifyContentStart]}>{CurrencyUtils.convertToDisplayString(calculatedFee)}</Text>
</View>
</FullPageNotFoundView>
</ScreenWrapper>
);
}
</ScrollView>
<View>
<FormAlertWithSubmitButton
buttonText={props.translate('transferAmountPage.transfer', {
amount: isTransferable ? CurrencyUtils.convertToDisplayString(transferAmount) : '',
})}
isLoading={props.walletTransfer.loading}
onSubmit={() => PaymentMethods.transferWalletBalance(selectedAccount)}
isDisabled={isButtonDisabled || props.network.isOffline}
message={errorMessage}
isAlertVisible={!_.isEmpty(errorMessage)}
/>
</View>
</FullPageNotFoundView>
</ScreenWrapper>
);
}

TransferBalancePage.propTypes = propTypes;
TransferBalancePage.defaultProps = defaultProps;
TransferBalancePage.displayName = 'TransferBalancePage';

export default compose(
withLocalize,
Expand Down

0 comments on commit 41eb9ba

Please sign in to comment.