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

Added Balance badge to the Settings page #4220

Merged
merged 3 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
63 changes: 63 additions & 0 deletions src/components/Badge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import {Pressable, View} from 'react-native';
import PropTypes from 'prop-types';
import styles, {getBadgeColorStyle} from '../styles/styles';
import Text from './Text';

const propTypes = {
/** Is success type */
success: PropTypes.bool,

/** Is success type */
Copy link
Contributor

Choose a reason for hiding this comment

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

Error type

error: PropTypes.bool,

/** Whether badge is clickable */
pressable: PropTypes.bool,

/** Text to display in the Badge */
text: PropTypes.string.isRequired,

/** Styles for Badge */
badgeStyles: PropTypes.arrayOf(PropTypes.object),

/** Callback to be called on onPress */
onPress: PropTypes.func,
};

const defaultProps = {
success: false,
error: false,
pressable: false,
badgeStyles: [],
onPress: undefined,
};

const Badge = (props) => {
const textStyles = props.success || props.error ? styles.textWhite : undefined;
const Wrapper = props.pressable ? Pressable : View;
const wrapperStyles = ({pressed}) => ([
styles.badge,
styles.ml2,
getBadgeColorStyle(props.success, props.error, pressed),
...props.badgeStyles,
]);

return (
<Wrapper
style={props.pressable ? wrapperStyles : wrapperStyles(false)}
onPress={props.onPress}
>
<Text
style={[styles.badgeText, textStyles]}
numberOfLines={1}
>
{props.text}
</Text>
</Wrapper>
);
};

Badge.displayName = 'Badge';
Badge.propTypes = propTypes;
Badge.defaultProps = defaultProps;
export default Badge;
18 changes: 6 additions & 12 deletions src/components/EnvironmentBadge.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import React from 'react';
import {View} from 'react-native';
import styles from '../styles/styles';
import CONST from '../CONST';
import Text from './Text';
import withEnvironment, {environmentPropTypes} from './withEnvironment';
import Badge from './Badge';

const EnvironmentBadge = (props) => {
// If we are on production, don't show any badge
if (props.environment === CONST.ENVIRONMENT.PRODUCTION) {
return null;
}

const backgroundColorStyle = props.environment === CONST.ENVIRONMENT.STAGING
? styles.badgeSuccess
: styles.badgeDanger;

return (
<View style={[styles.badge, backgroundColorStyle, styles.ml2]}>
<Text style={styles.badgeText}>
{props.environment}
</Text>
</View>
<Badge
success={props.environment === CONST.ENVIRONMENT.STAGING}
error={props.environment !== CONST.ENVIRONMENT.STAGING}
text={props.environment}
/>
);
};

Expand Down
32 changes: 11 additions & 21 deletions src/components/IOUBadge.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Pressable} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import ONYXKEYS from '../ONYXKEYS';
import styles, {getBadgeColorStyle} from '../styles/styles';
import Navigation from '../libs/Navigation/Navigation';
import ROUTES from '../ROUTES';
import compose from '../libs/compose';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import CONST from '../CONST';
import Text from './Text';
import Badge from './Badge';

const propTypes = {
/** IOU Report data object */
Expand Down Expand Up @@ -53,24 +51,16 @@ const IOUBadge = (props) => {
Navigation.navigate(ROUTES.getIouDetailsRoute(props.iouReport.chatReportID, props.iouReport.reportID));
};
return (
<Pressable
style={({pressed}) => ([
styles.badge,
styles.ml2,
getBadgeColorStyle(props.session.email === props.iouReport.ownerEmail, pressed),
])}
>
<Text
style={styles.badgeText}
numberOfLines={1}
onPress={launchIOUDetailsModal}
>
{props.numberFormat(
props.iouReport.total / 100,
{style: 'currency', currency: props.iouReport.currency},
)}
</Text>
</Pressable>
<Badge
pressable
onPress={launchIOUDetailsModal}
success={props.session.email === props.iouReport.ownerEmail}
error={props.session.email !== props.iouReport.ownerEmail}
text={props.numberFormat(
props.iouReport.total / 100,
{style: 'currency', currency: props.iouReport.currency},
)}
/>
);
};

Expand Down
7 changes: 7 additions & 0 deletions src/components/MenuItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import styles, {getButtonBackgroundColorStyle, getIconFillColor} from '../styles
import Icon from './Icon';
import {ArrowRight} from './Icon/Expensicons';
import getButtonState from '../libs/getButtonState';
import Badge from './Badge';

const propTypes = {
// Text to be shown as badge near the right end.
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment should use block comments per style guide

badgeText: PropTypes.string,

/** Any additional styles to apply */
// eslint-disable-next-line react/forbid-prop-types
wrapperStyle: PropTypes.object,
Expand Down Expand Up @@ -58,6 +62,7 @@ const propTypes = {
};

const defaultProps = {
badgeText: undefined,
shouldShowRightIcon: false,
wrapperStyle: {},
success: false,
Expand All @@ -74,6 +79,7 @@ const defaultProps = {
};

const MenuItem = ({
badgeText,
onPress,
icon,
iconRight,
Expand Down Expand Up @@ -141,6 +147,7 @@ const MenuItem = ({
</View>
</View>
<View style={[styles.flexRow, styles.menuItemTextContainer]}>
{badgeText && <Badge text={badgeText} badgeStyles={[styles.alignSelfCenter]} />}
{subtitle && (
<View style={[styles.justifyContentCenter, styles.mr1]}>
<Text
Expand Down
19 changes: 19 additions & 0 deletions src/pages/settings/InitialPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ const propTypes = {
role: PropTypes.string,
})),

/** The user's wallet account */
userWallet: PropTypes.shape({
/** The user's current wallet balance */
availableBalance: PropTypes.number,
}),

...withLocalizePropTypes,
};

Expand All @@ -75,6 +81,7 @@ const defaultProps = {
network: {},
session: {},
policies: {},
userWallet: {},
};

const defaultMenuItems = [
Expand Down Expand Up @@ -113,10 +120,17 @@ const defaultMenuItems = [
const InitialSettingsPage = ({
myPersonalDetails,
network,
numberFormat,
session,
policies,
translate,
userWallet,
}) => {
const walletBalance = numberFormat(
userWallet.availableBalance,
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like if the user has no availableBalance, this can be $NaN, so I think we want a fallback of 0 here.

{style: 'currency', currency: 'USD'},
);

// On the very first sign in or after clearing storage these
// details will not be present on the first render so we'll just
// return nothing for now.
Expand Down Expand Up @@ -169,6 +183,7 @@ const InitialSettingsPage = ({
</View>
{_.map(menuItems, (item, index) => {
const keyTitle = item.translationKey ? translate(item.translationKey) : item.title;
const isPaymentItem = item.translationKey === 'common.payments';
return (
<MenuItem
key={`${keyTitle}_${index}`}
Expand All @@ -178,6 +193,7 @@ const InitialSettingsPage = ({
iconStyles={item.iconStyles}
iconFill={item.iconFill}
shouldShowRightIcon
badgeText={isPaymentItem ? walletBalance : undefined}
/>
);
})}
Expand Down Expand Up @@ -206,5 +222,8 @@ export default compose(
policies: {
key: ONYXKEYS.COLLECTION.POLICY,
},
userWallet: {
key: ONYXKEYS.USER_WALLET,
},
}),
)(InitialSettingsPage);
20 changes: 12 additions & 8 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ const styles = {
},

badgeText: {
color: themeColors.textReversed,
color: themeColors.text,
fontSize: variables.fontSizeSmall,
lineHeight: 16,
...whiteSpace.noWrap,
Expand Down Expand Up @@ -2012,17 +2012,21 @@ function getBackgroundColorStyle(backgroundColor) {
}

/**
* Generate a style for the background color of the IOU badge
* Generate a style for the background color of the Badge
*
* @param {Boolean} isOwner
* @param {Boolean} [isPressed]
* @returns {Object}
* @param {*} success
* @param {*} error
Copy link
Contributor

Choose a reason for hiding this comment

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

These should be {Boolean} right?

* @param {boolean} [isPressed=false]
* @return {Object}
*/
function getBadgeColorStyle(isOwner, isPressed = false) {
if (isOwner) {
function getBadgeColorStyle(success, error, isPressed = false) {
if (success) {
return isPressed ? styles.badgeSuccessPressed : styles.badgeSuccess;
}
return isPressed ? styles.badgeDangerPressed : styles.badgeDanger;
if (error) {
return isPressed ? styles.badgeDangerPressed : styles.badgeDanger;
}
return {};
}

/**
Expand Down