Skip to content
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

Add Red Brick Road Offline Handling to Payments List #10333

Merged
merged 29 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
86bc1eb
Wrap in OfflineWithFeedback
thienlnam Aug 9, 2022
9071225
add errors and pending action to Payment method
thienlnam Aug 10, 2022
9ded98c
add method to check for payment method error
thienlnam Aug 10, 2022
b1f98b4
add check for error
thienlnam Aug 10, 2022
7208f5f
add styles for spacing and make it an error dot
thienlnam Aug 10, 2022
77951e7
add styles for the error portion of OfflineWithFeedback
thienlnam Aug 10, 2022
20ebfea
add methods to clear error
thienlnam Aug 10, 2022
36c96d3
add functionality for clearing payment error method
thienlnam Aug 10, 2022
2debc84
linter fixes
thienlnam Aug 10, 2022
36508c3
Merge branch 'main' into jack-paymentMethodPatternB
thienlnam Aug 10, 2022
5d31ae4
address comments
thienlnam Aug 10, 2022
d3b0cde
add offline text styles
thienlnam Aug 10, 2022
411f3b1
Merge branch 'main' into jack-paymentMethodPatternB
thienlnam Aug 10, 2022
0819698
Merge branch 'main' into jack-paymentMethodPatternB
thienlnam Aug 10, 2022
78318cd
Merge branch 'jack-paymentMethodPatternB' of github.com:Expensify/App…
thienlnam Aug 10, 2022
2c391e4
merge updates
thienlnam Aug 10, 2022
830667c
Merge branch 'main' into jack-paymentMethodPatternB
thienlnam Aug 11, 2022
8c1644d
fix bad merge
thienlnam Aug 11, 2022
6a567d2
hopefully reverts package-lock.json changes
thienlnam Aug 11, 2022
8e346e2
remove spread
thienlnam Aug 11, 2022
4b78aa4
update handling so it works with object or array
thienlnam Aug 11, 2022
e3e227c
store package-lock.json changes
thienlnam Aug 11, 2022
82a819f
Merge branch 'main' into jack-paymentMethodPatternB
thienlnam Aug 12, 2022
03e912f
nab updates
thienlnam Aug 12, 2022
56a9537
Merge branch 'main' into jack-paymentMethodPatternB
thienlnam Aug 15, 2022
7d082b5
look for fundID instead of cardID
thienlnam Aug 15, 2022
deb9cc5
Merge branch 'main' into jack-paymentMethodPatternB
thienlnam Aug 15, 2022
b88dd59
merge conflict
thienlnam Aug 15, 2022
4757556
re-add badge
thienlnam Aug 15, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 90 additions & 90 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/MenuItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const MenuItem = props => (
</View>
</View>
<View style={[styles.flexRow, styles.menuItemTextContainer, styles.pointerEventsNone]}>
{props.badgeText && <Badge text={props.badgeText} badgeStyles={[styles.alignSelfCenter]} />}
{props.badgeText && <Badge text={props.badgeText} badgeStyles={[styles.alignSelfCenter, (props.brickRoadIndicator ? styles.mr2 : undefined)]} />}
{/* Since subtitle can be of type number, we should allow 0 to be shown */}
{(props.subtitle || props.subtitle === 0) && (
<View style={[styles.justifyContentCenter, styles.mr1]}>
Expand Down
11 changes: 9 additions & 2 deletions src/components/OfflineWithFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,25 @@ const propTypes = {
/** Information about the network */
network: networkPropTypes.isRequired,

/** Additional styles to add after local styles. Applied to Pressable portion of button */
/** Additional styles to add after local styles. Applied to the parent container */
style: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.object),
PropTypes.object,
]),

/** Additional styles to add after local styles. Applied to the error text portion */
errorStyle: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.object),
PropTypes.object,
]),
...withLocalizePropTypes,
};

const defaultProps = {
pendingAction: null,
errors: null,
style: [],
errorStyle: [],
};

/**
Expand Down Expand Up @@ -97,7 +104,7 @@ const OfflineWithFeedback = (props) => {
</View>
)}
{hasErrors && (
<View style={styles.offlineFeedback.error}>
<View style={[styles.offlineFeedback.error, props.errorStyle]}>
<View style={styles.offlineFeedback.errorDot}>
<Icon src={Expensicons.DotIndicator} fill={colors.red} height={variables.iconSizeSmall} width={variables.iconSizeSmall} />
</View>
Expand Down
2 changes: 2 additions & 0 deletions src/libs/PaymentUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ function formatPaymentMethods(bankAccountList, cardList, payPalMeUsername = '',
accountType: CONST.PAYMENT_METHODS.BANK_ACCOUNT,
accountData: _.extend({}, bankAccount, {icon}),
isDefault,
errors: bankAccount.errors || null,
pendingAction: bankAccount.pendingAction || null,
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
});
});

Expand Down
40 changes: 40 additions & 0 deletions src/libs/actions/PaymentMethods.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _ from 'underscore';
import {createRef} from 'react';
import lodashGet from 'lodash/get';
import lodashMerge from 'lodash/merge';
import Onyx from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';
import * as DeprecatedAPI from '../deprecatedAPI';
Expand Down Expand Up @@ -311,6 +312,42 @@ function dismissSuccessfulTransferBalancePage() {
Navigation.navigate(ROUTES.SETTINGS_PAYMENTS);
}

/**
* Looks through each payment method to see if there is an existing error
* @param {Object} bankList
* @param {Object} cardList
* @returns {Boolean}
*/
function hasPaymentMethodError(bankList, cardList) {
const combinedPaymentMethods = lodashMerge(bankList, cardList);
return _.some(combinedPaymentMethods, item => !_.isEmpty(item.errors));
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Clears the error for the specified payment item
* @param {String} paymentList The onyx key for the provided payment method
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
* @param {String} paymentMethodID
*/
function clearDeletePaymentMethodError(paymentList, paymentMethodID) {
Onyx.merge(paymentList, {
[paymentMethodID]: {
pendingAction: null,
errors: null,
},
});
}

/**
* If there was a failure adding a payment method, clearing it removes the payment method from the list entirely
* @param {String} paymentList The onyx key for the provided payment method
* @param {String} paymentMethodID
*/
function clearAddPaymentMethodError(paymentList, paymentMethodID) {
Onyx.merge(paymentList, {
[paymentMethodID]: null,
});
}

export {
deleteDebitCard,
deletePayPalMe,
Expand All @@ -327,4 +364,7 @@ export {
saveWalletTransferAccountTypeAndID,
saveWalletTransferMethodType,
cleanLocalReimbursementData,
hasPaymentMethodError,
clearDeletePaymentMethodError,
clearAddPaymentMethodError,
};
82 changes: 49 additions & 33 deletions src/pages/settings/InitialSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import * as App from '../../libs/actions/App';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from '../../components/withCurrentUserPersonalDetails';
import * as Policy from '../../libs/actions/Policy';
import policyMemberPropType from '../policyMemberPropType';
import * as PaymentMethods from '../../libs/actions/PaymentMethods';
import bankAccountPropTypes from '../../components/bankAccountPropTypes';
import cardPropTypes from '../../components/cardPropTypes';

const propTypes = {
/* Onyx Props */
Expand Down Expand Up @@ -60,6 +63,12 @@ const propTypes = {
currentBalance: PropTypes.number,
}),

/** List of bank accounts */
bankAccountList: PropTypes.objectOf(bankAccountPropTypes),

/** List of cards */
cardList: PropTypes.objectOf(cardPropTypes),

/** List of betas available to current user */
betas: PropTypes.arrayOf(PropTypes.string),

Expand All @@ -78,39 +87,6 @@ const defaultProps = {
...withCurrentUserPersonalDetailsDefaultProps,
};

const defaultMenuItems = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this moved?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The brickRoadIndicator: PaymentMethods.hasPaymentMethodError(props.bankAccountList, props.cardList) ? 'error' : null, needs access to something in props now

{
translationKey: 'common.profile',
icon: Expensicons.Profile,
action: () => { App.openProfile(); },
},
{
translationKey: 'common.preferences',
icon: Expensicons.Gear,
action: () => { Navigation.navigate(ROUTES.SETTINGS_PREFERENCES); },
},
{
translationKey: 'initialSettingsPage.security',
icon: Expensicons.Lock,
action: () => { Navigation.navigate(ROUTES.SETTINGS_SECURITY); },
},
{
translationKey: 'common.payments',
icon: Expensicons.Wallet,
action: () => { Navigation.navigate(ROUTES.SETTINGS_PAYMENTS); },
},
{
translationKey: 'initialSettingsPage.about',
icon: Expensicons.Info,
action: () => { Navigation.navigate(ROUTES.SETTINGS_ABOUT); },
},
{
translationKey: 'initialSettingsPage.signOut',
icon: Expensicons.Exit,
action: Session.signOutAndRedirectToSignIn,
},
];

const InitialSettingsPage = (props) => {
const walletBalance = props.numberFormat(
props.userWallet.currentBalance / 100, // Divide by 100 because balance is in cents
Expand All @@ -124,6 +100,40 @@ const InitialSettingsPage = (props) => {
return null;
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
}

const defaultMenuItems = [
{
translationKey: 'common.profile',
icon: Expensicons.Profile,
action: () => { App.openProfile(); },
},
{
translationKey: 'common.preferences',
icon: Expensicons.Gear,
action: () => { Navigation.navigate(ROUTES.SETTINGS_PREFERENCES); },
},
{
translationKey: 'initialSettingsPage.security',
icon: Expensicons.Lock,
action: () => { Navigation.navigate(ROUTES.SETTINGS_SECURITY); },
},
{
translationKey: 'common.payments',
icon: Expensicons.Wallet,
action: () => { Navigation.navigate(ROUTES.SETTINGS_PAYMENTS); },
brickRoadIndicator: PaymentMethods.hasPaymentMethodError(props.bankAccountList, props.cardList) ? 'error' : null,
},
{
translationKey: 'initialSettingsPage.about',
icon: Expensicons.Info,
action: () => { Navigation.navigate(ROUTES.SETTINGS_ABOUT); },
},
{
translationKey: 'initialSettingsPage.signOut',
icon: Expensicons.Exit,
action: Session.signOutAndRedirectToSignIn,
},
];

// Add free policies (workspaces) to the list of menu items
const menuItems = _.chain(props.policies)
.filter(policy => policy && policy.type === CONST.POLICY.TYPE.FREE && policy.role === CONST.POLICY.ROLE.ADMIN)
Expand Down Expand Up @@ -225,5 +235,11 @@ export default compose(
betas: {
key: ONYXKEYS.BETAS,
},
bankAccountList: {
key: ONYXKEYS.BANK_ACCOUNT_LIST,
},
cardList: {
key: ONYXKEYS.CARD_LIST,
},
}),
)(InitialSettingsPage);
47 changes: 33 additions & 14 deletions src/pages/settings/Payments/PaymentMethodList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import _ from 'underscore';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {FlatList} from 'react-native';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
import styles from '../../../styles/styles';
import * as StyleUtils from '../../../styles/StyleUtils';
Expand All @@ -17,6 +18,8 @@ import bankAccountPropTypes from '../../../components/bankAccountPropTypes';
import cardPropTypes from '../../../components/cardPropTypes';
import * as PaymentUtils from '../../../libs/PaymentUtils';
import FormAlertWrapper from '../../../components/FormAlertWrapper';
import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
import * as PaymentMethods from '../../../libs/actions/PaymentMethods';

const MENU_ITEM = 'menuItem';
const BUTTON = 'button';
Expand Down Expand Up @@ -164,6 +167,20 @@ class PaymentMethodList extends Component {
return combinedPaymentMethods;
}

/**
* Dismisses the error on the payment method
* @param {*} item
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
*/
dismissError(item) {
const paymentList = item.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? ONYXKEYS.BANK_ACCOUNT_LIST : ONYXKEYS.CARD_LIST;
const paymentID = item.accountType === CONST.PAYMENT_METHODS.BANK_ACCOUNT ? lodashGet(item, ['accountData', 'bankAccountID']) : lodashGet(item, ['accountData', 'cardID']);
aldo-expensify marked this conversation as resolved.
Show resolved Hide resolved
if (item.pendingAction === 'delete') {
thienlnam marked this conversation as resolved.
Show resolved Hide resolved
PaymentMethods.clearDeletePaymentMethodError(paymentList, paymentID);
} else {
PaymentMethods.clearAddPaymentMethodError(paymentList, paymentID);
}
}

/**
* @param {Object} paymentMethod
* @param {String|Number} paymentMethod.methodID
Expand All @@ -185,20 +202,22 @@ class PaymentMethodList extends Component {
renderItem({item}) {
if (item.type === MENU_ITEM) {
return (
<MenuItem
onPress={item.onPress}
title={item.title}
description={item.description}
icon={item.icon}
disabled={item.disabled}
iconFill={item.iconFill}
iconHeight={item.iconSize}
iconWidth={item.iconSize}
badgeText={this.getDefaultBadgeText(item.isDefault)}
wrapperStyle={item.wrapperStyle}
shouldShowSelectedState={this.props.shouldShowSelectedState}
isSelected={this.props.selectedMethodID === item.methodID}
/>
<OfflineWithFeedback onClose={() => this.dismissError(item)} pendingAction={item.pendingAction} errors={item.errors} errorStyle={styles.ph6}>
<MenuItem
onPress={item.onPress}
title={item.title}
description={item.description}
icon={item.icon}
disabled={item.disabled}
iconFill={item.iconFill}
iconHeight={item.iconSize}
iconWidth={item.iconSize}
badgeText={this.getDefaultBadgeText(item.isDefault)}
wrapperStyle={item.wrapperStyle}
shouldShowSelectedState={this.props.shouldShowSelectedState}
isSelected={this.props.selectedMethodID === item.methodID}
/>
</OfflineWithFeedback>
);
}
if (item.type === BUTTON) {
Expand Down