Merge pull request #454 from trezor/feature/stealth-mode

Feature/stealth mode (hidden balance)
pull/464/head
Vladimir Volek 5 years ago committed by GitHub
commit 8e3258b8aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -82,6 +82,7 @@
"styled-normalize": "^8.0.6",
"trezor-bridge-communicator": "1.0.2",
"trezor-connect": "7.0.0-beta.3",
"trezor-ui-components": "^1.0.0-beta.4",
"wallet-address-validator": "^0.2.4",
"web3": "1.0.0-beta.35",
"webpack": "^4.29.3",

@ -58,6 +58,7 @@ const KEY_PENDING: string = `${STORAGE_PATH}pending`;
const KEY_BETA_MODAL: string = '/betaModalPrivacy'; // this key needs to be compatible with "parent" (old) wallet
const KEY_LANGUAGE: string = `${STORAGE_PATH}language`;
const KEY_LOCAL_CURRENCY: string = `${STORAGE_PATH}localCurrency`;
const KEY_HIDE_BALANCE: string = `${STORAGE_PATH}hideBalance`;
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
// or
@ -282,6 +283,11 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
if (localCurrency) {
dispatch(WalletActions.setLocalCurrency(JSON.parse(localCurrency)));
}
const hideBalance: ?boolean = storageUtils.get(TYPE, KEY_HIDE_BALANCE);
if (hideBalance) {
dispatch(WalletActions.setHideBalance(hideBalance));
}
};
export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
@ -303,6 +309,11 @@ export const setLanguage = (): ThunkAction => (dispatch: Dispatch, getState: Get
storageUtils.set(TYPE, KEY_LANGUAGE, JSON.stringify(language));
};
export const setHideBalance = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const { hideBalance } = getState().wallet;
storageUtils.set(TYPE, KEY_HIDE_BALANCE, hideBalance);
};
export const setLocalCurrency = (): ThunkAction => (
dispatch: Dispatch,
getState: GetState

@ -62,6 +62,10 @@ export type WalletAction =
| {
type: typeof WALLET.SET_LOCAL_CURRENCY,
localCurrency: string,
}
| {
type: typeof WALLET.SET_HIDE_BALANCE,
toggled: boolean,
};
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
@ -113,6 +117,11 @@ export const setLocalCurrency = (localCurrency: string): WalletAction => ({
localCurrency: localCurrency.toLowerCase(),
});
export const setHideBalance = (toggled: boolean): WalletAction => ({
type: WALLET.SET_HIDE_BALANCE,
toggled,
});
// This method will be called after each DEVICE.CONNECT action
// if connected device has different "passphrase_protection" settings than saved instances
// all saved instances will be removed immediately inside DevicesReducer

@ -18,3 +18,4 @@ export const CLEAR_UNAVAILABLE_DEVICE_DATA: 'wallet__clear_unavailable_device_da
export const TOGGLE_SIDEBAR: 'wallet__toggle_sidebar' = 'wallet__toggle_sidebar';
export const SET_LANGUAGE: 'wallet__set_language' = 'wallet__set_language';
export const SET_LOCAL_CURRENCY: 'wallet__set_local_currency' = 'wallet__set_local_currency';
export const SET_HIDE_BALANCE: 'wallet__set_hide_balance' = 'wallet__set_hide_balance';

@ -13,8 +13,9 @@ type State = {
ready: boolean,
online: boolean,
language: string,
localCurrency: string,
messages: { [string]: string },
localCurrency: string,
hideBalance: boolean,
dropdownOpened: boolean,
showBetaDisclaimer: boolean,
showSidebar: ?boolean,
@ -29,8 +30,9 @@ const initialState: State = {
ready: false,
online: navigator.onLine,
language: 'en',
localCurrency: 'usd',
messages: {},
localCurrency: 'usd',
hideBalance: false,
dropdownOpened: false,
firstLocationChange: true,
showBetaDisclaimer: false,
@ -137,6 +139,12 @@ export default function wallet(state: State = initialState, action: Action): Sta
localCurrency: action.localCurrency,
};
case WALLET.SET_HIDE_BALANCE:
return {
...state,
hideBalance: action.toggled,
};
default:
return state;
}

@ -26,6 +26,10 @@ const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: Middlewar
api.dispatch(LocalStorageActions.setLanguage());
break;
case WALLET.SET_HIDE_BALANCE:
api.dispatch(LocalStorageActions.setHideBalance());
break;
case WALLET.SET_LOCAL_CURRENCY:
api.dispatch(LocalStorageActions.setLocalCurrency());
break;

@ -18,6 +18,7 @@ import FirmwareUnsupported from './components/FirmwareUnsupported';
import l10nMessages from './index.messages';
type Props = {
className?: string,
children?: React.Node,
isLoading?: boolean,
loader?: $ElementType<$ElementType<State, 'selectedAccount'>, 'loader'>,
@ -76,8 +77,8 @@ const getExceptionPage = exceptionPage => {
}
};
const Content = ({ children, isLoading = false, loader, exceptionPage }: Props) => (
<Wrapper>
const Content = ({ className, children, isLoading = false, loader, exceptionPage }: Props) => (
<Wrapper className={className}>
{!isLoading && children}
{isLoading && exceptionPage && getExceptionPage(exceptionPage)}
{isLoading && loader && (
@ -98,6 +99,7 @@ const Content = ({ children, isLoading = false, loader, exceptionPage }: Props)
Content.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.array]),
className: PropTypes.string,
isLoading: PropTypes.bool,
loader: PropTypes.object,
exceptionPage: PropTypes.object,

@ -1,5 +1,5 @@
/* @flow */
import { toggleDeviceDropdown } from 'actions/WalletActions';
import { toggleDeviceDropdown, toggleSidebar, setHideBalance } from 'actions/WalletActions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
@ -42,6 +42,8 @@ const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps>
gotoDeviceSettings: bindActionCreators(RouterActions.gotoDeviceSettings, dispatch),
onSelectDevice: bindActionCreators(RouterActions.selectDevice, dispatch),
gotoExternalWallet: bindActionCreators(ModalActions.gotoExternalWallet, dispatch),
toggleSidebar: bindActionCreators(toggleSidebar, dispatch),
setHideBalance: bindActionCreators(setHideBalance, dispatch),
});
export default withRouter(

@ -141,7 +141,7 @@ const AccountMenu = (props: Props) => {
{...l10nCommonMessages.TR_ACCOUNT_HASH}
values={{ number: account.index + 1 }}
/>
{balance && (
{balance && !props.wallet.hideBalance && (
<Text>
{balance}
{fiatRates && (

@ -3,6 +3,7 @@ import styled from 'styled-components';
import PropTypes from 'prop-types';
import Icon from 'components/Icon';
import Link from 'components/Link';
import { Switch } from 'trezor-ui-components';
import DeviceIcon from 'components/images/DeviceIcon';
import { FormattedMessage } from 'react-intl';
import { getPattern } from 'support/routes';
@ -21,6 +22,7 @@ const Wrapper = styled.div`
const Item = styled.div`
padding: 6px 24px;
display: flex;
height: 38px;
align-items: center;
font-size: ${FONT_SIZE.BASE};
cursor: pointer;
@ -39,6 +41,7 @@ const Divider = styled.div`
const Label = styled.div`
padding-left: 15px;
flex: 1;
`;
class MenuItems extends PureComponent {
@ -108,6 +111,24 @@ class MenuItems extends PureComponent {
</Label>
</Item>
<Divider />
<Item>
<Icon icon={icons.EYE_CROSSED} size={25} color={colors.TEXT_SECONDARY} />
<Label>
<FormattedMessage {...l10nCommonMessages.TR_HIDE_BALANCE} />
</Label>
<Switch
width={36}
height={18}
handleDiameter={14}
checkedIcon={false}
uncheckedIcon={false}
onChange={checked => {
this.props.setHideBalance(checked);
}}
checked={this.props.wallet.hideBalance}
/>
</Item>
<Divider />
<Link to={getPattern('wallet-settings')}>
<Item>
<Icon icon={icons.COG} size={25} color={colors.TEXT_SECONDARY} />
@ -123,9 +144,11 @@ class MenuItems extends PureComponent {
MenuItems.propTypes = {
device: PropTypes.object.isRequired,
wallet: PropTypes.object.isRequired,
acquireDevice: PropTypes.func.isRequired,
forgetDevice: PropTypes.func.isRequired,
duplicateDevice: PropTypes.func.isRequired,
setHideBalance: PropTypes.func.isRequired,
// toggleDeviceDropdown: PropTypes.func.isRequired,
// gotoDeviceSettings: PropTypes.func.isRequired,
};

@ -3,7 +3,7 @@ import * as TrezorConnectActions from 'actions/TrezorConnectActions';
import * as DiscoveryActions from 'actions/DiscoveryActions';
import * as RouterActions from 'actions/RouterActions';
import * as ModalActions from 'actions/ModalActions';
import { toggleDeviceDropdown } from 'actions/WalletActions';
import * as WalletActions from 'actions/WalletActions';
import type { State } from 'flowtype';
export type StateProps = {
@ -19,7 +19,9 @@ export type StateProps = {
};
export type DispatchProps = {
toggleDeviceDropdown: typeof toggleDeviceDropdown,
toggleDeviceDropdown: typeof WalletActions.toggleDeviceDropdown,
toggleSidebar: typeof WalletActions.toggleSidebar,
setHideBalance: typeof WalletActions.setHideBalance,
addAccount: typeof DiscoveryActions.addAccount,
acquireDevice: typeof TrezorConnectActions.acquire,
forgetDevice: typeof TrezorConnectActions.forget,

@ -3,13 +3,14 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import colors from 'config/colors';
import { FONT_SIZE } from 'config/variables';
import { FONT_SIZE, SCREEN_SIZE } from 'config/variables';
import Icon from 'components/Icon';
import WalletTypeIcon from 'components/images/WalletType';
import icons from 'config/icons';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import styled from 'styled-components';
import DeviceHeader from 'components/DeviceHeader';
import Backdrop from 'components/Backdrop';
// import Link from 'components/Link';
import * as deviceUtils from 'utils/device';
@ -98,6 +99,14 @@ type TransitionMenuProps = {
children?: React.Node,
};
const StyledBackdrop = styled(Backdrop)`
display: none;
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
display: initial;
}
`;
// TransitionMenu needs to dispatch window.resize event
// in order to StickyContainer be recalculated
const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGroup> => (
@ -254,66 +263,74 @@ class LeftNavigation extends React.PureComponent<Props, State> {
selectedDevice && selectedDevice.connected && selectedDevice.available;
return (
<Sidebar isOpen={props.wallet.showSidebar}>
<Header
isSelected
testId="Main__page__device__header"
isHoverable={false}
onClickWrapper={() => {
if (isDeviceAccessible || this.props.devices.length > 1) {
this.handleOpen();
}
}}
device={selectedDevice}
disabled={!isDeviceAccessible && this.props.devices.length === 1}
isOpen={this.props.wallet.dropdownOpened}
icon={
<React.Fragment>
{showWalletType && (
<Tooltip
content={
<WalletTooltipMsg
walletType={walletType}
isDeviceReady={isDeviceReady}
/>
}
maxWidth={200}
placement="bottom"
enterDelayMs={0.5}
>
<WalletTypeIconWrapper>
<WalletTypeIcon
onClick={e => {
if (selectedDevice && isDeviceReady) {
this.props.duplicateDevice(selectedDevice);
e.stopPropagation();
<>
<StyledBackdrop
show={props.wallet.showSidebar}
onClick={props.toggleSidebar}
animated
/>
<Sidebar isOpen={props.wallet.showSidebar}>
<Header
isSelected
testId="Main__page__device__header"
isHoverable={false}
onClickWrapper={() => {
if (isDeviceAccessible || this.props.devices.length > 1) {
this.handleOpen();
}
}}
device={selectedDevice}
disabled={!isDeviceAccessible && this.props.devices.length === 1}
isOpen={this.props.wallet.dropdownOpened}
icon={
<React.Fragment>
{showWalletType && (
<Tooltip
content={
<WalletTooltipMsg
walletType={walletType}
isDeviceReady={isDeviceReady}
/>
}
maxWidth={200}
placement="bottom"
enterDelayMs={0.5}
>
<WalletTypeIconWrapper>
<WalletTypeIcon
onClick={e => {
if (selectedDevice && isDeviceReady) {
this.props.duplicateDevice(selectedDevice);
e.stopPropagation();
}
}}
hoverColor={
isDeviceReady
? colors.TEXT_PRIMARY
: colors.TEXT_SECONDARY
}
}}
hoverColor={
isDeviceReady
? colors.TEXT_PRIMARY
: colors.TEXT_SECONDARY
}
type={walletType}
size={25}
color={colors.TEXT_SECONDARY}
/>
</WalletTypeIconWrapper>
</Tooltip>
)}
{this.props.devices.length > 1 && (
<Tooltip
content={
<FormattedMessage {...l10nMessages.TR_NUMBER_OF_DEVICES} />
}
maxWidth={200}
placement="bottom"
enterDelayMs={0.5}
>
<Counter>{this.props.devices.length}</Counter>
</Tooltip>
)}
{/* <Tooltip
type={walletType}
size={25}
color={colors.TEXT_SECONDARY}
/>
</WalletTypeIconWrapper>
</Tooltip>
)}
{this.props.devices.length > 1 && (
<Tooltip
content={
<FormattedMessage
{...l10nMessages.TR_NUMBER_OF_DEVICES}
/>
}
maxWidth={200}
placement="bottom"
enterDelayMs={0.5}
>
<Counter>{this.props.devices.length}</Counter>
</Tooltip>
)}
{/* <Tooltip
content={
<FormattedMessage
{...l10nCommonMessages.TR_APPLICATION_SETTINGS}
@ -334,34 +351,35 @@ class LeftNavigation extends React.PureComponent<Props, State> {
</Link>
</WalletTypeIconWrapper>
</Tooltip> */}
<Icon
canAnimate={this.state.clicked === true}
isActive={this.props.wallet.dropdownOpened}
size={25}
color={colors.TEXT_SECONDARY}
icon={icons.ARROW_DOWN}
/>
</React.Fragment>
}
{...this.props}
/>
<Body minHeight={this.state.bodyMinHeight}>
{dropdownOpened && <DeviceMenu ref={this.deviceMenuRef} {...this.props} />}
{isDeviceAccessible && menu}
</Body>
<Footer data-test="Main__page__footer" key="sticky-footer">
<Help>
<A
href="https://trezor.io/support/"
target="_blank"
rel="noreferrer noopener"
>
<Icon size={26} icon={icons.CHAT} color={colors.TEXT_SECONDARY} />
<FormattedMessage {...l10nMessages.TR_NEED_HELP} />
</A>
</Help>
</Footer>
</Sidebar>
<Icon
canAnimate={this.state.clicked === true}
isActive={this.props.wallet.dropdownOpened}
size={25}
color={colors.TEXT_SECONDARY}
icon={icons.ARROW_DOWN}
/>
</React.Fragment>
}
{...this.props}
/>
<Body minHeight={this.state.bodyMinHeight}>
{dropdownOpened && <DeviceMenu ref={this.deviceMenuRef} {...this.props} />}
{isDeviceAccessible && menu}
</Body>
<Footer data-test="Main__page__footer" key="sticky-footer">
<Help>
<A
href="https://trezor.io/support/"
target="_blank"
rel="noreferrer noopener"
>
<Icon size={26} icon={icons.CHAT} color={colors.TEXT_SECONDARY} />
<FormattedMessage {...l10nMessages.TR_NEED_HELP} />
</A>
</Help>
</Footer>
</Sidebar>
</>
);
}
}
@ -383,6 +401,7 @@ LeftNavigation.propTypes = {
duplicateDevice: PropTypes.func,
gotoDeviceSettings: PropTypes.func,
onSelectDevice: PropTypes.func,
setHideBalance: PropTypes.func,
};
export default LeftNavigation;

@ -23,7 +23,6 @@ import ContextNotifications from 'components/notifications/Context';
import { SCREEN_SIZE } from 'config/variables';
import Log from 'components/Log';
import Backdrop from 'components/Backdrop';
import LeftNavigation from './components/LeftNavigation/Container';
import TopNavigationAccount from './components/TopNavigationAccount';
@ -110,14 +109,6 @@ const Body = styled.div`
flex-direction: column;
`;
const StyledBackdrop = styled(Backdrop)`
display: none;
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
display: initial;
}
`;
const Wallet = (props: Props) => (
<AppWrapper>
<Header
@ -127,11 +118,6 @@ const Wallet = (props: Props) => (
/>
<AppNotifications />
<WalletWrapper>
<StyledBackdrop
show={props.wallet.showSidebar}
onClick={props.toggleSidebar}
animated
/>
{props.wallet.selectedDevice && <LeftNavigation />}
<MainContent preventBgScroll={props.wallet.showSidebar}>
<Navigation>

@ -406,7 +406,7 @@ const AccountSend = (props: Props) => {
<AmountInputLabel>
<FormattedMessage {...l10nSendMessages.TR_AMOUNT} />
</AmountInputLabel>
{isCurrentCurrencyToken && selectedToken && (
{isCurrentCurrencyToken && selectedToken && !props.wallet.hideBalance && (
<AmountInputLabel>
<FormattedMessage
{...l10nMessages.YOU_HAVE_TOKEN_BALANCE}

@ -17,6 +17,7 @@ type Props = {
balance: string,
fiat: $ElementType<ReducersState, 'fiat'>,
localCurrency: string,
isHidden: boolean,
};
type State = {
@ -105,11 +106,20 @@ class AccountBalance extends PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isHidden: false,
canAnimateHideBalanceIcon: false,
isHidden: props.isHidden,
canAnimateHideBalanceIcon: props.isHidden,
};
}
componentDidUpdate(prevProps: Props) {
if (prevProps.isHidden !== this.props.isHidden) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
isHidden: this.props.isHidden,
});
}
}
handleHideBalanceIconClick() {
this.setState(previousState => ({
isHidden: !previousState.isHidden,

@ -1,15 +1,18 @@
import React, { PureComponent } from 'react';
import styled from 'styled-components';
import { FormattedMessage } from 'react-intl';
import ColorHash from 'color-hash';
import ScaleText from 'react-scale-text';
import colors from 'config/colors';
import { FONT_WEIGHT } from 'config/variables';
import Button from 'components/Button';
import Tooltip from 'components/Tooltip';
import Icon from 'components/Icon';
import ICONS from 'config/icons';
import * as stateUtils from 'reducers/utils';
import BigNumber from 'bignumber.js';
import PropTypes from 'prop-types';
import l10nCommonMessages from 'views/common.messages';
const TokenWrapper = styled.div`
padding: 14px 0;
@ -60,6 +63,10 @@ const RemoveTokenButton = styled(Button)`
padding: 0 0 0 10px;
`;
const TooltipIcon = styled(Icon)`
cursor: pointer;
`;
class AddedToken extends PureComponent {
getTokenBalance(token) {
const pendingAmount = stateUtils.getPendingAmount(this.props.pending, token.symbol, true);
@ -84,7 +91,25 @@ class AddedToken extends PureComponent {
<TokenName>{this.props.token.name}</TokenName>
<TokenBalance>
{this.getTokenBalance(this.props.token)} {this.props.token.symbol}
{this.props.hideBalance ? (
<Tooltip
maxWidth={200}
placement="top"
content={
<FormattedMessage
{...l10nCommonMessages.TR_THE_ACCOUNT_BALANCE_IS_HIDDEN}
/>
}
>
<TooltipIcon
icon={ICONS.EYE_CROSSED}
size={25}
color={colors.TEXT_SECONDARY}
/>
</Tooltip>
) : (
`${this.getTokenBalance(this.props.token)} ${this.props.token.symbol}`
)}
</TokenBalance>
<RemoveTokenButton
isTransparent
@ -101,6 +126,7 @@ AddedToken.propTypes = {
token: PropTypes.object,
pending: PropTypes.array,
removeToken: PropTypes.func,
hideBalance: PropTypes.bool,
};
export default AddedToken;

@ -105,6 +105,7 @@ const AccountSummary = (props: Props) => {
balance={balance}
fiat={props.fiat}
localCurrency={props.wallet.localCurrency}
isHidden={props.wallet.hideBalance}
/>
<H2Wrapper>
<H2>
@ -164,6 +165,7 @@ const AccountSummary = (props: Props) => {
token={token}
pending={pending}
removeToken={props.removeToken}
hideBalance={props.wallet.hideBalance}
/>
))}
</AddedTokensWrapper>

@ -17,6 +17,7 @@ type Props = {
reserve: string,
fiat: $ElementType<ReducersState, 'fiat'>,
localCurrency: string,
isHidden: boolean,
};
type State = {
@ -105,11 +106,21 @@ class AccountBalance extends PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isHidden: false,
canAnimateHideBalanceIcon: false,
isHidden: props.isHidden,
canAnimateHideBalanceIcon: props.isHidden,
};
}
componentDidUpdate(prevProps: Props) {
console.log(this.props.isHidden);
if (prevProps.isHidden !== this.props.isHidden) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
isHidden: this.props.isHidden,
});
}
}
handleHideBalanceIconClick() {
this.setState(previousState => ({
isHidden: !previousState.isHidden,

@ -86,6 +86,7 @@ const AccountSummary = (props: Props) => {
reserve={reserve}
fiat={props.fiat}
localCurrency={props.wallet.localCurrency}
isHidden={props.wallet.hideBalance}
/>
{TMP_SHOW_HISTORY && (
<H2Wrapper>

@ -18,6 +18,7 @@ type StateProps = {
type DispatchProps = {
setLocalCurrency: typeof WalletActions.setLocalCurrency,
setHideBalance: typeof WalletActions.setHideBalance,
};
export type Props = StateProps & DispatchProps;
@ -34,6 +35,7 @@ const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps>
dispatch: Dispatch
): DispatchProps => ({
setLocalCurrency: bindActionCreators(WalletActions.setLocalCurrency, dispatch),
setHideBalance: bindActionCreators(WalletActions.setHideBalance, dispatch),
});
export default injectIntl(

@ -3,24 +3,38 @@ import styled from 'styled-components';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import Link from 'components/Link';
import Content from 'views/Wallet/components/Content';
import { Select } from 'components/Select';
import Button from 'components/Button';
import colors from 'config/colors';
import {
Switch,
Select,
Link,
Button,
Tooltip,
Icon,
icons as ICONS,
colors,
} from 'trezor-ui-components';
import { FIAT_CURRENCIES } from 'config/app';
import { FONT_SIZE } from 'config/variables';
import l10nCommonMessages from 'views/common.messages';
import l10nMessages from './index.messages';
import type { Props } from './Container';
const StyledContent = styled(Content)`
max-width: 800px;
`;
const CurrencySelect = styled(Select)`
min-width: 77px;
/* max-width: 200px; */
`;
const CurrencyLabel = styled.div`
const Label = styled.div`
display: flex;
color: ${colors.TEXT_SECONDARY};
align-items: center;
`;
const LabelTop = styled.div`
color: ${colors.TEXT_SECONDARY};
padding-bottom: 10px;
`;
@ -29,8 +43,15 @@ const Section = styled.div`
margin-bottom: 20px;
`;
const Row = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`;
const Actions = styled.div`
display: flex;
margin-top: 40px;
`;
const Buttons = styled.div`
@ -45,16 +66,20 @@ const Info = styled.div`
align-self: center;
`;
const TooltipIcon = styled(Icon)`
cursor: pointer;
`;
const buildCurrencyOption = currency => {
return { value: currency, label: currency.toUpperCase() };
};
const WalletSettings = (props: Props) => (
<Content>
<StyledContent>
<Section>
<CurrencyLabel>
<LabelTop>
<FormattedMessage {...l10nMessages.TR_LOCAL_CURRENCY} />
</CurrencyLabel>
</LabelTop>
<CurrencySelect
isSearchable
isClearable={false}
@ -63,6 +88,26 @@ const WalletSettings = (props: Props) => (
options={FIAT_CURRENCIES.map(c => buildCurrencyOption(c))}
/>
</Section>
<Section>
<Row>
<Label>
<FormattedMessage {...l10nCommonMessages.TR_HIDE_BALANCE} />
<Tooltip
content={<FormattedMessage {...l10nMessages.TR_HIDE_BALANCE_EXPLAINED} />}
maxWidth={210}
placement="right"
>
<TooltipIcon icon={ICONS.HELP} color={colors.TEXT_SECONDARY} size={24} />
</Tooltip>
</Label>
<Switch
onChange={checked => {
props.setHideBalance(checked);
}}
checked={props.wallet.hideBalance}
/>
</Row>
</Section>
<Actions>
<Info>
<FormattedMessage {...l10nMessages.TR_THE_CHANGES_ARE_SAVED} />
@ -75,7 +120,7 @@ const WalletSettings = (props: Props) => (
</Link>
</Buttons>
</Actions>
</Content>
</StyledContent>
);
export default WalletSettings;

@ -7,6 +7,11 @@ const definedMessages: Messages = defineMessages({
id: 'TR_LOCAL_CURRENCY',
defaultMessage: 'Local currency',
},
TR_HIDE_BALANCE_EXPLAINED: {
id: 'TR_HIDE_BALANCE_EXPLAINED',
defaultMessage:
"Hides your account balance so you don't have to worry about anyone looking over your shoulder.",
},
TR_THE_CHANGES_ARE_SAVED: {
id: 'TR_THE_CHANGES_ARE_SAVED',
defaultMessage: 'The changes are saved automatically as they are made',

@ -70,6 +70,14 @@ const definedMessages: Messages = defineMessages({
id: 'TR_CLOSE',
defaultMessage: 'Close',
},
TR_HIDE_BALANCE: {
id: 'TR_HIDE_BALANCE',
defaultMessage: 'Hide balance',
},
TR_THE_ACCOUNT_BALANCE_IS_HIDDEN: {
id: 'TR_THE_ACCOUNT_BALANCE_IS_HIDDEN',
defaultMessage: 'The account balance is hidden.',
},
});
export default definedMessages;

@ -8844,6 +8844,15 @@ prop-types@^15.6.2:
loose-envify "^1.3.1"
object-assign "^4.1.1"
prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"
protobufjs@^6.8.8:
version "6.8.8"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c"
@ -9169,6 +9178,11 @@ react-is@^16.3.2, react-is@^16.6.0, react-is@^16.6.3:
version "16.6.3"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0"
react-is@^16.8.1:
version "16.8.4"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2"
integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==
react-json-view@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.19.1.tgz#95d8e59e024f08a25e5dc8f076ae304eed97cf5c"
@ -9257,6 +9271,13 @@ react-select@^2.3.0:
react-input-autosize "^2.2.1"
react-transition-group "^2.2.1"
react-switch@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/react-switch/-/react-switch-4.1.0.tgz#93379df044c9febe86d0b1485cb14b2a364562b6"
integrity sha512-mINolJx85ZojtsKdH+DE+p9i073a1mQscEADF6y/FLBshSmyciZffBWP0xu8jWyqRIg7tWykFLJt+2xlMya7sw==
dependencies:
prop-types "^15.6.2"
react-textarea-autosize@^6.1.0:
version "6.1.0"
resolved "http://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz#df91387f8a8f22020b77e3833c09829d706a09a5"
@ -11154,6 +11175,21 @@ trezor-translations-manager@^1.0.4:
request "^2.88.0"
request-promise-native "^1.0.5"
trezor-ui-components@^1.0.0-beta.4:
version "1.0.0-beta.4"
resolved "https://registry.yarnpkg.com/trezor-ui-components/-/trezor-ui-components-1.0.0-beta.4.tgz#e68185775e13a130076e548a68ba61053f1053bf"
integrity sha512-ikH/k8p4rVonfDqqo8NjZPE3N73aq3P4lk6cvN1XwRSP70lsGELG5qoU71eQsThGsN1dioxhOxWA7HVAazHvDw==
dependencies:
prop-types "^15.7.2"
rc-tooltip "^3.7.3"
react "^16.8.1"
react-dom "^16.8.1"
react-router-dom "^4.3.1"
react-select "^2.3.0"
react-switch "^4.1.0"
react-textarea-autosize "^7.1.0"
styled-components "^4.1.3"
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"

Loading…
Cancel
Save