mirror of
https://github.com/trezor/trezor-wallet
synced 2025-01-08 07:01:04 +00:00
Merge branch 'feature/currency-switcher' into feature/stealth-mode
This commit is contained in:
commit
5ab40c11fa
@ -57,6 +57,7 @@ const KEY_TOKENS: string = `${STORAGE_PATH}tokens`;
|
||||
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`;
|
||||
|
||||
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
|
||||
// or
|
||||
@ -276,6 +277,11 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
} else {
|
||||
dispatch(WalletActions.fetchLocale(l10nUtils.getInitialLocale()));
|
||||
}
|
||||
|
||||
const localCurrency: ?string = storageUtils.get(TYPE, KEY_LOCAL_CURRENCY);
|
||||
if (localCurrency) {
|
||||
dispatch(WalletActions.setLocalCurrency(JSON.parse(localCurrency)));
|
||||
}
|
||||
};
|
||||
|
||||
export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
@ -296,3 +302,11 @@ export const setLanguage = (): ThunkAction => (dispatch: Dispatch, getState: Get
|
||||
const { language } = getState().wallet;
|
||||
storageUtils.set(TYPE, KEY_LANGUAGE, JSON.stringify(language));
|
||||
};
|
||||
|
||||
export const setLocalCurrency = (): ThunkAction => (
|
||||
dispatch: Dispatch,
|
||||
getState: GetState
|
||||
): void => {
|
||||
const { localCurrency } = getState().wallet;
|
||||
storageUtils.set(TYPE, KEY_LOCAL_CURRENCY, JSON.stringify(localCurrency));
|
||||
};
|
||||
|
@ -58,6 +58,10 @@ export type WalletAction =
|
||||
type: typeof WALLET.SET_LANGUAGE,
|
||||
locale: string,
|
||||
messages: { [string]: string },
|
||||
}
|
||||
| {
|
||||
type: typeof WALLET.SET_LOCAL_CURRENCY,
|
||||
localCurrency: string,
|
||||
};
|
||||
|
||||
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||
@ -96,6 +100,11 @@ export const fetchLocale = (locale: string): ThunkAction => (dispatch: Dispatch)
|
||||
});
|
||||
};
|
||||
|
||||
export const setLocalCurrency = (localCurrency: string): WalletAction => ({
|
||||
type: WALLET.SET_LOCAL_CURRENCY,
|
||||
localCurrency: localCurrency.toLowerCase(),
|
||||
});
|
||||
|
||||
// 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
|
||||
|
@ -17,3 +17,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';
|
||||
|
@ -10,6 +10,7 @@ import * as WEB3 from 'actions/constants/web3';
|
||||
import { initialState } from 'reducers/SendFormEthereumReducer';
|
||||
import * as reducerUtils from 'reducers/utils';
|
||||
import * as ethUtils from 'utils/ethUtils';
|
||||
import { toFiatCurrency, fromFiatCurrency } from 'utils/fiatConverter';
|
||||
|
||||
import type {
|
||||
Dispatch,
|
||||
@ -150,6 +151,9 @@ export const init = (): AsyncAction => async (
|
||||
initialState.selectedFeeLevel
|
||||
);
|
||||
|
||||
// initial local currency is set according to wallet settings
|
||||
const { localCurrency } = getState().wallet;
|
||||
|
||||
dispatch({
|
||||
type: SEND.INIT,
|
||||
networkType: 'ethereum',
|
||||
@ -158,6 +162,7 @@ export const init = (): AsyncAction => async (
|
||||
networkName: network.shortcut,
|
||||
networkSymbol: network.symbol,
|
||||
currency: network.symbol,
|
||||
localCurrency,
|
||||
feeLevels,
|
||||
selectedFeeLevel,
|
||||
recommendedGasPrice: gasPrice.toString(),
|
||||
@ -200,6 +205,9 @@ export const onClear = (): AsyncAction => async (
|
||||
initialState.selectedFeeLevel
|
||||
);
|
||||
|
||||
// initial local currency is set according to wallet settings
|
||||
const { localCurrency } = getState().wallet;
|
||||
|
||||
dispatch({
|
||||
type: SEND.CLEAR,
|
||||
networkType: 'ethereum',
|
||||
@ -208,6 +216,7 @@ export const onClear = (): AsyncAction => async (
|
||||
networkName: network.shortcut,
|
||||
networkSymbol: network.symbol,
|
||||
currency: network.symbol,
|
||||
localCurrency,
|
||||
feeLevels,
|
||||
selectedFeeLevel,
|
||||
recommendedGasPrice: gasPrice.toString(),
|
||||
@ -241,11 +250,12 @@ export const onAddressChange = (address: string): ThunkAction => (
|
||||
/*
|
||||
* Called from UI on "amount" field change
|
||||
*/
|
||||
export const onAmountChange = (amount: string): ThunkAction => (
|
||||
dispatch: Dispatch,
|
||||
getState: GetState
|
||||
): void => {
|
||||
export const onAmountChange = (
|
||||
amount: string,
|
||||
shouldUpdateLocalAmount: boolean = true
|
||||
): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
const state = getState().sendFormEthereum;
|
||||
|
||||
dispatch({
|
||||
type: SEND.CHANGE,
|
||||
networkType: 'ethereum',
|
||||
@ -257,6 +267,67 @@ export const onAmountChange = (amount: string): ThunkAction => (
|
||||
amount,
|
||||
},
|
||||
});
|
||||
|
||||
if (shouldUpdateLocalAmount) {
|
||||
const { localCurrency } = getState().sendFormEthereum;
|
||||
const fiatRates = getState().fiat.find(f => f.network === state.currency.toLowerCase());
|
||||
const localAmount = toFiatCurrency(amount, localCurrency, fiatRates);
|
||||
dispatch(onLocalAmountChange(localAmount, false));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Called from UI on "localAmount" field change
|
||||
*/
|
||||
export const onLocalAmountChange = (
|
||||
localAmount: string,
|
||||
shouldUpdateAmount: boolean = true
|
||||
): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
const state = getState().sendFormEthereum;
|
||||
const { localCurrency } = getState().sendFormEthereum;
|
||||
const { network } = getState().selectedAccount;
|
||||
|
||||
// updates localAmount
|
||||
dispatch({
|
||||
type: SEND.CHANGE,
|
||||
networkType: 'ethereum',
|
||||
state: {
|
||||
...state,
|
||||
untouched: false,
|
||||
touched: { ...state.touched, localAmount: true },
|
||||
setMax: false,
|
||||
localAmount,
|
||||
},
|
||||
});
|
||||
|
||||
// updates amount
|
||||
if (shouldUpdateAmount) {
|
||||
if (!network) return;
|
||||
// converts amount in local currency to crypto currency that will be sent
|
||||
const fiatRates = getState().fiat.find(f => f.network === state.currency.toLowerCase());
|
||||
const amount = fromFiatCurrency(localAmount, localCurrency, fiatRates, network.decimals);
|
||||
dispatch(onAmountChange(amount, false));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Called from UI on "localCurrency" selection change
|
||||
*/
|
||||
export const onLocalCurrencyChange = (localCurrency: {
|
||||
value: string,
|
||||
label: string,
|
||||
}): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
const state = getState().sendFormEthereum;
|
||||
dispatch({
|
||||
type: SEND.CHANGE,
|
||||
networkType: 'ethereum',
|
||||
state: {
|
||||
...state,
|
||||
localCurrency: localCurrency.value,
|
||||
},
|
||||
});
|
||||
// Recalculates local amount with new currency rates
|
||||
dispatch(onAmountChange(state.amount, true));
|
||||
};
|
||||
|
||||
/*
|
||||
@ -298,6 +369,9 @@ export const onCurrencyChange = (currency: { value: string, label: string }): Th
|
||||
gasLimit,
|
||||
},
|
||||
});
|
||||
|
||||
// Recalculates local amount with new currency rates
|
||||
dispatch(onAmountChange(state.amount, true));
|
||||
};
|
||||
|
||||
/*
|
||||
@ -756,7 +830,9 @@ export default {
|
||||
toggleAdvanced,
|
||||
onAddressChange,
|
||||
onAmountChange,
|
||||
onLocalAmountChange,
|
||||
onCurrencyChange,
|
||||
onLocalCurrencyChange,
|
||||
onSetMax,
|
||||
onFeeLevelChange,
|
||||
updateFeeLevels,
|
||||
|
@ -4,6 +4,7 @@ import BigNumber from 'bignumber.js';
|
||||
import EthereumjsUtil from 'ethereumjs-util';
|
||||
import EthereumjsUnits from 'ethereumjs-units';
|
||||
import { findDevice, getPendingAmount, findToken } from 'reducers/utils';
|
||||
import { toFiatCurrency } from 'utils/fiatConverter';
|
||||
import * as SEND from 'actions/constants/send';
|
||||
import * as ethUtils from 'utils/ethUtils';
|
||||
|
||||
@ -130,6 +131,14 @@ export const recalculateTotalAmount = ($state: State): PayloadAction<State> => (
|
||||
} else {
|
||||
const b = new BigNumber(account.balance).minus(pendingAmount);
|
||||
state.amount = calculateMaxAmount(b, state.gasPrice, state.gasLimit);
|
||||
|
||||
// calculate amount in local currency
|
||||
const { localCurrency } = getState().sendFormEthereum;
|
||||
const fiatRates = getState().fiat.find(f => f.network === state.networkName);
|
||||
const localAmount = toFiatCurrency(state.amount, localCurrency, fiatRates);
|
||||
if (localAmount) {
|
||||
state.localAmount = localAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import * as BLOCKCHAIN from 'actions/constants/blockchain';
|
||||
import { initialState } from 'reducers/SendFormRippleReducer';
|
||||
import * as reducerUtils from 'reducers/utils';
|
||||
import { fromDecimalAmount } from 'utils/formatUtils';
|
||||
import { toFiatCurrency, fromFiatCurrency } from 'utils/fiatConverter';
|
||||
|
||||
import type {
|
||||
Dispatch,
|
||||
@ -109,6 +110,9 @@ export const init = (): AsyncAction => async (
|
||||
initialState.selectedFeeLevel
|
||||
);
|
||||
|
||||
// initial local currency is set according to wallet settings
|
||||
const { localCurrency } = getState().wallet;
|
||||
|
||||
dispatch({
|
||||
type: SEND.INIT,
|
||||
networkType: 'ripple',
|
||||
@ -116,6 +120,7 @@ export const init = (): AsyncAction => async (
|
||||
...initialState,
|
||||
networkName: network.shortcut,
|
||||
networkSymbol: network.symbol,
|
||||
localCurrency,
|
||||
feeLevels,
|
||||
selectedFeeLevel,
|
||||
fee: network.fee.defaultFee,
|
||||
@ -154,6 +159,9 @@ export const onClear = (): AsyncAction => async (
|
||||
initialState.selectedFeeLevel
|
||||
);
|
||||
|
||||
// initial local currency is set according to wallet settings
|
||||
const { localCurrency } = getState().wallet;
|
||||
|
||||
dispatch({
|
||||
type: SEND.CLEAR,
|
||||
networkType: 'ripple',
|
||||
@ -161,6 +169,7 @@ export const onClear = (): AsyncAction => async (
|
||||
...initialState,
|
||||
networkName: network.shortcut,
|
||||
networkSymbol: network.symbol,
|
||||
localCurrency,
|
||||
feeLevels,
|
||||
selectedFeeLevel,
|
||||
fee: network.fee.defaultFee,
|
||||
@ -193,10 +202,10 @@ export const onAddressChange = (address: string): ThunkAction => (
|
||||
/*
|
||||
* Called from UI on "amount" field change
|
||||
*/
|
||||
export const onAmountChange = (amount: string): ThunkAction => (
|
||||
dispatch: Dispatch,
|
||||
getState: GetState
|
||||
): void => {
|
||||
export const onAmountChange = (
|
||||
amount: string,
|
||||
shouldUpdateLocalAmount: boolean = true
|
||||
): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
const state = getState().sendFormRipple;
|
||||
dispatch({
|
||||
type: SEND.CHANGE,
|
||||
@ -209,6 +218,67 @@ export const onAmountChange = (amount: string): ThunkAction => (
|
||||
amount,
|
||||
},
|
||||
});
|
||||
|
||||
if (shouldUpdateLocalAmount) {
|
||||
const { localCurrency } = getState().sendFormRipple;
|
||||
const fiatRates = getState().fiat.find(f => f.network === state.networkName);
|
||||
const localAmount = toFiatCurrency(amount, localCurrency, fiatRates);
|
||||
dispatch(onLocalAmountChange(localAmount, false));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Called from UI on "localAmount" field change
|
||||
*/
|
||||
export const onLocalAmountChange = (
|
||||
localAmount: string,
|
||||
shouldUpdateAmount: boolean = true
|
||||
): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
const state = getState().sendFormRipple;
|
||||
const { localCurrency } = getState().sendFormRipple;
|
||||
const fiatRates = getState().fiat.find(f => f.network === state.networkName);
|
||||
const { network } = getState().selectedAccount;
|
||||
|
||||
// updates localAmount
|
||||
dispatch({
|
||||
type: SEND.CHANGE,
|
||||
networkType: 'ripple',
|
||||
state: {
|
||||
...state,
|
||||
untouched: false,
|
||||
touched: { ...state.touched, localAmount: true },
|
||||
setMax: false,
|
||||
localAmount,
|
||||
},
|
||||
});
|
||||
|
||||
// updates amount
|
||||
if (shouldUpdateAmount) {
|
||||
if (!network) return;
|
||||
// converts amount in local currency to crypto currency that will be sent
|
||||
const amount = fromFiatCurrency(localAmount, localCurrency, fiatRates, network.decimals);
|
||||
dispatch(onAmountChange(amount, false));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Called from UI on "localCurrency" selection change
|
||||
*/
|
||||
export const onLocalCurrencyChange = (localCurrency: {
|
||||
value: string,
|
||||
label: string,
|
||||
}): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||
const state = getState().sendFormRipple;
|
||||
dispatch({
|
||||
type: SEND.CHANGE,
|
||||
networkType: 'ripple',
|
||||
state: {
|
||||
...state,
|
||||
localCurrency: localCurrency.value,
|
||||
},
|
||||
});
|
||||
// Recalculates local amount with new currency rates
|
||||
dispatch(onAmountChange(state.amount, true));
|
||||
};
|
||||
|
||||
/*
|
||||
@ -434,6 +504,8 @@ export default {
|
||||
toggleAdvanced,
|
||||
onAddressChange,
|
||||
onAmountChange,
|
||||
onLocalAmountChange,
|
||||
onLocalCurrencyChange,
|
||||
onSetMax,
|
||||
onFeeLevelChange,
|
||||
updateFeeLevels,
|
||||
|
@ -4,6 +4,7 @@ import BigNumber from 'bignumber.js';
|
||||
import * as SEND from 'actions/constants/send';
|
||||
import { findDevice, getPendingAmount } from 'reducers/utils';
|
||||
import { toDecimalAmount } from 'utils/formatUtils';
|
||||
import { toFiatCurrency } from 'utils/fiatConverter';
|
||||
|
||||
import type {
|
||||
Dispatch,
|
||||
@ -103,6 +104,12 @@ const recalculateTotalAmount = ($state: State): PayloadAction<State> => (
|
||||
.minus(account.reserve)
|
||||
.minus(pendingAmount);
|
||||
state.amount = calculateMaxAmount(availableBalance, fee);
|
||||
|
||||
// calculate amount in local currency
|
||||
const { localCurrency } = getState().sendFormRipple;
|
||||
const fiatRates = getState().fiat.find(f => f.network === state.networkName);
|
||||
const localAmount = toFiatCurrency(state.amount, localCurrency, fiatRates);
|
||||
state.localAmount = localAmount;
|
||||
}
|
||||
|
||||
state.total = calculateTotal(state.amount, fee);
|
||||
|
@ -3,7 +3,8 @@ import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import colors from 'config/colors';
|
||||
import ReactSelect from 'react-select';
|
||||
import { LANGUAGE, SCREEN_SIZE } from 'config/variables';
|
||||
import { SCREEN_SIZE } from 'config/variables';
|
||||
import { LANGUAGE } from 'config/app';
|
||||
|
||||
import type { Props } from './Container';
|
||||
|
||||
|
@ -4,12 +4,15 @@ import ReactSelect from 'react-select';
|
||||
import ReactAsyncSelect from 'react-select/lib/Async';
|
||||
import colors from 'config/colors';
|
||||
|
||||
const styles = isSearchable => ({
|
||||
const styles = (isSearchable, withDropdownIndicator = true) => ({
|
||||
singleValue: base => ({
|
||||
...base,
|
||||
maxWidth: 'calc(100% - 10px)', // 8px padding + 2px maring-left
|
||||
width: '100%',
|
||||
color: colors.TEXT_SECONDARY,
|
||||
'&:hover': {
|
||||
cursor: isSearchable ? 'text' : 'pointer',
|
||||
},
|
||||
}),
|
||||
control: (base, { isDisabled, isFocused }) => ({
|
||||
...base,
|
||||
@ -20,7 +23,7 @@ const styles = isSearchable => ({
|
||||
boxShadow: isFocused ? `0 0px 6px 0 ${colors.INPUT_FOCUSED_SHADOW}` : 'none',
|
||||
background: isDisabled ? colors.LANDING : colors.WHITE,
|
||||
'&:hover': {
|
||||
cursor: isSearchable ? 'text' : 'pointer',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
}),
|
||||
indicatorSeparator: () => ({
|
||||
@ -28,7 +31,7 @@ const styles = isSearchable => ({
|
||||
}),
|
||||
dropdownIndicator: (base, { isDisabled }) => ({
|
||||
...base,
|
||||
display: isSearchable || isDisabled ? 'none' : 'block',
|
||||
display: !withDropdownIndicator || isDisabled ? 'none' : 'block',
|
||||
color: colors.TEXT_SECONDARY,
|
||||
path: '',
|
||||
'&:hover': {
|
||||
|
68
src/config/app.js
Normal file
68
src/config/app.js
Normal file
@ -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',
|
||||
];
|
@ -81,23 +81,3 @@ export const LINE_HEIGHT = {
|
||||
BASE: '1.8',
|
||||
TREZOR_ACTION: '37px',
|
||||
};
|
||||
|
||||
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' },
|
||||
];
|
||||
|
@ -7,7 +7,7 @@ import type { FiatRateAction } from 'services/CoingeckoService';
|
||||
|
||||
export type Fiat = {
|
||||
+network: string,
|
||||
value: string,
|
||||
rates: { [string]: number },
|
||||
};
|
||||
|
||||
export const initialState: Array<Fiat> = [];
|
||||
@ -15,12 +15,14 @@ export const initialState: Array<Fiat> = [];
|
||||
const update = (state: Array<Fiat>, action: FiatRateAction): Array<Fiat> => {
|
||||
const affected = state.find(f => f.network === action.network);
|
||||
const otherRates = state.filter(d => d !== affected);
|
||||
const { network, rate } = action;
|
||||
const { network, rates } = action;
|
||||
|
||||
Object.keys(rates).map(k => rates[k].toFixed(2));
|
||||
|
||||
return otherRates.concat([
|
||||
{
|
||||
network,
|
||||
value: rate.toFixed(2),
|
||||
rates,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
@ -17,11 +17,13 @@ export type State = {
|
||||
currency: string,
|
||||
|
||||
// form fields
|
||||
localCurrency: string,
|
||||
advanced: boolean,
|
||||
untouched: boolean, // set to true when user made any changes in form
|
||||
touched: { [k: string]: boolean },
|
||||
address: string,
|
||||
amount: string,
|
||||
localAmount: string,
|
||||
setMax: boolean,
|
||||
feeLevels: Array<FeeLevel>,
|
||||
selectedFeeLevel: FeeLevel,
|
||||
@ -45,6 +47,7 @@ export const initialState: State = {
|
||||
networkName: '',
|
||||
networkSymbol: '',
|
||||
currency: '',
|
||||
localCurrency: '',
|
||||
|
||||
advanced: false,
|
||||
untouched: true,
|
||||
@ -52,6 +55,7 @@ export const initialState: State = {
|
||||
address: '',
|
||||
//address: '0x574BbB36871bA6b78E27f4B4dCFb76eA0091880B',
|
||||
amount: '',
|
||||
localAmount: '',
|
||||
setMax: false,
|
||||
feeLevels: [],
|
||||
selectedFeeLevel: {
|
||||
|
@ -16,11 +16,13 @@ export type State = {
|
||||
+networkSymbol: string,
|
||||
|
||||
// form fields
|
||||
localCurrency: string,
|
||||
advanced: boolean,
|
||||
untouched: boolean, // set to true when user made any changes in form
|
||||
touched: { [k: string]: boolean },
|
||||
address: string,
|
||||
amount: string,
|
||||
localAmount: string,
|
||||
minAmount: string,
|
||||
setMax: boolean,
|
||||
feeLevels: Array<FeeLevel>,
|
||||
@ -41,12 +43,14 @@ export type State = {
|
||||
export const initialState: State = {
|
||||
networkName: '',
|
||||
networkSymbol: '',
|
||||
localCurrency: '',
|
||||
|
||||
advanced: false,
|
||||
untouched: true,
|
||||
touched: {},
|
||||
address: '',
|
||||
amount: '',
|
||||
localAmount: '',
|
||||
minAmount: '0',
|
||||
setMax: false,
|
||||
feeLevels: [],
|
||||
|
@ -13,6 +13,7 @@ type State = {
|
||||
ready: boolean,
|
||||
online: boolean,
|
||||
language: string,
|
||||
localCurrency: string,
|
||||
messages: { [string]: string },
|
||||
dropdownOpened: boolean,
|
||||
showBetaDisclaimer: boolean,
|
||||
@ -28,6 +29,7 @@ const initialState: State = {
|
||||
ready: false,
|
||||
online: navigator.onLine,
|
||||
language: 'en',
|
||||
localCurrency: 'usd',
|
||||
messages: {},
|
||||
dropdownOpened: false,
|
||||
firstLocationChange: true,
|
||||
@ -129,6 +131,12 @@ export default function wallet(state: State = initialState, action: Action): Sta
|
||||
messages: action.messages ? action.messages : state.messages,
|
||||
};
|
||||
|
||||
case WALLET.SET_LOCAL_CURRENCY:
|
||||
return {
|
||||
...state,
|
||||
localCurrency: action.localCurrency,
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
import { httpRequest } from 'utils/networkUtils';
|
||||
import { resolveAfter } from 'utils/promiseUtils';
|
||||
import { READY } from 'actions/constants/localStorage';
|
||||
import * as TOKEN from 'actions/constants/token';
|
||||
import type { Token } from 'reducers/TokensReducer';
|
||||
|
||||
import type {
|
||||
Middleware,
|
||||
@ -14,14 +16,27 @@ import type {
|
||||
GetState,
|
||||
} from 'flowtype';
|
||||
|
||||
const BASE_URL = 'https://api.coingecko.com/';
|
||||
|
||||
export const RATE_UPDATE: 'rate__update' = 'rate__update';
|
||||
|
||||
export type NetworkRate = {
|
||||
network: string,
|
||||
rates: { [string]: number },
|
||||
};
|
||||
|
||||
export type FiatRateAction = {
|
||||
type: typeof RATE_UPDATE,
|
||||
network: string,
|
||||
rate: number,
|
||||
rates: { [string]: number },
|
||||
};
|
||||
|
||||
// const getSupportedCurrencies = async () => {
|
||||
// const url = 'https://api.coingecko.com/api/v3/simple/supported_vs_currencies';
|
||||
// const res = await httpRequest(url, 'json');
|
||||
// return res;
|
||||
// };
|
||||
|
||||
const loadRateAction = (): AsyncAction => async (
|
||||
dispatch: Dispatch,
|
||||
getState: GetState
|
||||
@ -41,7 +56,7 @@ const loadRateAction = (): AsyncAction => async (
|
||||
dispatch({
|
||||
type: RATE_UPDATE,
|
||||
network: response.symbol,
|
||||
rate: response.market_data.current_price.usd,
|
||||
rates: response.market_data.current_price,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -52,6 +67,49 @@ const loadRateAction = (): AsyncAction => async (
|
||||
await resolveAfter(50000);
|
||||
};
|
||||
|
||||
const fetchCoinRate = async (id: string): Promise<any> => {
|
||||
const url = new URL(`/api/v3/coins/${id}`, BASE_URL);
|
||||
url.searchParams.set('tickers', 'false');
|
||||
url.searchParams.set('market_data', 'true');
|
||||
url.searchParams.set('community_data', 'false');
|
||||
url.searchParams.set('developer_data', 'false');
|
||||
url.searchParams.set('sparkline', 'false');
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
const rates = await response.json();
|
||||
return rates;
|
||||
};
|
||||
|
||||
const fetchCoinList = async (): Promise<any> => {
|
||||
const url = new URL('/api/v3/coins/list', BASE_URL);
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
const tokens = await response.json();
|
||||
return tokens;
|
||||
};
|
||||
|
||||
const loadTokenRateAction = (token: Token): AsyncAction => async (
|
||||
dispatch: Dispatch
|
||||
): Promise<void> => {
|
||||
const { symbol } = token;
|
||||
try {
|
||||
const tokens = await fetchCoinList();
|
||||
const tokenData = tokens.find(t => t.symbol === symbol.toLowerCase());
|
||||
if (!tokenData) return;
|
||||
|
||||
const res = await fetchCoinRate(tokenData.id);
|
||||
if (res) {
|
||||
dispatch({
|
||||
type: RATE_UPDATE,
|
||||
network: res.symbol,
|
||||
rates: res.market_data.current_price,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Middleware
|
||||
*/
|
||||
@ -64,6 +122,10 @@ const CoingeckoService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDi
|
||||
api.dispatch(loadRateAction());
|
||||
}
|
||||
|
||||
if (action.type === TOKEN.ADD) {
|
||||
api.dispatch(loadTokenRateAction(action.payload));
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
|
@ -25,6 +25,10 @@ const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: Middlewar
|
||||
case WALLET.SET_LANGUAGE:
|
||||
api.dispatch(LocalStorageActions.setLanguage());
|
||||
break;
|
||||
|
||||
case WALLET.SET_LOCAL_CURRENCY:
|
||||
api.dispatch(LocalStorageActions.setLocalCurrency());
|
||||
break;
|
||||
// first time saving
|
||||
case CONNECT.REMEMBER:
|
||||
api.dispatch(LocalStorageActions.save());
|
||||
|
114
src/utils/__tests__/fiatConverter.js
Normal file
114
src/utils/__tests__/fiatConverter.js
Normal file
@ -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'
|
||||
);
|
||||
});
|
||||
});
|
44
src/utils/fiatConverter.js
Normal file
44
src/utils/fiatConverter.js
Normal file
@ -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 };
|
@ -1,4 +1,4 @@
|
||||
import { LANGUAGE } from 'config/variables';
|
||||
import { LANGUAGE } from 'config/app';
|
||||
|
||||
export const getInitialLocale = (defaultLocale = 'en') => {
|
||||
const browserLocale = navigator.language.split('-')[0];
|
||||
|
@ -8,7 +8,8 @@ import styled, { css } from 'styled-components';
|
||||
import * as stateUtils from 'reducers/utils';
|
||||
import Tooltip from 'components/Tooltip';
|
||||
import ICONS from 'config/icons';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
||||
import { toFiatCurrency } from 'utils/fiatConverter';
|
||||
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { findDeviceAccounts } from 'reducers/AccountsReducer';
|
||||
@ -104,8 +105,6 @@ const AccountMenu = (props: Props) => {
|
||||
|
||||
if (!selected || !network) return null;
|
||||
|
||||
const fiatRate = props.fiat.find(f => f.network === network.shortcut);
|
||||
|
||||
const deviceAccounts: Accounts = findDeviceAccounts(accounts, selected, location.state.network);
|
||||
|
||||
const selectedAccounts = deviceAccounts.map((account, i) => {
|
||||
@ -113,6 +112,9 @@ const AccountMenu = (props: Props) => {
|
||||
const url: string = location.pathname.replace(/account+\/([0-9]*)/, `account/${i}`);
|
||||
|
||||
let balance: ?string = null;
|
||||
const fiatRates = props.fiat.find(f => f.network === network.shortcut);
|
||||
const { localCurrency } = props.wallet;
|
||||
let fiat = '';
|
||||
if (account.balance !== '') {
|
||||
const pending = stateUtils.getAccountPendingTx(props.pending, account);
|
||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
||||
@ -121,10 +123,9 @@ const AccountMenu = (props: Props) => {
|
||||
.toString(10);
|
||||
|
||||
balance = `${availableBalance} ${network.symbol}`;
|
||||
if (fiatRate) {
|
||||
const accountBalance = new BigNumber(availableBalance);
|
||||
const fiat = accountBalance.times(fiatRate.value).toFixed(2);
|
||||
balance = `${availableBalance} ${network.symbol} / $${fiat}`;
|
||||
if (fiatRates) {
|
||||
fiat = toFiatCurrency(availableBalance, localCurrency, fiatRates);
|
||||
balance = `${availableBalance} ${network.symbol} / `;
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +141,20 @@ const AccountMenu = (props: Props) => {
|
||||
{...l10nCommonMessages.TR_ACCOUNT_HASH}
|
||||
values={{ number: account.index + 1 }}
|
||||
/>
|
||||
{balance && <Text>{balance}</Text>}
|
||||
{balance && (
|
||||
<Text>
|
||||
{balance}
|
||||
{fiatRates && (
|
||||
<FormattedNumber
|
||||
currency={localCurrency}
|
||||
value={fiat}
|
||||
minimumFractionDigits={2}
|
||||
// eslint-disable-next-line react/style-prop-object
|
||||
style="currency"
|
||||
/>
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
{!balance && (
|
||||
<Text>
|
||||
<FormattedMessage {...l10nMessages.TR_LOADING_DOT_DOT_DOT} />
|
||||
|
@ -2,8 +2,10 @@ import React, { PureComponent } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from 'components/Icon';
|
||||
import Link from 'components/Link';
|
||||
import DeviceIcon from 'components/images/DeviceIcon';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { getPattern } from 'support/routes';
|
||||
|
||||
import icons from 'config/icons';
|
||||
import colors from 'config/colors';
|
||||
@ -29,6 +31,12 @@ const Item = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const Divider = styled.div`
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: ${colors.DIVIDER};
|
||||
`;
|
||||
|
||||
const Label = styled.div`
|
||||
padding-left: 15px;
|
||||
`;
|
||||
@ -99,6 +107,15 @@ class MenuItems extends PureComponent {
|
||||
<FormattedMessage {...l10nCommonMessages.TR_FORGET_DEVICE} />
|
||||
</Label>
|
||||
</Item>
|
||||
<Divider />
|
||||
<Link to={getPattern('wallet-settings')}>
|
||||
<Item>
|
||||
<Icon icon={icons.COG} size={25} color={colors.TEXT_SECONDARY} />
|
||||
<Label>
|
||||
<FormattedMessage {...l10nCommonMessages.TR_APPLICATION_SETTINGS} />
|
||||
</Label>
|
||||
</Item>
|
||||
</Link>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
@ -10,16 +10,19 @@ import icons from 'config/icons';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import styled from 'styled-components';
|
||||
import DeviceHeader from 'components/DeviceHeader';
|
||||
// import Link from 'components/Link';
|
||||
import * as deviceUtils from 'utils/device';
|
||||
|
||||
import Tooltip from 'components/Tooltip';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
// import { getPattern } from 'support/routes';
|
||||
import AccountMenu from './components/AccountMenu';
|
||||
import CoinMenu from './components/CoinMenu';
|
||||
import DeviceMenu from './components/DeviceMenu';
|
||||
import Sidebar from './components/Sidebar';
|
||||
|
||||
import type { Props } from './components/common';
|
||||
// import l10nCommonMessages from 'views/common.messages';
|
||||
import l10nMessages from './index.messages';
|
||||
|
||||
const Header = styled(DeviceHeader)`
|
||||
@ -310,6 +313,27 @@ class LeftNavigation extends React.PureComponent<Props, State> {
|
||||
<Counter>{this.props.devices.length}</Counter>
|
||||
</Tooltip>
|
||||
)}
|
||||
{/* <Tooltip
|
||||
content={
|
||||
<FormattedMessage
|
||||
{...l10nCommonMessages.TR_APPLICATION_SETTINGS}
|
||||
/>
|
||||
}
|
||||
maxWidth={200}
|
||||
placement="bottom"
|
||||
enterDelayMs={0.5}
|
||||
>
|
||||
<WalletTypeIconWrapper>
|
||||
<Link to={getPattern('wallet-settings')}>
|
||||
<Icon
|
||||
size={25}
|
||||
color={colors.TEXT_SECONDARY}
|
||||
hoverColor={colors.TEXT_PRIMARY}
|
||||
icon={icons.COG}
|
||||
/>
|
||||
</Link>
|
||||
</WalletTypeIconWrapper>
|
||||
</Tooltip> */}
|
||||
<Icon
|
||||
canAnimate={this.state.clicked === true}
|
||||
isActive={this.props.wallet.dropdownOpened}
|
||||
|
@ -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;
|
@ -5,6 +5,7 @@ import colors from 'config/colors';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { connect } from 'react-redux';
|
||||
import { Route, withRouter } from 'react-router-dom';
|
||||
import { getPattern } from 'support/routes';
|
||||
|
||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||
import type { State } from 'flowtype';
|
||||
@ -27,6 +28,7 @@ import Backdrop from 'components/Backdrop';
|
||||
import LeftNavigation from './components/LeftNavigation/Container';
|
||||
import TopNavigationAccount from './components/TopNavigationAccount';
|
||||
import TopNavigationDeviceSettings from './components/TopNavigationDeviceSettings';
|
||||
import TopNavigationWalletSettings from './components/TopNavigationWalletSettings';
|
||||
|
||||
type StateProps = {
|
||||
wallet: $ElementType<State, 'wallet'>,
|
||||
@ -76,6 +78,7 @@ const MainContent = styled.article`
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
border-top-right-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.SM}) {
|
||||
${props =>
|
||||
@ -89,6 +92,7 @@ const MainContent = styled.article`
|
||||
|
||||
@media screen and (max-width: 1170px) {
|
||||
border-top-right-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
}
|
||||
`;
|
||||
|
||||
@ -132,13 +136,17 @@ const Wallet = (props: Props) => (
|
||||
<MainContent preventBgScroll={props.wallet.showSidebar}>
|
||||
<Navigation>
|
||||
<Route
|
||||
path="/device/:device/network/:network/account/:account"
|
||||
path={getPattern('wallet-account-summary')}
|
||||
component={TopNavigationAccount}
|
||||
/>
|
||||
<Route
|
||||
path="/device/:device/device-settings"
|
||||
path={getPattern('wallet-device-settings')}
|
||||
component={TopNavigationDeviceSettings}
|
||||
/>
|
||||
<Route
|
||||
path={getPattern('wallet-settings')}
|
||||
component={TopNavigationWalletSettings}
|
||||
/>
|
||||
</Navigation>
|
||||
<ContextNotifications />
|
||||
<Log />
|
||||
|
@ -9,7 +9,8 @@ import Input from 'components/inputs/Input';
|
||||
import Icon from 'components/Icon';
|
||||
import Link from 'components/Link';
|
||||
import ICONS from 'config/icons';
|
||||
import { FONT_SIZE, FONT_WEIGHT, TRANSITION } from 'config/variables';
|
||||
import { FONT_SIZE, FONT_WEIGHT, TRANSITION, SCREEN_SIZE } from 'config/variables';
|
||||
import { FIAT_CURRENCIES } from 'config/app';
|
||||
import colors from 'config/colors';
|
||||
import Title from 'views/Wallet/components/Title';
|
||||
import P from 'components/Paragraph';
|
||||
@ -43,6 +44,44 @@ const InputRow = styled.div`
|
||||
padding-bottom: 28px;
|
||||
`;
|
||||
|
||||
const LocalAmountWrapper = styled.div`
|
||||
display: flex;
|
||||
align-self: flex-start;
|
||||
margin-top: 26px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex: 1 0 100%;
|
||||
justify-content: flex-end;
|
||||
margin-top: 0px;
|
||||
padding-top: 28px;
|
||||
}
|
||||
`;
|
||||
|
||||
const AmountRow = styled(InputRow)`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding-bottom: 28px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`;
|
||||
|
||||
const LocalAmountInput = styled(Input)`
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const LocalCurrencySelect = styled(Select)`
|
||||
min-width: 77px;
|
||||
height: 40px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const SetMaxAmountButton = styled(Button)`
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
@ -187,6 +226,16 @@ const QrButton = styled(Button)`
|
||||
padding: 0 10px;
|
||||
`;
|
||||
|
||||
const EqualsSign = styled.div`
|
||||
align-self: center;
|
||||
padding: 0 10px;
|
||||
font-size: ${FONT_SIZE.BIGGER};
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
// render helpers
|
||||
const getAddressInputState = (
|
||||
address: string,
|
||||
@ -230,6 +279,10 @@ const getTokensSelectData = (
|
||||
return tokensSelectData;
|
||||
};
|
||||
|
||||
const buildCurrencyOption = currency => {
|
||||
return { value: currency, label: currency.toUpperCase() };
|
||||
};
|
||||
|
||||
// stateless component
|
||||
const AccountSend = (props: Props) => {
|
||||
const device = props.wallet.selectedDevice;
|
||||
@ -237,9 +290,11 @@ const AccountSend = (props: Props) => {
|
||||
const {
|
||||
address,
|
||||
amount,
|
||||
localAmount,
|
||||
setMax,
|
||||
networkSymbol,
|
||||
currency,
|
||||
localCurrency,
|
||||
feeLevels,
|
||||
selectedFeeLevel,
|
||||
gasPriceNeedsUpdate,
|
||||
@ -255,8 +310,10 @@ const AccountSend = (props: Props) => {
|
||||
toggleAdvanced,
|
||||
onAddressChange,
|
||||
onAmountChange,
|
||||
onLocalAmountChange,
|
||||
onSetMax,
|
||||
onCurrencyChange,
|
||||
onLocalCurrencyChange,
|
||||
onFeeLevelChange,
|
||||
updateFeeLevels,
|
||||
onSend,
|
||||
@ -337,7 +394,7 @@ const AccountSend = (props: Props) => {
|
||||
]}
|
||||
/>
|
||||
</InputRow>
|
||||
<InputRow>
|
||||
<AmountRow>
|
||||
<Input
|
||||
state={getAmountInputState(errors.amount, warnings.amount)}
|
||||
autoComplete="off"
|
||||
@ -385,7 +442,29 @@ const AccountSend = (props: Props) => {
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</InputRow>
|
||||
<LocalAmountWrapper>
|
||||
<EqualsSign>=</EqualsSign>
|
||||
<LocalAmountInput
|
||||
state={getAmountInputState(errors.amount, warnings.amount)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={localAmount}
|
||||
onChange={event => onLocalAmountChange(event.target.value)}
|
||||
sideAddons={[
|
||||
<LocalCurrencySelect
|
||||
key="local-currency"
|
||||
isSearchable
|
||||
isClearable={false}
|
||||
onChange={option => onLocalCurrencyChange(option)}
|
||||
value={buildCurrencyOption(localCurrency)}
|
||||
options={FIAT_CURRENCIES.map(c => buildCurrencyOption(c))}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</LocalAmountWrapper>
|
||||
</AmountRow>
|
||||
|
||||
<InputRow>
|
||||
<FeeLabelWrapper>
|
||||
|
@ -9,7 +9,8 @@ import Input from 'components/inputs/Input';
|
||||
import Icon from 'components/Icon';
|
||||
import Link from 'components/Link';
|
||||
import ICONS from 'config/icons';
|
||||
import { FONT_SIZE, FONT_WEIGHT, TRANSITION } from 'config/variables';
|
||||
import { FONT_SIZE, FONT_WEIGHT, TRANSITION, SCREEN_SIZE } from 'config/variables';
|
||||
import { FIAT_CURRENCIES } from 'config/app';
|
||||
import colors from 'config/colors';
|
||||
import Title from 'views/Wallet/components/Title';
|
||||
import P from 'components/Paragraph';
|
||||
@ -185,6 +186,53 @@ const QrButton = styled(Button)`
|
||||
padding: 0 10px;
|
||||
`;
|
||||
|
||||
const LocalAmountWrapper = styled.div`
|
||||
display: flex;
|
||||
align-self: flex-start;
|
||||
margin-top: 26px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex: 1 0 100%;
|
||||
justify-content: flex-end;
|
||||
margin-top: 0px;
|
||||
padding-top: 28px;
|
||||
}
|
||||
`;
|
||||
|
||||
const AmountRow = styled(InputRow)`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
padding-bottom: 28px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
`;
|
||||
|
||||
const LocalAmountInput = styled(Input)`
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const LocalCurrencySelect = styled(Select)`
|
||||
min-width: 77px;
|
||||
height: 40px;
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
flex: 1 1 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const EqualsSign = styled.div`
|
||||
align-self: center;
|
||||
padding: 0 10px;
|
||||
font-size: ${FONT_SIZE.BIGGER};
|
||||
|
||||
@media screen and (max-width: ${SCREEN_SIZE.MD}) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
// render helpers
|
||||
const getAddressInputState = (
|
||||
address: string,
|
||||
@ -215,6 +263,10 @@ const getAmountInputState = (amountErrors: string, amountWarnings: string): stri
|
||||
return state;
|
||||
};
|
||||
|
||||
const buildCurrencyOption = currency => {
|
||||
return { value: currency, label: currency.toUpperCase() };
|
||||
};
|
||||
|
||||
// stateless component
|
||||
const AccountSend = (props: Props) => {
|
||||
const device = props.wallet.selectedDevice;
|
||||
@ -222,6 +274,8 @@ const AccountSend = (props: Props) => {
|
||||
const {
|
||||
address,
|
||||
amount,
|
||||
localAmount,
|
||||
localCurrency,
|
||||
setMax,
|
||||
feeLevels,
|
||||
selectedFeeLevel,
|
||||
@ -238,6 +292,8 @@ const AccountSend = (props: Props) => {
|
||||
toggleAdvanced,
|
||||
onAddressChange,
|
||||
onAmountChange,
|
||||
onLocalAmountChange,
|
||||
onLocalCurrencyChange,
|
||||
onSetMax,
|
||||
onFeeLevelChange,
|
||||
updateFeeLevels,
|
||||
@ -307,7 +363,7 @@ const AccountSend = (props: Props) => {
|
||||
]}
|
||||
/>
|
||||
</InputRow>
|
||||
<InputRow>
|
||||
<AmountRow>
|
||||
<Input
|
||||
state={getAmountInputState(errors.amount, warnings.amount)}
|
||||
autoComplete="off"
|
||||
@ -350,7 +406,29 @@ const AccountSend = (props: Props) => {
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</InputRow>
|
||||
<LocalAmountWrapper>
|
||||
<EqualsSign>=</EqualsSign>
|
||||
<LocalAmountInput
|
||||
state={getAmountInputState(errors.amount, warnings.amount)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={localAmount}
|
||||
onChange={event => onLocalAmountChange(event.target.value)}
|
||||
sideAddons={[
|
||||
<LocalCurrencySelect
|
||||
key="local-currency"
|
||||
isSearchable={false}
|
||||
isClearable={false}
|
||||
onChange={option => onLocalCurrencyChange(option)}
|
||||
value={buildCurrencyOption(localCurrency)}
|
||||
options={FIAT_CURRENCIES.map(c => buildCurrencyOption(c))}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</LocalAmountWrapper>
|
||||
</AmountRow>
|
||||
|
||||
<InputRow>
|
||||
<FeeLabelWrapper>
|
||||
|
@ -1,12 +1,13 @@
|
||||
/* @flow */
|
||||
import React, { PureComponent } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import styled from 'styled-components';
|
||||
import Icon from 'components/Icon';
|
||||
import colors from 'config/colors';
|
||||
import ICONS from 'config/icons';
|
||||
import Tooltip from 'components/Tooltip';
|
||||
import { toFiatCurrency } from 'utils/fiatConverter';
|
||||
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||
import type { Network, State as ReducersState } from 'flowtype';
|
||||
import l10nMessages from './index.messages';
|
||||
@ -15,6 +16,7 @@ type Props = {
|
||||
network: Network,
|
||||
balance: string,
|
||||
fiat: $ElementType<ReducersState, 'fiat'>,
|
||||
localCurrency: string,
|
||||
};
|
||||
|
||||
type State = {
|
||||
@ -116,15 +118,13 @@ class AccountBalance extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { network } = this.props;
|
||||
const fiatRate = this.props.fiat.find(f => f.network === network.shortcut);
|
||||
let accountBalance = '';
|
||||
const { network, localCurrency } = this.props;
|
||||
const fiatRates = this.props.fiat.find(f => f.network === network.shortcut);
|
||||
let fiatRateValue = '';
|
||||
let fiat = '';
|
||||
if (fiatRate) {
|
||||
accountBalance = new BigNumber(this.props.balance);
|
||||
fiatRateValue = new BigNumber(fiatRate.value).toFixed(2);
|
||||
fiat = accountBalance.times(fiatRateValue).toFixed(2);
|
||||
if (fiatRates) {
|
||||
fiatRateValue = new BigNumber(fiatRates.rates[localCurrency]).toFixed(2);
|
||||
fiat = toFiatCurrency(this.props.balance, localCurrency, fiatRates);
|
||||
}
|
||||
|
||||
const NoRatesTooltip = (
|
||||
@ -155,8 +155,20 @@ class AccountBalance extends PureComponent<Props, State> {
|
||||
<FormattedMessage {...l10nMessages.TR_BALANCE} />
|
||||
</Label>
|
||||
<TooltipWrapper>
|
||||
<FiatValue>{fiatRate ? `$ ${fiat}` : 'N/A'}</FiatValue>
|
||||
{!fiatRate && NoRatesTooltip}
|
||||
<FiatValue>
|
||||
{fiatRates ? (
|
||||
<FormattedNumber
|
||||
currency={localCurrency}
|
||||
value={fiat}
|
||||
minimumFractionDigits={2}
|
||||
// eslint-disable-next-line react/style-prop-object
|
||||
style="currency"
|
||||
/>
|
||||
) : (
|
||||
'N/A'
|
||||
)}
|
||||
</FiatValue>
|
||||
{!fiatRates && NoRatesTooltip}
|
||||
</TooltipWrapper>
|
||||
<CoinBalance>
|
||||
{this.props.balance} {network.symbol}
|
||||
@ -168,9 +180,19 @@ class AccountBalance extends PureComponent<Props, State> {
|
||||
</Label>
|
||||
<TooltipWrapper>
|
||||
<FiatValueRate>
|
||||
{fiatRate ? `$ ${fiatRateValue}` : 'N/A'}
|
||||
{fiatRates ? (
|
||||
<FormattedNumber
|
||||
currency={localCurrency}
|
||||
value={fiatRateValue}
|
||||
minimumFractionDigits={2}
|
||||
// eslint-disable-next-line react/style-prop-object
|
||||
style="currency"
|
||||
/>
|
||||
) : (
|
||||
'N/A'
|
||||
)}
|
||||
</FiatValueRate>
|
||||
{!fiatRate && NoRatesTooltip}
|
||||
{!fiatRates && NoRatesTooltip}
|
||||
</TooltipWrapper>
|
||||
<CoinBalance>1 {network.symbol}</CoinBalance>
|
||||
</BalanceRateWrapper>
|
||||
|
@ -100,7 +100,12 @@ const AccountSummary = (props: Props) => {
|
||||
/>
|
||||
</Link>
|
||||
</AccountHeading>
|
||||
<AccountBalance network={network} balance={balance} fiat={props.fiat} />
|
||||
<AccountBalance
|
||||
network={network}
|
||||
balance={balance}
|
||||
fiat={props.fiat}
|
||||
localCurrency={props.wallet.localCurrency}
|
||||
/>
|
||||
<H2Wrapper>
|
||||
<H2>
|
||||
<FormattedMessage {...l10nSummaryMessages.TR_TOKENS} />
|
||||
@ -116,6 +121,7 @@ const AccountSummary = (props: Props) => {
|
||||
<AsyncSelectWrapper>
|
||||
<AsyncSelect
|
||||
isSearchable
|
||||
withDropdownIndicator={false}
|
||||
defaultOptions
|
||||
value={null}
|
||||
isMulti={false}
|
||||
|
@ -14,6 +14,7 @@ type Props = {
|
||||
balance: string,
|
||||
reserve: string,
|
||||
fiat: $ElementType<ReducersState, 'fiat'>,
|
||||
localCurrency: string,
|
||||
};
|
||||
|
||||
type State = {
|
||||
@ -115,14 +116,14 @@ class AccountBalance extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { network } = this.props;
|
||||
const fiatRate = this.props.fiat.find(f => f.network === network.shortcut);
|
||||
const { network, localCurrency } = this.props;
|
||||
const fiatRates = this.props.fiat.find(f => f.network === network.shortcut);
|
||||
let accountBalance = '';
|
||||
let fiatRateValue = '';
|
||||
let fiat = '';
|
||||
if (fiatRate) {
|
||||
if (fiatRates) {
|
||||
accountBalance = new BigNumber(this.props.balance);
|
||||
fiatRateValue = new BigNumber(fiatRate.value).toFixed(2);
|
||||
fiatRateValue = new BigNumber(fiatRates.rates[localCurrency]).toFixed(2);
|
||||
fiat = accountBalance.times(fiatRateValue).toFixed(2);
|
||||
}
|
||||
|
||||
@ -152,8 +153,10 @@ class AccountBalance extends PureComponent<Props, State> {
|
||||
<BalanceWrapper>
|
||||
<Label>Balance</Label>
|
||||
<TooltipWrapper>
|
||||
<FiatValue>{fiatRate ? `$ ${fiat}` : 'N/A'}</FiatValue>
|
||||
{!fiatRate && NoRatesTooltip}
|
||||
<FiatValue>
|
||||
{fiatRates ? `${fiat} ${localCurrency}` : 'N/A'}
|
||||
</FiatValue>
|
||||
{!fiatRates && NoRatesTooltip}
|
||||
</TooltipWrapper>
|
||||
<CoinBalance>
|
||||
{this.props.balance} {network.symbol}
|
||||
@ -172,9 +175,9 @@ class AccountBalance extends PureComponent<Props, State> {
|
||||
<Label>Rate</Label>
|
||||
<TooltipWrapper>
|
||||
<FiatValueRate>
|
||||
{fiatRate ? `$ ${fiatRateValue}` : 'N/A'}
|
||||
{fiatRates ? `${fiatRateValue} ${localCurrency}` : 'N/A'}
|
||||
</FiatValueRate>
|
||||
{!fiatRate && NoRatesTooltip}
|
||||
{!fiatRates && NoRatesTooltip}
|
||||
</TooltipWrapper>
|
||||
<CoinBalance>1 {network.symbol}</CoinBalance>
|
||||
</BalanceRateWrapper>
|
||||
|
@ -85,6 +85,7 @@ const AccountSummary = (props: Props) => {
|
||||
balance={balance}
|
||||
reserve={reserve}
|
||||
fiat={props.fiat}
|
||||
localCurrency={props.wallet.localCurrency}
|
||||
/>
|
||||
{TMP_SHOW_HISTORY && (
|
||||
<H2Wrapper>
|
||||
|
44
src/views/Wallet/views/WalletSettings/Container.js
Normal file
44
src/views/Wallet/views/WalletSettings/Container.js
Normal file
@ -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,82 @@
|
||||
/* @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>
|
||||
{console.log(props)}
|
||||
<Section>
|
||||
<Row>
|
||||
<Icon size={60} color={colors.WARNING_PRIMARY} icon={icons.WARNING} />
|
||||
<StyledH1>Wallet settings is under construction</StyledH1>
|
||||
<Link to="/">
|
||||
<Button>Take me back</Button>
|
||||
</Link>
|
||||
</Row>
|
||||
<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 isGreen>
|
||||
<FormattedMessage {...l10nCommonMessages.TR_CLOSE} />
|
||||
</Button>
|
||||
</Link>
|
||||
</Buttons>
|
||||
</Actions>
|
||||
</Content>
|
||||
);
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
null
|
||||
)(WalletSettings);
|
||||
export default WalletSettings;
|
||||
|
16
src/views/Wallet/views/WalletSettings/index.messages.js
Normal file
16
src/views/Wallet/views/WalletSettings/index.messages.js
Normal file
@ -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;
|
@ -7,6 +7,10 @@ const definedMessages: Messages = defineMessages({
|
||||
id: 'TR_DEVICE_SETTINGS',
|
||||
defaultMessage: 'Device settings',
|
||||
},
|
||||
TR_APPLICATION_SETTINGS: {
|
||||
id: 'TR_APPLICATION_SETTINGS',
|
||||
defaultMessage: 'Application settings',
|
||||
},
|
||||
TR_ACCOUNT_HASH: {
|
||||
id: 'TR_ACCOUNT_HASH',
|
||||
defaultMessage: 'Account #{number}',
|
||||
@ -62,6 +66,10 @@ const definedMessages: Messages = defineMessages({
|
||||
id: 'TR_FORGET_DEVICE',
|
||||
defaultMessage: 'Forget device',
|
||||
},
|
||||
TR_CLOSE: {
|
||||
id: 'TR_CLOSE',
|
||||
defaultMessage: 'Close',
|
||||
},
|
||||
});
|
||||
|
||||
export default definedMessages;
|
||||
|
@ -25,7 +25,7 @@ import AccountSignVerify from 'views/Wallet/views/Account/SignVerify/Container';
|
||||
|
||||
import WalletDashboard from 'views/Wallet/views/Dashboard';
|
||||
import WalletDeviceSettings from 'views/Wallet/views/DeviceSettings';
|
||||
import WalletSettings from 'views/Wallet/views/WalletSettings';
|
||||
import WalletSettings from 'views/Wallet/views/WalletSettings/Container';
|
||||
import WalletBootloader from 'views/Wallet/views/Bootloader';
|
||||
import WalletFirmwareUpdate from 'views/Wallet/views/FirmwareUpdate';
|
||||
import WalletNoBackup from 'views/Wallet/views/NoBackup';
|
||||
|
Loading…
Reference in New Issue
Block a user