Skip to content

Commit

Permalink
Merge pull request #26406 from pac-guerreiro/feature/empty-state-view…
Browse files Browse the repository at this point in the history
…-and-add-bank-accounts-plus-wallet-tile

feat(Wallet): add empty state and redesign wallet page
  • Loading branch information
grgia authored Oct 13, 2023
2 parents 148f5b1 + 7b09007 commit 6af0d66
Show file tree
Hide file tree
Showing 21 changed files with 489 additions and 205 deletions.
1 change: 1 addition & 0 deletions assets/animations/FastMoney.json

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default {
WORKSPACES: 'Settings_Workspaces',
SECURITY: 'Settings_Security',
STATUS: 'Settings_Status',
WALLET: 'Settings_Wallet',
WALLET_DOMAIN_CARDS: 'Settings_Wallet_DomainCards',
},
SAVE_THE_WORLD: {
ROOT: 'SaveTheWorld_Root',
Expand Down
2 changes: 2 additions & 0 deletions src/components/Icon/Illustrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import MoneyBadge from '../../../assets/images/simple-illustrations/simple-illus
import TreasureChest from '../../../assets/images/simple-illustrations/simple-illustration__treasurechest.svg';
import ThumbsUpStars from '../../../assets/images/simple-illustrations/simple-illustration__thumbsupstars.svg';
import Hands from '../../../assets/images/product-illustrations/home-illustration-hands.svg';
import HandEarth from '../../../assets/images/simple-illustrations/simple-illustration__handearth.svg';

export {
Abracadabra,
Expand Down Expand Up @@ -94,4 +95,5 @@ export {
TreasureChest,
ThumbsUpStars,
Hands,
HandEarth,
};
3 changes: 2 additions & 1 deletion src/components/LottieAnimations.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const ExpensifyLounge = require('../../assets/animations/ExpensifyLounge.json');
const FastMoney = require('../../assets/animations/FastMoney.json');
const Fireworks = require('../../assets/animations/Fireworks.json');
const Hands = require('../../assets/animations/Hands.json');
const PreferencesDJ = require('../../assets/animations/PreferencesDJ.json');
Expand All @@ -8,4 +9,4 @@ const SaveTheWorld = require('../../assets/animations/SaveTheWorld.json');
const Safe = require('../../assets/animations/Safe.json');
const Magician = require('../../assets/animations/Magician.json');

export {ExpensifyLounge, Fireworks, Hands, PreferencesDJ, ReviewingBankInfo, SaveTheWorld, WorkspacePlanet, Safe, Magician};
export {ExpensifyLounge, FastMoney, Fireworks, Hands, PreferencesDJ, ReviewingBankInfo, SaveTheWorld, WorkspacePlanet, Safe, Magician};
44 changes: 34 additions & 10 deletions src/components/Section.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const propTypes = {
/** The text to display in the title of the section */
title: PropTypes.string.isRequired,

/** The text to display in the subtitle of the section */
subtitle: PropTypes.string,

/** The icon to display along with the title */
icon: PropTypes.func,

Expand All @@ -27,6 +30,18 @@ const propTypes = {
// eslint-disable-next-line react/forbid-prop-types
containerStyles: PropTypes.arrayOf(PropTypes.object),

/** Customize the Section container */
// eslint-disable-next-line react/forbid-prop-types
titleStyles: PropTypes.arrayOf(PropTypes.object),

/** Customize the Section container */
// eslint-disable-next-line react/forbid-prop-types
subtitleStyles: PropTypes.arrayOf(PropTypes.object),

/** Customize the Section container */
// eslint-disable-next-line react/forbid-prop-types
childrenStyles: PropTypes.arrayOf(PropTypes.object),

/** Customize the Icon container */
// eslint-disable-next-line react/forbid-prop-types
iconContainerStyles: PropTypes.arrayOf(PropTypes.object),
Expand All @@ -39,21 +54,24 @@ const defaultProps = {
IconComponent: null,
containerStyles: [],
iconContainerStyles: [],
titleStyles: [],
subtitleStyles: [],
childrenStyles: [],
subtitle: null,
};

function Section(props) {
const IconComponent = props.IconComponent;
function Section({children, childrenStyles, containerStyles, icon, IconComponent, iconContainerStyles, menuItems, subtitle, subtitleStyles, title, titleStyles}) {
return (
<>
<View style={[styles.pageWrapper, styles.cardSection, ...props.containerStyles]}>
<View style={[styles.flexRow, styles.alignItemsCenter, styles.w100]}>
<View style={[styles.pageWrapper, styles.cardSection, ...containerStyles]}>
<View style={[styles.flexRow, styles.alignItemsCenter, styles.w100, ...titleStyles]}>
<View style={[styles.flexShrink1]}>
<Text style={[styles.textHeadline, styles.cardSectionTitle]}>{props.title}</Text>
<Text style={[styles.textHeadline, styles.cardSectionTitle]}>{title}</Text>
</View>
<View style={[styles.flexGrow1, styles.flexRow, styles.justifyContentEnd, ...props.iconContainerStyles]}>
{Boolean(props.icon) && (
<View style={[styles.flexGrow1, styles.flexRow, styles.justifyContentEnd, ...iconContainerStyles]}>
{Boolean(icon) && (
<Icon
src={props.icon}
src={icon}
height={68}
width={68}
/>
Expand All @@ -62,9 +80,15 @@ function Section(props) {
</View>
</View>

<View style={[styles.w100]}>{props.children}</View>
{Boolean(subtitle) && (
<View style={[styles.flexRow, styles.alignItemsCenter, styles.w100, styles.mt4, ...subtitleStyles]}>
<Text style={styles.textNormal}>{subtitle}</Text>
</View>
)}

<View style={[styles.w100, ...childrenStyles]}>{children}</View>

<View style={[styles.w100]}>{Boolean(props.menuItems) && <MenuItemList menuItems={props.menuItems} />}</View>
<View style={[styles.w100]}>{Boolean(menuItems) && <MenuItemList menuItems={menuItems} />}</View>
</View>
</>
);
Expand Down
45 changes: 45 additions & 0 deletions src/components/WalletSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import PropTypes from 'prop-types';
import React from 'react';
import Section from './Section';
import styles from '../styles/styles';

const propTypes = {
/** Contents to display inside the section */
children: PropTypes.node,

/** The icon to display along with the title */
icon: PropTypes.func,

/** The text to display in the subtitle of the section */
subtitle: PropTypes.string,

/** The text to display in the title of the section */
title: PropTypes.string.isRequired,
};

const defaultProps = {
children: null,
icon: null,
subtitle: null,
};

function WalletSection({children, icon, subtitle, title}) {
return (
<Section
icon={icon}
subtitle={subtitle}
title={title}
containerStyles={[styles.p0, styles.pv5]}
titleStyles={[styles.ph5]}
subtitleStyles={[styles.ph5]}
>
{children}
</Section>
);
}

WalletSection.defaultProps = defaultProps;
WalletSection.displayName = 'WalletSection';
WalletSection.propTypes = propTypes;

export default WalletSection;
10 changes: 10 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,16 @@ export default {
setDefaultFailure: 'Something went wrong. Please chat with Concierge for further assistance.',
},
addBankAccountFailure: 'An unexpected error occurred while trying to add your bank account. Please try again.',
getPaidFaster: 'Get paid faster',
addPaymentMethod: 'Add a payment method to send and receive payments directly in the app.',
getPaidBackFaster: 'Get paid back faster',
secureAccessToYourMoney: 'Secure access to your money',
receiveMoney: 'Receive money in your local currency',
expensifyWallet: 'Expensify Wallet',
sendAndReceiveMoney: 'Send and receive money from your Expensify Wallet.',
bankAccounts: 'Bank accounts',
addBankAccountToSendAndReceive: 'Add a bank account to send and receive payments directly in the app.',
addBankAccount: 'Add bank account',
},
cardPage: {
expensifyCard: 'Expensify Card',
Expand Down
10 changes: 10 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,16 @@ export default {
setDefaultFailure: 'No se ha podido configurar el método de pago.',
},
addBankAccountFailure: 'Ocurrió un error inesperado al intentar añadir la cuenta bancaria. Inténtalo de nuevo.',
getPaidFaster: 'Cobra más rápido',
addPaymentMethod: 'Añade un método de pago para enviar y recibir pagos directamente en la aplicación.',
getPaidBackFaster: 'Recibe tus pagos más rápido',
secureAccessToYourMoney: 'Acceso seguro a tu dinero',
receiveMoney: 'Recibe dinero en tu moneda local',
expensifyWallet: 'Billetera Expensify',
sendAndReceiveMoney: 'Envía y recibe dinero desde tu Billetera Expensify.',
bankAccounts: 'Cuentas bancarias',
addBankAccountToSendAndReceive: 'Añade una cuenta bancaria para enviar y recibir pagos directamente en la aplicación.',
addBankAccount: 'Agregar cuenta bancaria',
},
cardPage: {
expensifyCard: 'Tarjeta Expensify',
Expand Down
2 changes: 1 addition & 1 deletion src/libs/Navigation/NavigationRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ function NavigationRoot(props) {

const animateStatusBarBackgroundColor = () => {
const currentRoute = navigationRef.getCurrentRoute();
const currentScreenBackgroundColor = themeColors.PAGE_BACKGROUND_COLORS[currentRoute.name] || themeColors.appBG;
const currentScreenBackgroundColor = (currentRoute.params && currentRoute.params.backgroundColor) || themeColors.PAGE_BACKGROUND_COLORS[currentRoute.name] || themeColors.appBG;

prevStatusBarBackgroundColor.current = statusBarBackgroundColor.current;
statusBarBackgroundColor.current = currentScreenBackgroundColor;
Expand Down
69 changes: 57 additions & 12 deletions src/pages/settings/Wallet/PaymentMethodList.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import FormAlertWrapper from '../../../components/FormAlertWrapper';
import OfflineWithFeedback from '../../../components/OfflineWithFeedback';
import * as PaymentMethods from '../../../libs/actions/PaymentMethods';
import Log from '../../../libs/Log';
import stylePropTypes from '../../../styles/stylePropTypes';

const propTypes = {
/** What to do when a menu item is pressed */
Expand All @@ -36,6 +37,9 @@ const propTypes = {
/** Whether the add Payment button be shown on the list */
shouldShowAddPaymentMethodButton: PropTypes.bool,

/** Whether the empty list message should be shown when the list is empty */
shouldShowEmptyListMessage: PropTypes.bool,

/** Are we loading payment methods? */
isLoadingPaymentMethods: PropTypes.bool,

Expand Down Expand Up @@ -69,6 +73,12 @@ const propTypes = {
/** React ref being forwarded to the PaymentMethodList Button */
buttonRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),

/** To enable/disable scrolling */
shouldEnableScroll: PropTypes.bool,

/** List container style */
style: stylePropTypes,

...withLocalizePropTypes,
};

Expand All @@ -81,13 +91,16 @@ const defaultProps = {
},
isLoadingPaymentMethods: true,
shouldShowAddPaymentMethodButton: true,
shouldShowEmptyListMessage: true,
filterType: '',
actionPaymentMethodType: '',
activePaymentMethodID: '',
selectedMethodID: '',
listHeaderComponent: null,
buttonRef: () => {},
onListContentSizeChange: () => {},
shouldEnableScroll: true,
style: {},
};

/**
Expand Down Expand Up @@ -143,9 +156,26 @@ function shouldShowDefaultBadge(filteredPaymentMethods, isDefault = false) {
function isPaymentMethodActive(actionPaymentMethodType, activePaymentMethodID, paymentMethod) {
return paymentMethod.accountType === actionPaymentMethodType && paymentMethod.methodID === activePaymentMethodID;
}
function PaymentMethodList(props) {
const {actionPaymentMethodType, activePaymentMethodID, bankAccountList, fundList, filterType, network, onPress, shouldShowSelectedState, selectedMethodID, translate} = props;

function PaymentMethodList({
actionPaymentMethodType,
activePaymentMethodID,
bankAccountList,
buttonRef,
fundList,
filterType,
isLoadingPaymentMethods,
listHeaderComponent,
network,
onListContentSizeChange,
onPress,
shouldEnableScroll,
shouldShowSelectedState,
shouldShowAddPaymentMethodButton,
shouldShowEmptyListMessage,
selectedMethodID,
style,
translate,
}) {
const filteredPaymentMethods = useMemo(() => {
const paymentCardList = fundList || {};
// Hide any billing cards that are not P2P debit cards for now because you cannot make them your default method, or delete them
Expand Down Expand Up @@ -183,6 +213,18 @@ function PaymentMethodList(props) {
*/
const renderListEmptyComponent = useCallback(() => <Text style={[styles.popoverMenuItem]}>{translate('paymentMethodList.addFirstPaymentMethod')}</Text>, [translate]);

const renderListFooterComponent = useCallback(
() => (
<MenuItem
onPress={onPress}
title={translate('walletPage.addBankAccount')}
icon={Expensicons.Plus}
wrapperStyle={styles.paymentMethod}
/>
),
[onPress, translate],
);

/**
* Create a menuItem for each passed paymentMethod
*
Expand All @@ -209,13 +251,13 @@ function PaymentMethodList(props) {
iconHeight={item.iconSize}
iconWidth={item.iconSize}
badgeText={shouldShowDefaultBadge(filteredPaymentMethods, item.isDefault) ? translate('paymentMethodList.defaultPaymentMethod') : null}
wrapperStyle={item.wrapperStyle}
wrapperStyle={styles.paymentMethod}
shouldShowSelectedState={shouldShowSelectedState}
isSelected={selectedMethodID === item.methodID}
/>
</OfflineWithFeedback>
),
[shouldShowSelectedState, selectedMethodID, filteredPaymentMethods, translate],
[filteredPaymentMethods, translate, shouldShowSelectedState, selectedMethodID],
);

return (
Expand All @@ -224,25 +266,28 @@ function PaymentMethodList(props) {
data={filteredPaymentMethods}
renderItem={renderItem}
keyExtractor={(item) => item.key}
ListEmptyComponent={renderListEmptyComponent(translate)}
ListHeaderComponent={props.listHeaderComponent}
onContentSizeChange={props.onListContentSizeChange}
ListEmptyComponent={shouldShowEmptyListMessage ? renderListEmptyComponent(translate) : null}
ListHeaderComponent={listHeaderComponent}
ListFooterComponent={renderListFooterComponent}
onContentSizeChange={onListContentSizeChange}
scrollEnabled={shouldEnableScroll}
style={style}
/>
{props.shouldShowAddPaymentMethodButton && (
{shouldShowAddPaymentMethodButton && (
<FormAlertWrapper>
{(isOffline) => (
<Button
text={translate('paymentMethodList.addPaymentMethod')}
icon={Expensicons.CreditCard}
onPress={props.onPress}
isDisabled={props.isLoadingPaymentMethods || isOffline}
onPress={onPress}
isDisabled={isLoadingPaymentMethods || isOffline}
style={[styles.mh4, styles.buttonCTA]}
iconStyles={[styles.buttonCTAIcon]}
key="addPaymentMethodButton"
success
shouldShowRightIcon
large
ref={props.buttonRef}
ref={buttonRef}
/>
)}
</FormAlertWrapper>
Expand Down
Loading

0 comments on commit 6af0d66

Please sign in to comment.