mirror of https://github.com/trezor/trezor-wallet
Merge pull request #448 from trezor/feature/currency-switcher
Feature/Fiat currency switcherpull/453/head
commit
0ee49b93e4
@ -0,0 +1,68 @@
|
||||
export const LANGUAGE = [
|
||||
{ code: 'en', name: 'English', en: 'English' },
|
||||
{ code: 'bn', name: 'Bengali', en: 'Bengali' },
|
||||
{ code: 'cs', name: 'Česky', en: 'Czech' },
|
||||
{ code: 'de', name: 'Deutsch', en: 'German' },
|
||||
{ code: 'el', name: 'Ελληνικά', en: 'Greek' },
|
||||
{ code: 'es', name: 'Español', en: 'Spanish' },
|
||||
{ code: 'fr', name: 'Français', en: 'French' },
|
||||
{ code: 'id', name: 'Bahasa Indonesia', en: 'Indonesian' },
|
||||
{ code: 'it', name: 'Italiano', en: 'Italian' },
|
||||
{ code: 'ja', name: '日本語', en: 'Japanese' },
|
||||
{ code: 'nl', name: 'Nederlands', en: 'Dutch' },
|
||||
{ code: 'pl', name: 'Polski', en: 'Polish' },
|
||||
{ code: 'pt', name: 'Português', en: 'Portuguese' },
|
||||
{ code: 'ru', name: 'Русский', en: 'Russian' },
|
||||
{ code: 'uk', name: 'Український', en: 'Ukrainian' },
|
||||
{ code: 'zh', name: '中文(简体)', en: 'Chinese Simplified' },
|
||||
{ code: 'zh_TW', name: '中文(台灣)', en: 'Chinese Traditional' },
|
||||
];
|
||||
|
||||
export const FIAT_CURRENCIES = [
|
||||
'usd',
|
||||
'aed',
|
||||
'ars',
|
||||
'aud',
|
||||
'bdt',
|
||||
'bhd',
|
||||
'bmd',
|
||||
'brl',
|
||||
'cad',
|
||||
'chf',
|
||||
'clp',
|
||||
'cny',
|
||||
'czk',
|
||||
'dkk',
|
||||
'eur',
|
||||
'gbp',
|
||||
'hkd',
|
||||
'huf',
|
||||
'idr',
|
||||
'ils',
|
||||
'inr',
|
||||
'jpy',
|
||||
'krw',
|
||||
'kwd',
|
||||
'lkr',
|
||||
'mmk',
|
||||
'mxn',
|
||||
'myr',
|
||||
'nok',
|
||||
'nzd',
|
||||
'php',
|
||||
'pkr',
|
||||
'pln',
|
||||
'rub',
|
||||
'sar',
|
||||
'sek',
|
||||
'sgd',
|
||||
'thb',
|
||||
'try',
|
||||
'twd',
|
||||
'vef',
|
||||
'vnd',
|
||||
'zar',
|
||||
'xdr',
|
||||
'xag',
|
||||
'xau',
|
||||
];
|
@ -1,18 +0,0 @@
|
||||
import * as utils from '../cryptoUriParser';
|
||||
|
||||
describe('crypto uri parser', () => {
|
||||
it('parseUri', () => {
|
||||
expect(utils.parseUri('http://www.trezor.io')).toEqual({ address: '//www.trezor.io' }); // TODO: Error in function
|
||||
expect(utils.parseUri('www.trezor.io')).toEqual({ address: 'www.trezor.io' });
|
||||
expect(utils.parseUri('www.trezor.io/TT')).toEqual({ address: 'www.trezor.io/TT' });
|
||||
expect(utils.parseUri('www.trezor.io/TT?param1=aha')).toEqual({
|
||||
address: 'www.trezor.io/TT',
|
||||
param1: 'aha',
|
||||
});
|
||||
expect(utils.parseUri('www.trezor.io/TT?param1=aha¶m2=hah')).toEqual({
|
||||
address: 'www.trezor.io/TT',
|
||||
param1: 'aha',
|
||||
param2: 'hah',
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,114 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
import * as utils from '../fiatConverter';
|
||||
|
||||
describe('fiatConverter utils: toFiatCurrency', () => {
|
||||
const ratesETH = {
|
||||
network: 'eth',
|
||||
rates: {
|
||||
czk: 3007.1079886708517,
|
||||
eos: 36.852136278995445,
|
||||
eur: 117.13118845579191,
|
||||
gbp: 100.43721437661289,
|
||||
},
|
||||
};
|
||||
|
||||
it('to existing fiat currency', () => {
|
||||
expect(utils.toFiatCurrency('1', 'czk', ratesETH)).toBe('3007.11');
|
||||
expect(utils.toFiatCurrency('0', 'czk', ratesETH)).toBe('0.00');
|
||||
expect(utils.toFiatCurrency('1.00000000000', 'czk', ratesETH)).toBe('3007.11');
|
||||
expect(utils.toFiatCurrency('0.12345678910111213', 'eur', ratesETH)).toBe('14.46');
|
||||
});
|
||||
|
||||
it('to missing fiat currency', () => {
|
||||
expect(utils.toFiatCurrency('1', 'usd', ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency('0', 'usd', ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency('1.00000000000', 'usd', ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency('0.12345678910111213', 'usd', ratesETH)).toBe('');
|
||||
});
|
||||
|
||||
it('non-numeric amount to fiat currency', () => {
|
||||
expect(utils.toFiatCurrency(undefined, 'czk', ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency(null, 'czk', ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency('12133.3131.3141.4', 'czk', ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency(BigNumber('nanbla'), 'czk', ratesETH)).toBe('');
|
||||
});
|
||||
|
||||
it('with null/undefined/empty rates', () => {
|
||||
expect(utils.toFiatCurrency('1', 'czk', {})).toBe('');
|
||||
expect(utils.toFiatCurrency('1', 'czk', null)).toBe('');
|
||||
expect(utils.toFiatCurrency('1', 'czk', undefined)).toBe('');
|
||||
});
|
||||
|
||||
it('with null/undefined/empty currency', () => {
|
||||
expect(utils.toFiatCurrency('1', {}, ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency('1', null, ratesETH)).toBe('');
|
||||
expect(utils.toFiatCurrency('1', undefined, ratesETH)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fiatConverter utils: fromFiatCurrency', () => {
|
||||
const ratesETH = {
|
||||
network: 'eth',
|
||||
rates: {
|
||||
czk: 3007.1079886708517,
|
||||
eos: 36.852136278995445,
|
||||
eur: 117.13118845579191,
|
||||
gbp: 100.43721437661289,
|
||||
},
|
||||
};
|
||||
const decimals = 18;
|
||||
|
||||
it('from existing fiat currency', () => {
|
||||
expect(utils.fromFiatCurrency('3007.1079886708517', 'czk', ratesETH, decimals)).toBe(
|
||||
'1.000000000000000000'
|
||||
);
|
||||
expect(utils.fromFiatCurrency('0', 'czk', ratesETH, decimals)).toBe('0.000000000000000000');
|
||||
expect(utils.fromFiatCurrency('3007.1079886708517', 'czk', ratesETH, decimals)).toBe(
|
||||
'1.000000000000000000'
|
||||
);
|
||||
expect(utils.fromFiatCurrency('117.13118845579191', 'eur', ratesETH, decimals)).toBe(
|
||||
'1.000000000000000000'
|
||||
);
|
||||
});
|
||||
|
||||
it('from missing fiat currency', () => {
|
||||
expect(utils.fromFiatCurrency('1', 'usd', ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('0', 'usd', ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('1.00000000000', 'usd', ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('0.12345678910111213', 'usd', ratesETH, decimals)).toBe('');
|
||||
});
|
||||
|
||||
it('non-numeric amount to fiat currency', () => {
|
||||
expect(utils.fromFiatCurrency(undefined, 'czk', ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency(null, 'czk', ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('12133.3131.3141.4', 'czk', ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency(BigNumber('nanbla'), 'czk', ratesETH, decimals)).toBe('');
|
||||
});
|
||||
|
||||
it('with null/undefined/empty rates', () => {
|
||||
expect(utils.fromFiatCurrency('1', 'czk', {}, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('1', 'czk', null, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('1', 'czk', undefined, decimals)).toBe('');
|
||||
});
|
||||
|
||||
it('with null/undefined/empty currency', () => {
|
||||
expect(utils.fromFiatCurrency('1', {}, ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('1', null, ratesETH, decimals)).toBe('');
|
||||
expect(utils.fromFiatCurrency('1', undefined, ratesETH, decimals)).toBe('');
|
||||
});
|
||||
|
||||
it('different decimals', () => {
|
||||
expect(utils.fromFiatCurrency('3007.1079886708517', 'czk', ratesETH, 1)).toBe('1.0');
|
||||
expect(utils.fromFiatCurrency('0', 'czk', ratesETH, 0)).toBe('0');
|
||||
expect(utils.fromFiatCurrency('3007.1079886708517', 'czk', ratesETH, 5)).toBe('1.00000');
|
||||
});
|
||||
|
||||
it('from fiat currency with comma decimal separator', () => {
|
||||
expect(utils.fromFiatCurrency('3007,1079886708517', 'czk', ratesETH, decimals)).toBe(
|
||||
'1.000000000000000000'
|
||||
);
|
||||
expect(utils.fromFiatCurrency('117,13118845579191', 'eur', ratesETH, decimals)).toBe(
|
||||
'1.000000000000000000'
|
||||
);
|
||||
});
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
const toFiatCurrency = (amount, fiatCurrency, networkRates) => {
|
||||
// calculate amount in local currency
|
||||
if (!networkRates || !networkRates.rates || !amount) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const rate = networkRates.rates[fiatCurrency];
|
||||
if (!rate) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let formattedAmount = amount;
|
||||
if (typeof amount === 'string') {
|
||||
formattedAmount = amount.replace(',', '.');
|
||||
}
|
||||
|
||||
let localAmount = BigNumber(formattedAmount).times(rate);
|
||||
localAmount = localAmount.isNaN() ? '' : localAmount.toFixed(2);
|
||||
return localAmount;
|
||||
};
|
||||
|
||||
const fromFiatCurrency = (localAmount, fiatCurrency, networkRates, decimals) => {
|
||||
if (!networkRates || !networkRates.rates || !localAmount) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const rate = networkRates.rates[fiatCurrency];
|
||||
if (!rate) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let formattedLocalAmount = localAmount;
|
||||
if (typeof localAmount === 'string') {
|
||||
formattedLocalAmount = localAmount.replace(',', '.');
|
||||
}
|
||||
|
||||
let amount = BigNumber(formattedLocalAmount).div(rate);
|
||||
amount = amount.isNaN() ? '' : amount.toFixed(decimals);
|
||||
return amount;
|
||||
};
|
||||
|
||||
export { toFiatCurrency, fromFiatCurrency };
|
@ -0,0 +1,56 @@
|
||||
import styled from 'styled-components';
|
||||
import React from 'react';
|
||||
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||
import { getPattern } from 'support/routes';
|
||||
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
import colors from 'config/colors';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import l10nCommonMessages from 'views/common.messages';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
padding: 0px 30px 0 35px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
`;
|
||||
|
||||
const StyledNavLink = styled(NavLink)`
|
||||
font-weight: ${FONT_WEIGHT.MEDIUM};
|
||||
font-size: ${FONT_SIZE.TOP_MENU};
|
||||
color: ${colors.TEXT_SECONDARY};
|
||||
margin: 0px 4px;
|
||||
padding: 20px 10px;
|
||||
white-space: nowrap;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
transition: all 0.3s ease-in-out;
|
||||
color: ${colors.TEXT_PRIMARY};
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
// TODO: make universal TopNavigation component
|
||||
const TopNavigationWalletSettings = () => (
|
||||
<Wrapper>
|
||||
<StyledNavLink exact to={getPattern('wallet-settings')}>
|
||||
<FormattedMessage {...l10nCommonMessages.TR_APPLICATION_SETTINGS} />
|
||||
</StyledNavLink>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
export default TopNavigationWalletSettings;
|
@ -0,0 +1,44 @@
|
||||
/* @flow */
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { injectIntl } from 'react-intl';
|
||||
|
||||
import * as WalletActions from 'actions/WalletActions';
|
||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||
import type { State, Dispatch } from 'flowtype';
|
||||
import WalletSettings from './index';
|
||||
|
||||
type OwnProps = {};
|
||||
|
||||
type StateProps = {
|
||||
wallet: $ElementType<State, 'wallet'>,
|
||||
fiat: $ElementType<State, 'fiat'>,
|
||||
localStorage: $ElementType<State, 'localStorage'>,
|
||||
};
|
||||
|
||||
type DispatchProps = {
|
||||
setLocalCurrency: typeof WalletActions.setLocalCurrency,
|
||||
};
|
||||
|
||||
export type Props = StateProps & DispatchProps;
|
||||
|
||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (
|
||||
state: State
|
||||
): StateProps => ({
|
||||
wallet: state.wallet,
|
||||
fiat: state.fiat,
|
||||
localStorage: state.localStorage,
|
||||
});
|
||||
|
||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (
|
||||
dispatch: Dispatch
|
||||
): DispatchProps => ({
|
||||
setLocalCurrency: bindActionCreators(WalletActions.setLocalCurrency, dispatch),
|
||||
});
|
||||
|
||||
export default injectIntl(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(WalletSettings)
|
||||
);
|
@ -1,47 +1,81 @@
|
||||
/* @flow */
|
||||
import styled from 'styled-components';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import colors from 'config/colors';
|
||||
import icons from 'config/icons';
|
||||
|
||||
import Content from 'views/Wallet/components/Content';
|
||||
import { H1 } from 'components/Heading';
|
||||
import Icon from 'components/Icon';
|
||||
import Link from 'components/Link';
|
||||
import Content from 'views/Wallet/components/Content';
|
||||
import { Select } from 'components/Select';
|
||||
import Button from 'components/Button';
|
||||
|
||||
const Section = styled.section`
|
||||
import colors from 'config/colors';
|
||||
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 CurrencySelect = styled(Select)`
|
||||
min-width: 77px;
|
||||
/* max-width: 200px; */
|
||||
`;
|
||||
|
||||
const CurrencyLabel = styled.div`
|
||||
color: ${colors.TEXT_SECONDARY};
|
||||
padding-bottom: 10px;
|
||||
`;
|
||||
|
||||
const Section = styled.div`
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const Actions = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const Row = styled.div`
|
||||
const Buttons = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 50px 0;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
const StyledH1 = styled(H1)`
|
||||
text-align: center;
|
||||
const Info = styled.div`
|
||||
flex: 1;
|
||||
color: ${colors.TEXT_SECONDARY};
|
||||
font-size: ${FONT_SIZE.SMALL};
|
||||
align-self: center;
|
||||
`;
|
||||
|
||||
const WalletSettings = () => (
|
||||
const buildCurrencyOption = currency => {
|
||||
return { value: currency, label: currency.toUpperCase() };
|
||||
};
|
||||
|
||||
const WalletSettings = (props: Props) => (
|
||||
<Content>
|
||||
<Section>
|
||||
<Row>
|
||||
<Icon size={60} color={colors.WARNING_PRIMARY} icon={icons.WARNING} />
|
||||
<StyledH1>Wallet settings is under construction</StyledH1>
|
||||
<CurrencyLabel>
|
||||
<FormattedMessage {...l10nMessages.TR_LOCAL_CURRENCY} />
|
||||
</CurrencyLabel>
|
||||
<CurrencySelect
|
||||
isSearchable
|
||||
isClearable={false}
|
||||
onChange={option => props.setLocalCurrency(option.value)}
|
||||
value={buildCurrencyOption(props.wallet.localCurrency)}
|
||||
options={FIAT_CURRENCIES.map(c => buildCurrencyOption(c))}
|
||||
/>
|
||||
</Section>
|
||||
<Actions>
|
||||
<Info>
|
||||
<FormattedMessage {...l10nMessages.TR_THE_CHANGES_ARE_SAVED} />
|
||||
</Info>
|
||||
<Buttons>
|
||||
<Link to="/">
|
||||
<Button>Take me back</Button>
|
||||
<Button isGreen>
|
||||
<FormattedMessage {...l10nCommonMessages.TR_CLOSE} />
|
||||
</Button>
|
||||
</Link>
|
||||
</Row>
|
||||
</Section>
|
||||
</Buttons>
|
||||
</Actions>
|
||||
</Content>
|
||||
);
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
null
|
||||
)(WalletSettings);
|
||||
export default WalletSettings;
|
||||
|
@ -0,0 +1,16 @@
|
||||
/* @flow */
|
||||
import { defineMessages } from 'react-intl';
|
||||
import type { Messages } from 'flowtype/npm/react-intl';
|
||||
|
||||
const definedMessages: Messages = defineMessages({
|
||||
TR_LOCAL_CURRENCY: {
|
||||
id: 'TR_LOCAL_CURRENCY',
|
||||
defaultMessage: 'Local currency',
|
||||
},
|
||||
TR_THE_CHANGES_ARE_SAVED: {
|
||||
id: 'TR_THE_CHANGES_ARE_SAVED',
|
||||
defaultMessage: 'The changes are saved automatically as they are made',
|
||||
},
|
||||
});
|
||||
|
||||
export default definedMessages;
|
Loading…
Reference in new issue