mirror of
https://github.com/trezor/trezor-wallet
synced 2024-12-29 02:18:06 +00:00
merge
This commit is contained in:
commit
a31301a9f5
@ -6,7 +6,7 @@
|
|||||||
"bin": {
|
"bin": {
|
||||||
"flow": "./node_modules/flow-bin"
|
"flow": "./node_modules/flow-bin"
|
||||||
},
|
},
|
||||||
"license": "LGPL-3.0+",
|
"license": "T-RSL",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npx webpack-dev-server --config webpack/dev.babel.js",
|
"dev": "npx webpack-dev-server --config webpack/dev.babel.js",
|
||||||
"dev:local": "npx webpack-dev-server --config webpack/local.babel.js",
|
"dev:local": "npx webpack-dev-server --config webpack/local.babel.js",
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"coins": [
|
"networks": [
|
||||||
{
|
{
|
||||||
"name": "Ethereum",
|
"name": "Ethereum",
|
||||||
"symbol": "ETH",
|
"symbol": "ETH",
|
||||||
"network": "eth",
|
"shortcut": "eth",
|
||||||
"bip44": "m/44'/60'/0'/0",
|
"bip44": "m/44'/60'/0'/0",
|
||||||
"chainId": 1,
|
"chainId": 1,
|
||||||
"defaultGasPrice": 64,
|
"defaultGasPrice": 64,
|
||||||
@ -21,7 +21,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Ethereum Classic",
|
"name": "Ethereum Classic",
|
||||||
"symbol": "ETC",
|
"symbol": "ETC",
|
||||||
"network": "etc",
|
"shortcut": "etc",
|
||||||
"chainId": 61,
|
"chainId": 61,
|
||||||
"bip44": "m/44'/61'/0'/0",
|
"bip44": "m/44'/61'/0'/0",
|
||||||
"defaultGasPrice": 64,
|
"defaultGasPrice": 64,
|
||||||
@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Ethereum Ropsten",
|
"name": "Ethereum Ropsten",
|
||||||
"symbol": "tROP",
|
"symbol": "tROP",
|
||||||
"network": "trop",
|
"shortcut": "trop",
|
||||||
"chainId": 3,
|
"chainId": 3,
|
||||||
"bip44": "m/44'/60'/0'/0",
|
"bip44": "m/44'/60'/0'/0",
|
||||||
"defaultGasPrice": 64,
|
"defaultGasPrice": 64,
|
||||||
|
@ -16,5 +16,11 @@
|
|||||||
"name": "Trezor13",
|
"name": "Trezor13",
|
||||||
"symbol": "T013",
|
"symbol": "T013",
|
||||||
"decimals": 13
|
"decimals": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "0x58cda554935e4a1f2acbe15f8757400af275e084",
|
||||||
|
"name": "LahodCoin",
|
||||||
|
"symbol": "LAHOD",
|
||||||
|
"decimals": 13
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -92,7 +92,7 @@ const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockchain = getState().blockchain.find(b => b.name === network);
|
const blockchain = getState().blockchain.find(b => b.shortcut === network);
|
||||||
if (blockchain && !blockchain.connected && (!discoveryProcess || !discoveryProcess.completed)) {
|
if (blockchain && !blockchain.connected && (!discoveryProcess || !discoveryProcess.completed)) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.WAITING_FOR_BLOCKCHAIN,
|
type: DISCOVERY.WAITING_FOR_BLOCKCHAIN,
|
||||||
@ -124,8 +124,8 @@ const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean)
|
|||||||
// start discovery process
|
// start discovery process
|
||||||
const begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
const begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const { config } = getState().localStorage;
|
const { config } = getState().localStorage;
|
||||||
const coinToDiscover = config.coins.find(c => c.network === network);
|
const networkData = config.networks.find(c => c.shortcut === network);
|
||||||
if (!coinToDiscover) return;
|
if (!networkData) return;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.WAITING_FOR_DEVICE,
|
type: DISCOVERY.WAITING_FOR_DEVICE,
|
||||||
@ -140,7 +140,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => async (dis
|
|||||||
instance: device.instance,
|
instance: device.instance,
|
||||||
state: device.state,
|
state: device.state,
|
||||||
},
|
},
|
||||||
path: coinToDiscover.bip44,
|
path: networkData.bip44,
|
||||||
keepSession: true, // acquire and hold session
|
keepSession: true, // acquire and hold session
|
||||||
//useEmptyPassphrase: !device.instance,
|
//useEmptyPassphrase: !device.instance,
|
||||||
useEmptyPassphrase: device.useEmptyPassphrase,
|
useEmptyPassphrase: device.useEmptyPassphrase,
|
||||||
@ -179,7 +179,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => async (dis
|
|||||||
// send data to reducer
|
// send data to reducer
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.START,
|
type: DISCOVERY.START,
|
||||||
network: coinToDiscover.network,
|
network,
|
||||||
device,
|
device,
|
||||||
publicKey: response.payload.publicKey,
|
publicKey: response.payload.publicKey,
|
||||||
chainCode: response.payload.chainCode,
|
chainCode: response.payload.chainCode,
|
||||||
|
@ -7,13 +7,25 @@ import * as TOKEN from 'actions/constants/token';
|
|||||||
import * as DISCOVERY from 'actions/constants/discovery';
|
import * as DISCOVERY from 'actions/constants/discovery';
|
||||||
import * as STORAGE from 'actions/constants/localStorage';
|
import * as STORAGE from 'actions/constants/localStorage';
|
||||||
import * as PENDING from 'actions/constants/pendingTx';
|
import * as PENDING from 'actions/constants/pendingTx';
|
||||||
|
import * as WALLET from 'actions/constants/wallet';
|
||||||
import { httpRequest } from 'utils/networkUtils';
|
import { httpRequest } from 'utils/networkUtils';
|
||||||
import * as buildUtils from 'utils/build';
|
import * as buildUtils from 'utils/build';
|
||||||
|
|
||||||
|
import { findAccountTokens } from 'reducers/TokensReducer';
|
||||||
|
import type { Account } from 'reducers/AccountsReducer';
|
||||||
|
import type { Token } from 'reducers/TokensReducer';
|
||||||
|
import type { PendingTx } from 'reducers/PendingTxReducer';
|
||||||
|
import type { Discovery } from 'reducers/DiscoveryReducer';
|
||||||
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ThunkAction, AsyncAction, /* GetState, */ Dispatch,
|
TrezorDevice,
|
||||||
|
ThunkAction,
|
||||||
|
AsyncAction,
|
||||||
|
GetState,
|
||||||
|
Dispatch,
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
import type { Config, Coin, TokensCollection } from 'reducers/LocalStorageReducer';
|
import type { Config, Network, TokensCollection } from 'reducers/LocalStorageReducer';
|
||||||
|
|
||||||
import Erc20AbiJSON from 'public/data/ERC20Abi.json';
|
import Erc20AbiJSON from 'public/data/ERC20Abi.json';
|
||||||
import AppConfigJSON from 'public/data/appConfig.json';
|
import AppConfigJSON from 'public/data/appConfig.json';
|
||||||
@ -31,7 +43,7 @@ export type StorageAction = {
|
|||||||
error: string,
|
error: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get = (key: string): ?string => {
|
const get = (key: string): ?string => {
|
||||||
try {
|
try {
|
||||||
return window.localStorage.getItem(key);
|
return window.localStorage.getItem(key);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -40,203 +52,212 @@ export const get = (key: string): ?string => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function update(event: StorageEvent): AsyncAction {
|
const set = (key: string, value: string | boolean): void => {
|
||||||
return async (dispatch: Dispatch/* , getState: GetState */): Promise<void> => {
|
try {
|
||||||
if (!event.newValue) return;
|
window.localStorage.setItem(key, value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Local Storage ERROR: ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (event.key === 'devices') {
|
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
|
||||||
// check if device was added/ removed
|
// or
|
||||||
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
|
// https://www.npmjs.com/package/redux-react-session
|
||||||
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
|
|
||||||
|
|
||||||
// if (newDevices.length !== myDevices.length) {
|
const findAccounts = (devices: Array<TrezorDevice>, accounts: Array<Account>): Array<Account> => devices.reduce((arr, dev) => arr.concat(accounts.filter(a => a.deviceState === dev.state)), []);
|
||||||
// const diff = myDevices.filter(d => newDevices.indexOf(d) < 0)
|
|
||||||
// console.warn("DEV LIST CHANGED!", newDevices.length, myDevices.length, diff)
|
|
||||||
// // check if difference is caused by local device which is not saved
|
|
||||||
// // or device which was saved in other tab
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const diff = oldDevices.filter(d => newDevices.indexOf())
|
const findTokens = (accounts: Array<Account>, tokens: Array<Token>): Array<Token> => accounts.reduce((arr, account) => arr.concat(findAccountTokens(tokens, account)), []);
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === 'accounts') {
|
const findDiscovery = (devices: Array<TrezorDevice>, discovery: Array<Discovery>): Array<Discovery> => devices.reduce((arr, dev) => arr.concat(discovery.filter(a => a.deviceState === dev.state && a.publicKey.length > 0)), []);
|
||||||
dispatch({
|
|
||||||
type: ACCOUNT.FROM_STORAGE,
|
|
||||||
payload: JSON.parse(event.newValue),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === 'tokens') {
|
const findPendingTxs = (accounts: Array<Account>, pending: Array<PendingTx>): Array<PendingTx> => accounts.reduce((result, account) => result.concat(pending.filter(p => p.address === account.address && p.network === account.network)), []);
|
||||||
dispatch({
|
|
||||||
type: TOKEN.FROM_STORAGE,
|
|
||||||
payload: JSON.parse(event.newValue),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.key === 'pending') {
|
export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
dispatch({
|
const devices: Array<TrezorDevice> = getState().devices.filter(d => d.features && d.remember === true);
|
||||||
type: PENDING.FROM_STORAGE,
|
const accounts: Array<Account> = findAccounts(devices, getState().accounts);
|
||||||
payload: JSON.parse(event.newValue),
|
const tokens: Array<Token> = findTokens(accounts, getState().tokens);
|
||||||
});
|
const pending: Array<PendingTx> = findPendingTxs(accounts, getState().pending);
|
||||||
}
|
const discovery: Array<Discovery> = findDiscovery(devices, getState().discovery);
|
||||||
|
|
||||||
if (event.key === 'discovery') {
|
// save devices
|
||||||
dispatch({
|
set('devices', JSON.stringify(devices));
|
||||||
type: DISCOVERY.FROM_STORAGE,
|
|
||||||
payload: JSON.parse(event.newValue),
|
// save already preloaded accounts
|
||||||
});
|
set('accounts', JSON.stringify(accounts));
|
||||||
}
|
|
||||||
};
|
// save discovery state
|
||||||
}
|
set('discovery', JSON.stringify(discovery));
|
||||||
|
|
||||||
|
// tokens
|
||||||
|
set('tokens', JSON.stringify(tokens));
|
||||||
|
|
||||||
|
// pending transactions
|
||||||
|
set('pending', JSON.stringify(pending));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
|
if (!event.newValue) return;
|
||||||
|
|
||||||
|
if (event.key === 'devices') {
|
||||||
|
// check if device was added/ removed
|
||||||
|
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
|
||||||
|
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
|
||||||
|
|
||||||
|
// if (newDevices.length !== myDevices.length) {
|
||||||
|
// const diff = myDevices.filter(d => newDevices.indexOf(d) < 0)
|
||||||
|
// console.warn("DEV LIST CHANGED!", newDevices.length, myDevices.length, diff)
|
||||||
|
// // check if difference is caused by local device which is not saved
|
||||||
|
// // or device which was saved in other tab
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const diff = oldDevices.filter(d => newDevices.indexOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'accounts') {
|
||||||
|
dispatch({
|
||||||
|
type: ACCOUNT.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(event.newValue),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'tokens') {
|
||||||
|
dispatch({
|
||||||
|
type: TOKEN.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(event.newValue),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'pending') {
|
||||||
|
dispatch({
|
||||||
|
type: PENDING.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(event.newValue),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === 'discovery') {
|
||||||
|
dispatch({
|
||||||
|
type: DISCOVERY.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(event.newValue),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const VERSION: string = '1';
|
const VERSION: string = '1';
|
||||||
|
|
||||||
export function loadTokensFromJSON(): AsyncAction {
|
const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
||||||
return async (dispatch: Dispatch): Promise<void> => {
|
if (typeof window.localStorage === 'undefined') return;
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const config: Config = await httpRequest(AppConfigJSON, 'json');
|
const config: Config = await httpRequest(AppConfigJSON, 'json');
|
||||||
|
|
||||||
if (!buildUtils.isDev()) {
|
if (!buildUtils.isDev()) {
|
||||||
const index = config.coins.findIndex(c => c.network === 'trop');
|
const index = config.networks.findIndex(c => c.shortcut === 'trop');
|
||||||
delete config.coins[index];
|
delete config.networks[index];
|
||||||
}
|
|
||||||
|
|
||||||
const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json');
|
|
||||||
|
|
||||||
window.addEventListener('storage', (event) => {
|
|
||||||
dispatch(update(event));
|
|
||||||
});
|
|
||||||
|
|
||||||
// validate version
|
|
||||||
const version: ?string = get('version');
|
|
||||||
if (version !== VERSION) {
|
|
||||||
window.localStorage.clear();
|
|
||||||
dispatch(save('version', VERSION));
|
|
||||||
}
|
|
||||||
|
|
||||||
// load tokens
|
|
||||||
const tokens = await config.coins.reduce(async (promise: Promise<TokensCollection>, coin: Coin): Promise<TokensCollection> => {
|
|
||||||
const collection: TokensCollection = await promise;
|
|
||||||
const json = await httpRequest(coin.tokens, 'json');
|
|
||||||
collection[coin.network] = json;
|
|
||||||
return collection;
|
|
||||||
}, Promise.resolve({}));
|
|
||||||
|
|
||||||
const devices: ?string = get('devices');
|
|
||||||
if (devices) {
|
|
||||||
dispatch({
|
|
||||||
type: CONNECT.DEVICE_FROM_STORAGE,
|
|
||||||
payload: JSON.parse(devices),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const accounts: ?string = get('accounts');
|
|
||||||
if (accounts) {
|
|
||||||
dispatch({
|
|
||||||
type: ACCOUNT.FROM_STORAGE,
|
|
||||||
payload: JSON.parse(accounts),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const userTokens: ?string = get('tokens');
|
|
||||||
if (userTokens) {
|
|
||||||
dispatch({
|
|
||||||
type: TOKEN.FROM_STORAGE,
|
|
||||||
payload: JSON.parse(userTokens),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const pending: ?string = get('pending');
|
|
||||||
if (pending) {
|
|
||||||
dispatch({
|
|
||||||
type: PENDING.FROM_STORAGE,
|
|
||||||
payload: JSON.parse(pending),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const discovery: ?string = get('discovery');
|
|
||||||
if (discovery) {
|
|
||||||
dispatch({
|
|
||||||
type: DISCOVERY.FROM_STORAGE,
|
|
||||||
payload: JSON.parse(discovery),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: STORAGE.READY,
|
|
||||||
config,
|
|
||||||
tokens,
|
|
||||||
ERC20Abi,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
dispatch({
|
|
||||||
type: STORAGE.ERROR,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadData = (): ThunkAction => (dispatch: Dispatch): void => {
|
const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json');
|
||||||
// check if local storage is available
|
|
||||||
// let available: boolean = true;
|
|
||||||
// if (typeof window.localStorage === 'undefined') {
|
|
||||||
// available = false;
|
|
||||||
// } else {
|
|
||||||
// try {
|
|
||||||
// window.localStorage.setItem('ethereum_wallet', true);
|
|
||||||
// } catch (error) {
|
|
||||||
// available = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
dispatch(loadTokensFromJSON());
|
window.addEventListener('storage', (event) => {
|
||||||
|
dispatch(update(event));
|
||||||
|
});
|
||||||
|
|
||||||
|
// validate version
|
||||||
|
const version: ?string = get('version');
|
||||||
|
if (version !== VERSION) {
|
||||||
|
try {
|
||||||
|
window.localStorage.clear();
|
||||||
|
window.sessionStorage.clear();
|
||||||
|
} catch (error) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
set('version', VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load tokens
|
||||||
|
const tokens = await config.networks.reduce(async (promise: Promise<TokensCollection>, network: Network): Promise<TokensCollection> => {
|
||||||
|
const collection: TokensCollection = await promise;
|
||||||
|
const json = await httpRequest(network.tokens, 'json');
|
||||||
|
collection[network.shortcut] = json;
|
||||||
|
return collection;
|
||||||
|
}, Promise.resolve({}));
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: STORAGE.READY,
|
||||||
|
config,
|
||||||
|
tokens,
|
||||||
|
ERC20Abi,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: STORAGE.ERROR,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// const parseConfig = (json: JSON): Config => {
|
|
||||||
|
|
||||||
// if (json['coins']) {
|
const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
|
const devices: ?string = get('devices');
|
||||||
|
if (devices) {
|
||||||
|
dispatch({
|
||||||
|
type: CONNECT.DEVICE_FROM_STORAGE,
|
||||||
|
payload: JSON.parse(devices),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
const accounts: ?string = get('accounts');
|
||||||
|
if (accounts) {
|
||||||
|
dispatch({
|
||||||
|
type: ACCOUNT.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(accounts),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// for (let key in json) {
|
const userTokens: ?string = get('tokens');
|
||||||
// if (key === 'coins') {
|
if (userTokens) {
|
||||||
|
dispatch({
|
||||||
|
type: TOKEN.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(userTokens),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
const pending: ?string = get('pending');
|
||||||
// }
|
if (pending) {
|
||||||
|
dispatch({
|
||||||
|
type: PENDING.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(pending),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// const coins: Array<Object> = json.coins || [];
|
const discovery: ?string = get('discovery');
|
||||||
|
if (discovery) {
|
||||||
|
dispatch({
|
||||||
|
type: DISCOVERY.FROM_STORAGE,
|
||||||
|
payload: JSON.parse(discovery),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// if ("coins" in json){
|
if (buildUtils.isDev() || buildUtils.isBeta()) {
|
||||||
// json.coins
|
const betaModal = get('/betaModalPrivacy');
|
||||||
// }
|
if (!betaModal) {
|
||||||
// if (!json.hasOwnProperty("fiatValueTickers")) throw new Error(`Property "fiatValueTickers" is missing in appConfig.json`);
|
dispatch({
|
||||||
// if (json.config && json.hasOwnProperty('coins') && Array.isArray(json.coins)) {
|
type: WALLET.SHOW_BETA_DISCLAIMER,
|
||||||
// json.coins.map(c => {
|
show: true,
|
||||||
// return {
|
});
|
||||||
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// } else {
|
|
||||||
// throw new Error(`Property "coins" is missing in appConfig.json`);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// coins: [],
|
|
||||||
// fiatValueTickers: []
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
export const save = (key: string, value: string): ThunkAction => (): void => {
|
|
||||||
if (typeof window.localStorage !== 'undefined') {
|
|
||||||
try {
|
|
||||||
window.localStorage.setItem(key, value);
|
|
||||||
} catch (error) {
|
|
||||||
// available = false;
|
|
||||||
console.error(`Local Storage ERROR: ${error}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
|
dispatch(loadStorageData());
|
||||||
|
|
||||||
|
// stop loading resources and wait for user action
|
||||||
|
if (!getState().wallet.showBetaDisclaimer) {
|
||||||
|
dispatch(loadJSON());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hideBetaDisclaimer = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
|
set('/betaModalPrivacy', true);
|
||||||
|
dispatch(loadJSON());
|
||||||
|
};
|
||||||
|
@ -119,7 +119,6 @@ export const onWalletTypeRequest = (device: TrezorDevice, hidden: boolean, state
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const gotoExternalWallet = (id: string, url: string): ThunkAction => (dispatch: Dispatch): void => {
|
export const gotoExternalWallet = (id: string, url: string): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
console.warn('OPEN', id, url);
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: MODAL.OPEN_EXTERNAL_WALLET,
|
type: MODAL.OPEN_EXTERNAL_WALLET,
|
||||||
id,
|
id,
|
||||||
@ -127,7 +126,6 @@ export const gotoExternalWallet = (id: string, url: string): ThunkAction => (dis
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onPinSubmit,
|
onPinSubmit,
|
||||||
onPassphraseSubmit,
|
onPassphraseSubmit,
|
||||||
|
@ -71,8 +71,8 @@ export const paramsValidation = (params: RouterLocationState): PayloadAction<boo
|
|||||||
// validate requested network
|
// validate requested network
|
||||||
if (params.hasOwnProperty('network')) {
|
if (params.hasOwnProperty('network')) {
|
||||||
const { config } = getState().localStorage;
|
const { config } = getState().localStorage;
|
||||||
const coin = config.coins.find(c => c.network === params.network);
|
const network = config.networks.find(c => c.shortcut === params.network);
|
||||||
if (!coin) return false;
|
if (!network) return false;
|
||||||
if (!params.account) return false;
|
if (!params.account) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ export const getValidUrl = (action: RouterAction): PayloadAction<string> => (dis
|
|||||||
const shouldBeLandingPage = getState().devices.length < 1 || !getState().wallet.ready || getState().connect.error !== null;
|
const shouldBeLandingPage = getState().devices.length < 1 || !getState().wallet.ready || getState().connect.error !== null;
|
||||||
const landingPageUrl = dispatch(isLandingPageUrl(requestedUrl));
|
const landingPageUrl = dispatch(isLandingPageUrl(requestedUrl));
|
||||||
if (shouldBeLandingPage) {
|
if (shouldBeLandingPage) {
|
||||||
const landingPageRoute = dispatch(isLandingPageUrl(requestedUrl, true));
|
const landingPageRoute = dispatch(isLandingPageUrl(requestedUrl, getState().wallet.ready));
|
||||||
return !landingPageRoute ? '/' : requestedUrl;
|
return !landingPageRoute ? '/' : requestedUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ const getAccountStatus = (state: State, selectedAccount: SelectedAccountState):
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockchain = state.blockchain.find(b => b.name === network.network);
|
const blockchain = state.blockchain.find(b => b.shortcut === network.shortcut);
|
||||||
if (blockchain && !blockchain.connected) {
|
if (blockchain && !blockchain.connected) {
|
||||||
return {
|
return {
|
||||||
type: 'backend',
|
type: 'backend',
|
||||||
|
@ -130,7 +130,7 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const gasPrice: BigNumber = await dispatch(BlockchainActions.getGasPrice(network.network, network.defaultGasPrice));
|
const gasPrice: BigNumber = await dispatch(BlockchainActions.getGasPrice(network.shortcut, network.defaultGasPrice));
|
||||||
const gasLimit = network.defaultGasLimit.toString();
|
const gasLimit = network.defaultGasLimit.toString();
|
||||||
const feeLevels = ValidationActions.getFeeLevels(network.symbol, gasPrice, gasLimit);
|
const feeLevels = ValidationActions.getFeeLevels(network.symbol, gasPrice, gasLimit);
|
||||||
const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, initialState.selectedFeeLevel);
|
const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, initialState.selectedFeeLevel);
|
||||||
@ -139,7 +139,7 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS
|
|||||||
type: SEND.INIT,
|
type: SEND.INIT,
|
||||||
state: {
|
state: {
|
||||||
...initialState,
|
...initialState,
|
||||||
networkName: network.network,
|
networkName: network.shortcut,
|
||||||
networkSymbol: network.symbol,
|
networkSymbol: network.symbol,
|
||||||
currency: network.symbol,
|
currency: network.symbol,
|
||||||
feeLevels,
|
feeLevels,
|
||||||
@ -411,7 +411,7 @@ const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const gasLimit = await dispatch(BlockchainActions.estimateGasLimit(network.network, state.data, state.amount, state.gasPrice));
|
const gasLimit = await dispatch(BlockchainActions.estimateGasLimit(network.shortcut, state.data, state.amount, state.gasPrice));
|
||||||
|
|
||||||
// double check "data" field
|
// double check "data" field
|
||||||
// possible race condition when data changed before backend respond
|
// possible race condition when data changed before backend respond
|
||||||
@ -439,7 +439,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
|
|||||||
const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce;
|
const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce;
|
||||||
|
|
||||||
const txData = await dispatch(prepareEthereumTx({
|
const txData = await dispatch(prepareEthereumTx({
|
||||||
network: network.network,
|
network: network.shortcut,
|
||||||
token: isToken ? findToken(getState().tokens, account.address, currentState.currency, account.deviceState) : null,
|
token: isToken ? findToken(getState().tokens, account.address, currentState.currency, account.deviceState) : null,
|
||||||
from: account.address,
|
from: account.address,
|
||||||
to: currentState.address,
|
to: currentState.address,
|
||||||
@ -487,7 +487,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
|
|||||||
const serializedTx: string = await dispatch(serializeEthereumTx(txData));
|
const serializedTx: string = await dispatch(serializeEthereumTx(txData));
|
||||||
const push = await TrezorConnect.pushTransaction({
|
const push = await TrezorConnect.pushTransaction({
|
||||||
tx: serializedTx,
|
tx: serializedTx,
|
||||||
coin: network.network,
|
coin: network.shortcut,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!push.success) {
|
if (!push.success) {
|
||||||
|
@ -174,7 +174,7 @@ export const addressLabel = ($state: State): PayloadAction<State> => (dispatch:
|
|||||||
const savedAccounts = getState().accounts.filter(a => a.address.toLowerCase() === address.toLowerCase());
|
const savedAccounts = getState().accounts.filter(a => a.address.toLowerCase() === address.toLowerCase());
|
||||||
if (savedAccounts.length > 0) {
|
if (savedAccounts.length > 0) {
|
||||||
// check if found account belongs to this network
|
// check if found account belongs to this network
|
||||||
const currentNetworkAccount = savedAccounts.find(a => a.network === network.network);
|
const currentNetworkAccount = savedAccounts.find(a => a.network === network.shortcut);
|
||||||
if (currentNetworkAccount) {
|
if (currentNetworkAccount) {
|
||||||
const device = findDevice(getState().devices, currentNetworkAccount.deviceID, currentNetworkAccount.deviceState);
|
const device = findDevice(getState().devices, currentNetworkAccount.deviceID, currentNetworkAccount.deviceState);
|
||||||
if (device) {
|
if (device) {
|
||||||
@ -184,8 +184,8 @@ export const addressLabel = ($state: State): PayloadAction<State> => (dispatch:
|
|||||||
// corner-case: the same derivation path is used on different networks
|
// corner-case: the same derivation path is used on different networks
|
||||||
const otherNetworkAccount = savedAccounts[0];
|
const otherNetworkAccount = savedAccounts[0];
|
||||||
const device = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState);
|
const device = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState);
|
||||||
const { coins } = getState().localStorage.config;
|
const { networks } = getState().localStorage.config;
|
||||||
const otherNetwork = coins.find(c => c.network === otherNetworkAccount.network);
|
const otherNetwork = networks.find(c => c.shortcut === otherNetworkAccount.network);
|
||||||
if (device && otherNetwork) {
|
if (device && otherNetwork) {
|
||||||
state.warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`;
|
state.warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`;
|
||||||
}
|
}
|
||||||
|
@ -56,14 +56,16 @@ export const setBalance = (tokenAddress: string, ethAddress: string, balance: st
|
|||||||
const newState: Array<Token> = [...getState().tokens];
|
const newState: Array<Token> = [...getState().tokens];
|
||||||
const token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress);
|
const token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress);
|
||||||
if (token) {
|
if (token) {
|
||||||
token.loaded = true;
|
const others = newState.filter(t => t !== token);
|
||||||
token.balance = balance;
|
dispatch({
|
||||||
|
type: TOKEN.SET_BALANCE,
|
||||||
|
payload: others.concat([{
|
||||||
|
...token,
|
||||||
|
loaded: true,
|
||||||
|
balance,
|
||||||
|
}]),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: TOKEN.SET_BALANCE,
|
|
||||||
payload: newState,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
|
||||||
|
@ -36,7 +36,7 @@ export type TrezorConnectAction = {
|
|||||||
type: typeof CONNECT.INITIALIZATION_ERROR,
|
type: typeof CONNECT.INITIALIZATION_ERROR,
|
||||||
error: string
|
error: string
|
||||||
} | {
|
} | {
|
||||||
type: typeof CONNECT.COIN_CHANGED,
|
type: typeof CONNECT.NETWORK_CHANGED,
|
||||||
payload: {
|
payload: {
|
||||||
network: string
|
network: string
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,8 @@ export type WalletAction = {
|
|||||||
} | {
|
} | {
|
||||||
type: typeof WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
|
type: typeof WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
|
||||||
devices: Array<TrezorDevice>
|
devices: Array<TrezorDevice>
|
||||||
|
} | {
|
||||||
|
type: typeof WALLET.SHOW_BETA_DISCLAIMER | typeof WALLET.HIDE_BETA_DISCLAIMER,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
|
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
|
||||||
@ -51,6 +53,10 @@ export const init = (): ThunkAction => (dispatch: Dispatch): void => {
|
|||||||
window.addEventListener('offline', updateOnlineStatus);
|
window.addEventListener('offline', updateOnlineStatus);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const hideBetaDisclaimer = (): WalletAction => ({
|
||||||
|
type: WALLET.HIDE_BETA_DISCLAIMER,
|
||||||
|
});
|
||||||
|
|
||||||
export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({
|
export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({
|
||||||
type: WALLET.TOGGLE_DEVICE_DROPDOWN,
|
type: WALLET.TOGGLE_DEVICE_DROPDOWN,
|
||||||
opened,
|
opened,
|
||||||
|
@ -57,15 +57,15 @@ export const initWeb3 = (network: string, urlIndex: number = 0): PromiseAction<W
|
|||||||
// requested web3 wasn't initialized or is disconnected
|
// requested web3 wasn't initialized or is disconnected
|
||||||
// initialize again
|
// initialize again
|
||||||
const { config, ERC20Abi } = getState().localStorage;
|
const { config, ERC20Abi } = getState().localStorage;
|
||||||
const coin = config.coins.find(c => c.network === network);
|
const networkData = config.networks.find(c => c.shortcut === network);
|
||||||
if (!coin) {
|
if (!networkData) {
|
||||||
// coin not found
|
// network not found
|
||||||
reject(new Error(`Network ${network} not found in application config.`));
|
reject(new Error(`Network ${network} not found in application config.`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get first url
|
// get first url
|
||||||
const url = coin.web3[urlIndex];
|
const url = networkData.web3[urlIndex];
|
||||||
if (!url) {
|
if (!url) {
|
||||||
reject(new Error('Web3 backend is not responding'));
|
reject(new Error('Web3 backend is not responding'));
|
||||||
return;
|
return;
|
||||||
@ -80,7 +80,7 @@ export const initWeb3 = (network: string, urlIndex: number = 0): PromiseAction<W
|
|||||||
const newInstance = {
|
const newInstance = {
|
||||||
network,
|
network,
|
||||||
web3,
|
web3,
|
||||||
chainId: coin.chainId,
|
chainId: networkData.chainId,
|
||||||
erc20: new web3.eth.Contract(ERC20Abi),
|
erc20: new web3.eth.Contract(ERC20Abi),
|
||||||
latestBlock,
|
latestBlock,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
|
@ -10,7 +10,7 @@ export const INITIALIZATION_ERROR: 'connect__init_error' = 'connect__init_error'
|
|||||||
|
|
||||||
export const DEVICE_FROM_STORAGE: 'connect__device_from_storage' = 'connect__device_from_storage';
|
export const DEVICE_FROM_STORAGE: 'connect__device_from_storage' = 'connect__device_from_storage';
|
||||||
export const AUTH_DEVICE: 'connect__auth_device' = 'connect__auth_device';
|
export const AUTH_DEVICE: 'connect__auth_device' = 'connect__auth_device';
|
||||||
export const COIN_CHANGED: 'connect__coin_changed' = 'connect__coin_changed';
|
export const NETWORK_CHANGED: 'connect__network_changed' = 'connect__network_changed';
|
||||||
|
|
||||||
export const REMEMBER_REQUEST: 'connect__remember_request' = 'connect__remember_request';
|
export const REMEMBER_REQUEST: 'connect__remember_request' = 'connect__remember_request';
|
||||||
export const FORGET_REQUEST: 'connect__forget_request' = 'connect__forget_request';
|
export const FORGET_REQUEST: 'connect__forget_request' = 'connect__forget_request';
|
||||||
|
@ -6,5 +6,7 @@ export const ONLINE_STATUS: 'wallet__online_status' = 'wallet__online_status';
|
|||||||
|
|
||||||
export const SET_SELECTED_DEVICE: 'wallet__set_selected_device' = 'wallet__set_selected_device';
|
export const SET_SELECTED_DEVICE: 'wallet__set_selected_device' = 'wallet__set_selected_device';
|
||||||
export const UPDATE_SELECTED_DEVICE: 'wallet__update_selected_device' = 'wallet__update_selected_device';
|
export const UPDATE_SELECTED_DEVICE: 'wallet__update_selected_device' = 'wallet__update_selected_device';
|
||||||
|
export const SHOW_BETA_DISCLAIMER: 'wallet__show_beta_disclaimer' = 'wallet__show_beta_disclaimer';
|
||||||
|
export const HIDE_BETA_DISCLAIMER: 'wallet__hide_beta_disclaimer' = 'wallet__hide_beta_disclaimer';
|
||||||
|
|
||||||
export const CLEAR_UNAVAILABLE_DEVICE_DATA: 'wallet__clear_unavailable_device_data' = 'wallet__clear_unavailable_device_data';
|
export const CLEAR_UNAVAILABLE_DEVICE_DATA: 'wallet__clear_unavailable_device_data' = 'wallet__clear_unavailable_device_data';
|
@ -75,7 +75,7 @@ const Header = (): React$Element<string> => (
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
</Left>
|
</Left>
|
||||||
<Right>
|
<Right>
|
||||||
<A href="https://trezor.io/" target="_blank" rel="noreferrer noopener">TREZOR</A>
|
<A href="https://trezor.io/" target="_blank" rel="noreferrer noopener">Trezor</A>
|
||||||
<A href="https://wiki.trezor.io/" target="_blank" rel="noreferrer noopener">Wiki</A>
|
<A href="https://wiki.trezor.io/" target="_blank" rel="noreferrer noopener">Wiki</A>
|
||||||
<A href="https://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</A>
|
<A href="https://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</A>
|
||||||
<A href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</A>
|
<A href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</A>
|
||||||
|
@ -19,32 +19,21 @@ class CoinLogo extends PureComponent {
|
|||||||
this.longIcons = ['etc', 'eth', 'trop'];
|
this.longIcons = ['etc', 'eth', 'trop'];
|
||||||
}
|
}
|
||||||
|
|
||||||
getIcon() {
|
hasLongIcon(network) {
|
||||||
const { coinNetwork, coinId } = this.props;
|
|
||||||
let coinImgName = coinNetwork;
|
|
||||||
if (coinImgName === 'ethereum') {
|
|
||||||
coinImgName = 'eth';
|
|
||||||
} else if (coinImgName === 'ethereum-classic') {
|
|
||||||
coinImgName = 'etc';
|
|
||||||
}
|
|
||||||
return coinImgName || coinId;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasLongIcon(coinImgName) {
|
|
||||||
let hasLongIcon = false;
|
let hasLongIcon = false;
|
||||||
if (this.longIcons.includes(coinImgName)) {
|
if (this.longIcons.includes(network)) {
|
||||||
hasLongIcon = true;
|
hasLongIcon = true;
|
||||||
}
|
}
|
||||||
return hasLongIcon;
|
return hasLongIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const iconName = this.getIcon();
|
const { network } = this.props;
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Logo
|
<Logo
|
||||||
hasLongIcon={this.hasLongIcon(iconName)}
|
hasLongIcon={this.hasLongIcon(network)}
|
||||||
src={require(`./images/${iconName}.png`)} // eslint-disable-line
|
src={require(`./images/${network}.png`)} // eslint-disable-line
|
||||||
/>
|
/>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
@ -52,8 +41,7 @@ class CoinLogo extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CoinLogo.propTypes = {
|
CoinLogo.propTypes = {
|
||||||
coinId: PropTypes.string,
|
network: PropTypes.string,
|
||||||
coinNetwork: PropTypes.string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CoinLogo;
|
export default CoinLogo;
|
||||||
|
@ -41,7 +41,7 @@ const ConfirmAddress = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Header>
|
<Header>
|
||||||
<H3>Confirm address on TREZOR</H3>
|
<H3>Confirm address on Trezor</H3>
|
||||||
<P>Please compare your address on device with address shown bellow.</P>
|
<P>Please compare your address on device with address shown bellow.</P>
|
||||||
</Header>
|
</Header>
|
||||||
<Content>
|
<Content>
|
||||||
|
@ -102,7 +102,7 @@ class ConfirmUnverifiedAddress extends PureComponent<Props> {
|
|||||||
<Icon size={20} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
<Icon size={20} color={colors.TEXT_SECONDARY} icon={icons.CLOSE} />
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
<H2>{ deviceStatus }</H2>
|
<H2>{ deviceStatus }</H2>
|
||||||
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</StyledP>
|
<StyledP isSmaller>To prevent phishing attacks, you should verify the address on your Trezor first. { claim } to continue with the verification process.</StyledP>
|
||||||
<Row>
|
<Row>
|
||||||
<StyledButton onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</StyledButton>
|
<StyledButton onClick={() => (!account ? this.verifyAddress() : 'false')}>Try again</StyledButton>
|
||||||
<StyledButton isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</StyledButton>
|
<StyledButton isWhite onClick={() => this.showUnverifiedAddress()}>Show unverified address</StyledButton>
|
||||||
|
@ -63,7 +63,7 @@ class ForgetDevice extends PureComponent<Props> {
|
|||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<H3>Forget { this.props.device.instanceLabel }?</H3>
|
<H3>Forget { this.props.device.instanceLabel }?</H3>
|
||||||
<StyledP isSmaller>Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your TREZOR again.</StyledP>
|
<StyledP isSmaller>Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your Trezor again.</StyledP>
|
||||||
<Row>
|
<Row>
|
||||||
<StyledButton onClick={() => this.forget()}>Forget</StyledButton>
|
<StyledButton onClick={() => this.forget()}>Forget</StyledButton>
|
||||||
<StyledButton isWhite onClick={this.props.onCancel}>Don't forget</StyledButton>
|
<StyledButton isWhite onClick={this.props.onCancel}>Don't forget</StyledButton>
|
||||||
|
@ -126,7 +126,7 @@ class RememberDevice extends PureComponent<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<H3>Forget {label}?</H3>
|
<H3>Forget {label}?</H3>
|
||||||
<StyledP isSmaller>Would you like TREZOR Wallet to forget your { devicePlural }, so that it is still visible even while disconnected?</StyledP>
|
<StyledP isSmaller>Would you like Trezor Wallet to forget your { devicePlural }, so that it is still visible even while disconnected?</StyledP>
|
||||||
<Column>
|
<Column>
|
||||||
<StyledButton onClick={() => this.forget()}>
|
<StyledButton onClick={() => this.forget()}>
|
||||||
<ButtonContent>
|
<ButtonContent>
|
||||||
|
@ -53,7 +53,7 @@ const NemWallet = (props: Props) => (
|
|||||||
</StyledLink>
|
</StyledLink>
|
||||||
<H3>NEM Wallet</H3>
|
<H3>NEM Wallet</H3>
|
||||||
<P isSmaller>We have partnered up with the NEM Foundation to provide you with a full-fledged NEM Wallet.</P>
|
<P isSmaller>We have partnered up with the NEM Foundation to provide you with a full-fledged NEM Wallet.</P>
|
||||||
<H4>Make sure you download the Universal Client for TREZOR support.</H4>
|
<H4>Make sure you download the Universal Client for Trezor support.</H4>
|
||||||
<Img src={NemImage} />
|
<Img src={NemImage} />
|
||||||
<Link href={coins.find(i => i.id === 'xem').url}>
|
<Link href={coins.find(i => i.id === 'xem').url}>
|
||||||
<StyledButton>Go to nem.io</StyledButton>
|
<StyledButton>Go to nem.io</StyledButton>
|
||||||
|
@ -147,7 +147,7 @@ class Pin extends PureComponent<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<H2>Enter { device.label } PIN</H2>
|
<H2>Enter { device.label } PIN</H2>
|
||||||
<P isSmaller>The PIN layout is displayed on your TREZOR.</P>
|
<P isSmaller>The PIN layout is displayed on your Trezor.</P>
|
||||||
<InputRow>
|
<InputRow>
|
||||||
<PinInput value={pin} onDeleteClick={() => this.onPinBackspace()} />
|
<PinInput value={pin} onDeleteClick={() => this.onPinBackspace()} />
|
||||||
</InputRow>
|
</InputRow>
|
||||||
|
@ -20,7 +20,7 @@ export default (props: Props) => {
|
|||||||
[{
|
[{
|
||||||
label: 'Connect',
|
label: 'Connect',
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
await props.blockchainReconnect(network.network);
|
await props.blockchainReconnect(network.shortcut);
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
@ -2,47 +2,47 @@ export default [
|
|||||||
{
|
{
|
||||||
id: 'btc',
|
id: 'btc',
|
||||||
coinName: 'Bitcoin',
|
coinName: 'Bitcoin',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=btc',
|
url: '../#/?coin=btc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'bch',
|
id: 'bch',
|
||||||
coinName: 'Bitcoin Cash',
|
coinName: 'Bitcoin Cash',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=bch',
|
url: '../#/?coin=bch',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'btg',
|
id: 'btg',
|
||||||
coinName: 'Bitcoin Gold',
|
coinName: 'Bitcoin Gold',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=btg',
|
url: '../#/?coin=btg',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'dash',
|
id: 'dash',
|
||||||
coinName: 'Dash',
|
coinName: 'Dash',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=dash',
|
url: '../#/?coin=dash',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'doge',
|
id: 'doge',
|
||||||
coinName: 'Dogecoin',
|
coinName: 'Dogecoin',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=doge',
|
url: '../#/?coin=doge',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'ltc',
|
id: 'ltc',
|
||||||
coinName: 'Litecoin',
|
coinName: 'Litecoin',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=ltc',
|
url: '../#/?coin=ltc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'nmc',
|
id: 'nmc',
|
||||||
coinName: 'Namecoin',
|
coinName: 'Namecoin',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=nmc',
|
url: '../#/?coin=nmc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'vtc',
|
id: 'vtc',
|
||||||
coinName: 'Vertcoin',
|
coinName: 'Vertcoin',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=vtc',
|
url: '../#/?coin=vtc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'zec',
|
id: 'zec',
|
||||||
coinName: 'Zcash',
|
coinName: 'Zcash',
|
||||||
url: 'https://wallet.trezor.io/#/?coin=zec',
|
url: '../#/?coin=zec',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'xem',
|
id: 'xem',
|
||||||
|
@ -150,7 +150,7 @@ export type Action =
|
|||||||
export type State = ReducersState;
|
export type State = ReducersState;
|
||||||
|
|
||||||
// reexport reduces types
|
// reexport reduces types
|
||||||
export type { Coin } from 'reducers/LocalStorageReducer';
|
export type { Network } from 'reducers/LocalStorageReducer';
|
||||||
export type { Account } from 'reducers/AccountsReducer';
|
export type { Account } from 'reducers/AccountsReducer';
|
||||||
export type { Discovery } from 'reducers/DiscoveryReducer';
|
export type { Discovery } from 'reducers/DiscoveryReducer';
|
||||||
export type { Token } from 'reducers/TokensReducer';
|
export type { Token } from 'reducers/TokensReducer';
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
<title>Ethereum Wallet | TREZOR</title>
|
<title>Ethereum Wallet | Trezor</title>
|
||||||
<meta name="title" content="Trezor Wallet" />
|
<meta name="title" content="Trezor Wallet" />
|
||||||
<meta name="description" content="Trezor Wallet" />
|
<meta name="description" content="Trezor Wallet" />
|
||||||
<meta name="keywords" content="trezor wallet" />
|
<meta name="keywords" content="trezor wallet" />
|
||||||
|
@ -5,7 +5,7 @@ import { BLOCKCHAIN } from 'trezor-connect';
|
|||||||
import type { Action } from 'flowtype';
|
import type { Action } from 'flowtype';
|
||||||
|
|
||||||
export type BlockchainNetwork = {
|
export type BlockchainNetwork = {
|
||||||
+name: string;
|
+shortcut: string;
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,16 +13,16 @@ export type State = Array<BlockchainNetwork>;
|
|||||||
|
|
||||||
export const initialState: State = [];
|
export const initialState: State = [];
|
||||||
|
|
||||||
const find = (state: State, name: string): number => state.findIndex(b => b.name === name);
|
const find = (state: State, shortcut: string): number => state.findIndex(b => b.shortcut === shortcut);
|
||||||
|
|
||||||
const connect = (state: State, action: any): State => {
|
const connect = (state: State, action: any): State => {
|
||||||
const name = action.payload.coin.shortcut.toLowerCase();
|
const shortcut = action.payload.coin.shortcut.toLowerCase();
|
||||||
const network: BlockchainNetwork = {
|
const network: BlockchainNetwork = {
|
||||||
name,
|
shortcut,
|
||||||
connected: true,
|
connected: true,
|
||||||
};
|
};
|
||||||
const newState: State = [...state];
|
const newState: State = [...state];
|
||||||
const index: number = find(newState, name);
|
const index: number = find(newState, shortcut);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
newState[index] = network;
|
newState[index] = network;
|
||||||
} else {
|
} else {
|
||||||
@ -32,13 +32,13 @@ const connect = (state: State, action: any): State => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const disconnect = (state: State, action: any): State => {
|
const disconnect = (state: State, action: any): State => {
|
||||||
const name = action.payload.coin.shortcut.toLowerCase();
|
const shortcut = action.payload.coin.shortcut.toLowerCase();
|
||||||
const network: BlockchainNetwork = {
|
const network: BlockchainNetwork = {
|
||||||
name,
|
shortcut,
|
||||||
connected: false,
|
connected: false,
|
||||||
};
|
};
|
||||||
const newState: State = [...state];
|
const newState: State = [...state];
|
||||||
const index: number = find(newState, name);
|
const index: number = find(newState, shortcut);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
newState[index] = network;
|
newState[index] = network;
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,9 +5,9 @@ import * as STORAGE from 'actions/constants/localStorage';
|
|||||||
|
|
||||||
import type { Action } from 'flowtype';
|
import type { Action } from 'flowtype';
|
||||||
|
|
||||||
export type Coin = {
|
export type Network = {
|
||||||
name: string;
|
name: string;
|
||||||
network: string;
|
shortcut: string;
|
||||||
symbol: string;
|
symbol: string;
|
||||||
bip44: string;
|
bip44: string;
|
||||||
defaultGasLimit: number;
|
defaultGasLimit: number;
|
||||||
@ -57,40 +57,27 @@ export type FiatValueTicker = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Config = {
|
export type Config = {
|
||||||
coins: Array<Coin>;
|
networks: Array<Network>;
|
||||||
fiatValueTickers: Array<FiatValueTicker>;
|
fiatValueTickers: Array<FiatValueTicker>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CustomBackend = {
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
error: ?string;
|
error: ?string;
|
||||||
config: Config;
|
config: Config;
|
||||||
ERC20Abi: Array<Object>;
|
ERC20Abi: Array<Object>;
|
||||||
tokens: TokensCollection;
|
tokens: TokensCollection;
|
||||||
customBackend: Array<CustomBackend>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
error: null,
|
error: null,
|
||||||
config: {
|
config: {
|
||||||
coins: [],
|
networks: [],
|
||||||
fiatValueTickers: [],
|
fiatValueTickers: [],
|
||||||
},
|
},
|
||||||
ERC20Abi: [],
|
ERC20Abi: [],
|
||||||
tokens: {},
|
tokens: {},
|
||||||
customBackend: [
|
|
||||||
{
|
|
||||||
name: 'Custom1',
|
|
||||||
slug: 'custom1',
|
|
||||||
url: 'http://google.com',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function localStorage(state: State = initialState, action: Action): State {
|
export default function localStorage(state: State = initialState, action: Action): State {
|
||||||
|
@ -4,7 +4,7 @@ import * as ACCOUNT from 'actions/constants/account';
|
|||||||
import type {
|
import type {
|
||||||
Action,
|
Action,
|
||||||
Account,
|
Account,
|
||||||
Coin,
|
Network,
|
||||||
Token,
|
Token,
|
||||||
PendingTx,
|
PendingTx,
|
||||||
Discovery,
|
Discovery,
|
||||||
@ -13,7 +13,7 @@ import type {
|
|||||||
export type State = {
|
export type State = {
|
||||||
location: string;
|
location: string;
|
||||||
account: ?Account;
|
account: ?Account;
|
||||||
network: ?Coin;
|
network: ?Network;
|
||||||
tokens: Array<Token>,
|
tokens: Array<Token>,
|
||||||
pending: Array<PendingTx>,
|
pending: Array<PendingTx>,
|
||||||
discovery: ?Discovery,
|
discovery: ?Discovery,
|
||||||
|
@ -7,13 +7,13 @@ import * as MODAL from 'actions/constants/modal';
|
|||||||
import * as WALLET from 'actions/constants/wallet';
|
import * as WALLET from 'actions/constants/wallet';
|
||||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||||
|
|
||||||
|
|
||||||
import type { Action, RouterLocationState, TrezorDevice } from 'flowtype';
|
import type { Action, RouterLocationState, TrezorDevice } from 'flowtype';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
dropdownOpened: boolean;
|
dropdownOpened: boolean;
|
||||||
|
showBetaDisclaimer: boolean;
|
||||||
initialParams: ?RouterLocationState;
|
initialParams: ?RouterLocationState;
|
||||||
initialPathname: ?string;
|
initialPathname: ?string;
|
||||||
disconnectRequest: ?TrezorDevice;
|
disconnectRequest: ?TrezorDevice;
|
||||||
@ -24,6 +24,7 @@ const initialState: State = {
|
|||||||
ready: false,
|
ready: false,
|
||||||
online: navigator.onLine,
|
online: navigator.onLine,
|
||||||
dropdownOpened: false,
|
dropdownOpened: false,
|
||||||
|
showBetaDisclaimer: false,
|
||||||
initialParams: null,
|
initialParams: null,
|
||||||
initialPathname: null,
|
initialPathname: null,
|
||||||
disconnectRequest: null,
|
disconnectRequest: null,
|
||||||
@ -86,6 +87,17 @@ export default function wallet(state: State = initialState, action: Action): Sta
|
|||||||
selectedDevice: action.device,
|
selectedDevice: action.device,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case WALLET.SHOW_BETA_DISCLAIMER:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
showBetaDisclaimer: true,
|
||||||
|
};
|
||||||
|
case WALLET.HIDE_BETA_DISCLAIMER:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
showBetaDisclaimer: false,
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import type {
|
|||||||
Device,
|
Device,
|
||||||
TrezorDevice,
|
TrezorDevice,
|
||||||
Account,
|
Account,
|
||||||
Coin,
|
Network,
|
||||||
Discovery,
|
Discovery,
|
||||||
Token,
|
Token,
|
||||||
PendingTx,
|
PendingTx,
|
||||||
@ -66,13 +66,13 @@ export const getSelectedAccount = (state: State): ?Account => {
|
|||||||
return state.accounts.find(a => a.deviceState === device.state && a.index === index && a.network === locationState.network);
|
return state.accounts.find(a => a.deviceState === device.state && a.index === index && a.network === locationState.network);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSelectedNetwork = (state: State): ?Coin => {
|
export const getSelectedNetwork = (state: State): ?Network => {
|
||||||
const device = state.wallet.selectedDevice;
|
const device = state.wallet.selectedDevice;
|
||||||
const { coins } = state.localStorage.config;
|
const { networks } = state.localStorage.config;
|
||||||
const locationState = state.router.location.state;
|
const locationState = state.router.location.state;
|
||||||
if (!device || !locationState.network) return null;
|
if (!device || !locationState.network) return null;
|
||||||
|
|
||||||
return coins.find(c => c.network === locationState.network);
|
return networks.find(c => c.shortcut === locationState.network);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDiscoveryProcess = (state: State): ?Discovery => {
|
export const getDiscoveryProcess = (state: State): ?Discovery => {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
import { DEVICE } from 'trezor-connect';
|
import { DEVICE } from 'trezor-connect';
|
||||||
import * as LocalStorageActions from 'actions/LocalStorageActions';
|
import * as LocalStorageActions from 'actions/LocalStorageActions';
|
||||||
// import * as WalletActions from 'actions/WalletActions';
|
|
||||||
|
|
||||||
import * as CONNECT from 'actions/constants/TrezorConnect';
|
import * as CONNECT from 'actions/constants/TrezorConnect';
|
||||||
import * as TOKEN from 'actions/constants/token';
|
import * as TOKEN from 'actions/constants/token';
|
||||||
@ -9,95 +8,45 @@ import * as ACCOUNT from 'actions/constants/account';
|
|||||||
import * as DISCOVERY from 'actions/constants/discovery';
|
import * as DISCOVERY from 'actions/constants/discovery';
|
||||||
import * as SEND from 'actions/constants/send';
|
import * as SEND from 'actions/constants/send';
|
||||||
import * as PENDING from 'actions/constants/pendingTx';
|
import * as PENDING from 'actions/constants/pendingTx';
|
||||||
import { findAccountTokens } from 'reducers/TokensReducer';
|
import * as WALLET from 'actions/constants/wallet';
|
||||||
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Middleware,
|
Middleware,
|
||||||
MiddlewareAPI,
|
MiddlewareAPI,
|
||||||
MiddlewareDispatch,
|
MiddlewareDispatch,
|
||||||
Dispatch,
|
|
||||||
Action,
|
Action,
|
||||||
GetState,
|
|
||||||
TrezorDevice,
|
|
||||||
} from 'flowtype';
|
} from 'flowtype';
|
||||||
|
|
||||||
import type { Account } from 'reducers/AccountsReducer';
|
|
||||||
import type { Token } from 'reducers/TokensReducer';
|
|
||||||
import type { PendingTx } from 'reducers/PendingTxReducer';
|
|
||||||
import type { Discovery } from 'reducers/DiscoveryReducer';
|
|
||||||
|
|
||||||
|
|
||||||
// https://github.com/STRML/react-localstorage/blob/master/react-localstorage.js
|
|
||||||
// or
|
|
||||||
// https://www.npmjs.com/package/redux-react-session
|
|
||||||
|
|
||||||
const findAccounts = (devices: Array<TrezorDevice>, accounts: Array<Account>): Array<Account> => devices.reduce((arr, dev) => arr.concat(accounts.filter(a => a.deviceState === dev.state)), []);
|
|
||||||
|
|
||||||
const findTokens = (accounts: Array<Account>, tokens: Array<Token>): Array<Token> => accounts.reduce((arr, account) => arr.concat(findAccountTokens(tokens, account)), []);
|
|
||||||
|
|
||||||
const findDiscovery = (devices: Array<TrezorDevice>, discovery: Array<Discovery>): Array<Discovery> => devices.reduce((arr, dev) => arr.concat(discovery.filter(a => a.deviceState === dev.state && a.publicKey.length > 0)), []);
|
|
||||||
|
|
||||||
const findPendingTxs = (accounts: Array<Account>, pending: Array<PendingTx>): Array<PendingTx> => accounts.reduce((result, account) => result.concat(pending.filter(p => p.address === account.address && p.network === account.network)), []);
|
|
||||||
|
|
||||||
const save = (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const devices: Array<TrezorDevice> = getState().devices.filter(d => d.features && d.remember === true);
|
|
||||||
const accounts: Array<Account> = findAccounts(devices, getState().accounts);
|
|
||||||
const tokens: Array<Token> = findTokens(accounts, getState().tokens);
|
|
||||||
const pending: Array<PendingTx> = findPendingTxs(accounts, getState().pending);
|
|
||||||
const discovery: Array<Discovery> = findDiscovery(devices, getState().discovery);
|
|
||||||
|
|
||||||
// save devices
|
|
||||||
dispatch(LocalStorageActions.save('devices', JSON.stringify(devices)));
|
|
||||||
|
|
||||||
// save already preloaded accounts
|
|
||||||
dispatch(LocalStorageActions.save('accounts', JSON.stringify(accounts)));
|
|
||||||
|
|
||||||
// save discovery state
|
|
||||||
dispatch(LocalStorageActions.save('discovery', JSON.stringify(discovery)));
|
|
||||||
|
|
||||||
// tokens
|
|
||||||
dispatch(LocalStorageActions.save('tokens', JSON.stringify(tokens)));
|
|
||||||
|
|
||||||
// pending transactions
|
|
||||||
dispatch(LocalStorageActions.save('pending', JSON.stringify(pending)));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => (action: Action): Action => {
|
const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => (action: Action): Action => {
|
||||||
// Application live cycle starts here
|
// pass action
|
||||||
// if (action.type === LOCATION_CHANGE) {
|
|
||||||
// const { location } = api.getState().router;
|
|
||||||
// if (!location) {
|
|
||||||
// api.dispatch( WalletActions.init() );
|
|
||||||
// // load data from config.json and local storage
|
|
||||||
// api.dispatch( LocalStorageActions.loadData() );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
next(action);
|
next(action);
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case WALLET.HIDE_BETA_DISCLAIMER:
|
||||||
|
api.dispatch(LocalStorageActions.hideBetaDisclaimer());
|
||||||
|
break;
|
||||||
// first time saving
|
// first time saving
|
||||||
case CONNECT.REMEMBER:
|
case CONNECT.REMEMBER:
|
||||||
save(api.dispatch, api.getState);
|
api.dispatch(LocalStorageActions.save());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOKEN.ADD:
|
case TOKEN.ADD:
|
||||||
case TOKEN.REMOVE:
|
case TOKEN.REMOVE:
|
||||||
case TOKEN.SET_BALANCE:
|
case TOKEN.SET_BALANCE:
|
||||||
save(api.dispatch, api.getState);
|
api.dispatch(LocalStorageActions.save());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACCOUNT.CREATE:
|
case ACCOUNT.CREATE:
|
||||||
case ACCOUNT.SET_BALANCE:
|
case ACCOUNT.SET_BALANCE:
|
||||||
case ACCOUNT.SET_NONCE:
|
case ACCOUNT.SET_NONCE:
|
||||||
save(api.dispatch, api.getState);
|
api.dispatch(LocalStorageActions.save());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DISCOVERY.START:
|
case DISCOVERY.START:
|
||||||
case DISCOVERY.STOP:
|
case DISCOVERY.STOP:
|
||||||
case DISCOVERY.COMPLETE:
|
case DISCOVERY.COMPLETE:
|
||||||
save(api.dispatch, api.getState);
|
api.dispatch(LocalStorageActions.save());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONNECT.FORGET:
|
case CONNECT.FORGET:
|
||||||
@ -107,13 +56,13 @@ const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: Middlewar
|
|||||||
case DEVICE.CHANGED:
|
case DEVICE.CHANGED:
|
||||||
case DEVICE.DISCONNECT:
|
case DEVICE.DISCONNECT:
|
||||||
case CONNECT.AUTH_DEVICE:
|
case CONNECT.AUTH_DEVICE:
|
||||||
save(api.dispatch, api.getState);
|
api.dispatch(LocalStorageActions.save());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SEND.TX_COMPLETE:
|
case SEND.TX_COMPLETE:
|
||||||
case PENDING.TX_RESOLVED:
|
case PENDING.TX_RESOLVED:
|
||||||
case PENDING.TX_REJECTED:
|
case PENDING.TX_REJECTED:
|
||||||
save(api.dispatch, api.getState);
|
api.dispatch(LocalStorageActions.save());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -70,10 +70,10 @@ const WalletService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispa
|
|||||||
const prevLocation = prevState.router.location;
|
const prevLocation = prevState.router.location;
|
||||||
const currentLocation = api.getState().router.location;
|
const currentLocation = api.getState().router.location;
|
||||||
if (action.type === LOCATION_CHANGE && prevLocation.pathname !== currentLocation.pathname) {
|
if (action.type === LOCATION_CHANGE && prevLocation.pathname !== currentLocation.pathname) {
|
||||||
// watch for coin change
|
// watch for network change
|
||||||
if (prevLocation.state.network !== currentLocation.state.network) {
|
if (prevLocation.state.network !== currentLocation.state.network) {
|
||||||
api.dispatch({
|
api.dispatch({
|
||||||
type: CONNECT.COIN_CHANGED,
|
type: CONNECT.NETWORK_CHANGED,
|
||||||
payload: {
|
payload: {
|
||||||
network: currentLocation.state.network,
|
network: currentLocation.state.network,
|
||||||
},
|
},
|
||||||
|
41
src/utils/__tests__/__snapshots__/notification.test.js.snap
Normal file
41
src/utils/__tests__/__snapshots__/notification.test.js.snap
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`device utils get icon 1`] = `
|
||||||
|
Array [
|
||||||
|
"M693.024 330.944c-99.968-99.936-262.080-99.936-362.048 0s-99.968 262.112 0 362.080c99.968 100 262.144 99.936 362.048 0 99.968-99.904 99.968-262.176 0-362.080zM507.904 300.192c27.008 0 48.992 21.984 48.992 49.088 0 27.296-21.984 49.472-48.992 49.472-27.264 0-49.536-22.176-49.536-49.472 0-27.552 21.728-49.088 49.536-49.088zM586.656 660.8c0 10.304-4.96 15.328-15.264 15.328h-126.464c-10.304 0-15.328-5.024-15.328-15.328v-32.256c0-10.304 5.024-15.264 15.328-15.264h23.36v-136.064h-23.872c-10.304 0-15.264-5.024-15.264-15.328v-32.224c0-10.304 4.96-15.264 15.264-15.264h88.288c10.304 0 15.264 4.96 15.264 15.264v183.648h23.424c10.304 0 15.264 4.96 15.264 15.264v32.224z",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`device utils get icon 2`] = `
|
||||||
|
Array [
|
||||||
|
"M693.12 330.88c-46.317-46.267-110.276-74.88-180.919-74.88-141.385 0-256 114.615-256 256s114.615 256 256 256c70.642 0 134.602-28.613 180.921-74.882l-0.002 0.002c46.387-46.337 75.081-110.377 75.081-181.12s-28.694-134.783-75.079-181.118l-0.002-0.002zM494.080 344.32h53.12c16 0 18.24 9.28 18.24 14.72v10.24l-10.88 194.56c0 14.4-8 17.28-18.88 17.28h-28.16c-10.56 0-17.28-2.88-18.88-17.92l-10.88-193.92v-10.56c-1.28-4.8 2.24-14.080 16.32-14.080zM521.28 717.76c-0.095 0.001-0.207 0.001-0.319 0.001-27.747 0-50.24-22.493-50.24-50.24s22.493-50.24 50.24-50.24c27.747 0 50.24 22.493 50.24 50.24 0 0.112 0 0.224-0.001 0.336v-0.017c0 0 0 0.001 0 0.001 0 27.634-22.311 50.057-49.903 50.239h-0.017z",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`device utils get icon 3`] = `
|
||||||
|
Array [
|
||||||
|
"M795.616 735.008l-264.896-465.44c-10.272-18.080-27.168-18.080-37.504 0l-264.864 465.44c-10.272 18.176-1.696 32.992 19.040 32.992h529.184c20.8 0 29.376-14.816 19.040-32.992zM549.76 673.12c0 10.464-8.48 18.976-18.912 18.976h-37.792c-10.336 0-18.912-8.512-18.912-18.976v-37.952c0-10.464 8.576-18.976 18.912-18.976h37.792c10.4 0 18.912 8.544 18.912 18.976v37.952zM549.76 559.264c0 10.464-8.48 18.976-18.912 18.976h-37.792c-10.336 0-18.912-8.512-18.912-18.976v-113.856c0-10.464 8.576-18.976 18.912-18.976h37.792c10.4 0 18.912 8.544 18.912 18.976v113.856z",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`device utils get icon 4`] = `
|
||||||
|
Array [
|
||||||
|
"M692.8 313.92l-1.92-1.92c-6.246-7.057-15.326-11.484-25.44-11.484s-19.194 4.427-25.409 11.448l-0.031 0.036-196.48 224-3.84 1.6-3.84-1.92-48.64-57.28c-7.010-7.905-17.193-12.862-28.533-12.862-21.031 0-38.080 17.049-38.080 38.080 0 7.495 2.165 14.485 5.905 20.377l-0.092-0.155 100.8 148.16c5.391 8.036 14.386 13.292 24.618 13.44h8.662c17.251-0.146 32.385-9.075 41.163-22.529l0.117-0.191 195.2-296.32c4.473-6.632 7.141-14.803 7.141-23.597 0-11.162-4.297-21.32-11.326-28.911l0.025 0.028z",
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`device utils get icon 5`] = `undefined`;
|
||||||
|
|
||||||
|
exports[`device utils get icon 6`] = `undefined`;
|
||||||
|
|
||||||
|
exports[`device utils get status 1`] = `"#1E7FF0"`;
|
||||||
|
|
||||||
|
exports[`device utils get status 2`] = `"#ED1212"`;
|
||||||
|
|
||||||
|
exports[`device utils get status 3`] = `"#EB8A00"`;
|
||||||
|
|
||||||
|
exports[`device utils get status 4`] = `"#01B757"`;
|
||||||
|
|
||||||
|
exports[`device utils get status 5`] = `null`;
|
||||||
|
|
||||||
|
exports[`device utils get status 6`] = `null`;
|
32
src/utils/__tests__/notification.test.js
Normal file
32
src/utils/__tests__/notification.test.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import * as nUtils from 'utils/notification';
|
||||||
|
|
||||||
|
describe('device utils', () => {
|
||||||
|
it('get status', () => {
|
||||||
|
const types = [
|
||||||
|
'info',
|
||||||
|
'error',
|
||||||
|
'warning',
|
||||||
|
'success',
|
||||||
|
'kdsjflds',
|
||||||
|
'',
|
||||||
|
];
|
||||||
|
|
||||||
|
types.forEach((type) => {
|
||||||
|
expect(nUtils.getColor(type)).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('get icon', () => {
|
||||||
|
const types = [
|
||||||
|
'info',
|
||||||
|
'error',
|
||||||
|
'warning',
|
||||||
|
'success',
|
||||||
|
'kdsjflds',
|
||||||
|
'',
|
||||||
|
];
|
||||||
|
|
||||||
|
types.forEach((type) => {
|
||||||
|
expect(nUtils.getIcon(type)).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
80
src/views/Landing/components/BetaDisclaimer/index.js
Normal file
80
src/views/Landing/components/BetaDisclaimer/index.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/* @flow */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
|
||||||
|
import colors from 'config/colors';
|
||||||
|
import icons from 'config/icons';
|
||||||
|
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
import Button from 'components/Button';
|
||||||
|
import P from 'components/Paragraph';
|
||||||
|
import { H2 } from 'components/Heading';
|
||||||
|
import * as WalletActions from 'actions/WalletActions';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
background: rgba(0, 0, 0, 0.35);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ModalWindow = styled.div`
|
||||||
|
margin: auto;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: ${colors.WHITE};
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 620px;
|
||||||
|
padding: 24px 48px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledP = styled(P)`
|
||||||
|
padding: 10px 0px;
|
||||||
|
font-size: 14px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledButton = styled(Button)`
|
||||||
|
margin: 10px 0px;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledIcon = styled(Icon)`
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const BetaDisclaimer = (props: { close: () => void }) => (
|
||||||
|
<Wrapper>
|
||||||
|
<ModalWindow>
|
||||||
|
<H2>You are opening Trezor Beta Wallet</H2>
|
||||||
|
<StyledP><i>Trezor Beta Wallet</i> is a public feature-testing version of the <i>Trezor Wallet</i>, offering the newest features before they are available to the general public.</StyledP>
|
||||||
|
<StyledP>In contrast, <i>Trezor Wallet</i> is feature-conservative, making sure that its functionality is maximally reliable and dependable for the general public.</StyledP>
|
||||||
|
<StyledP>
|
||||||
|
<StyledIcon
|
||||||
|
size={24}
|
||||||
|
color={colors.WARNING_PRIMARY}
|
||||||
|
icon={icons.WARNING}
|
||||||
|
/>
|
||||||
|
Please note that the <i>Trezor Beta Wallet</i> might be collecting anonymized usage data, especially error logs, for development purposes. The <i>Trezor Wallet</i> does not log any data.
|
||||||
|
</StyledP>
|
||||||
|
<StyledButton onClick={props.close}>OK, I understand</StyledButton>
|
||||||
|
</ModalWindow>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
null,
|
||||||
|
(dispatch: Dispatch) => ({
|
||||||
|
close: bindActionCreators(WalletActions.hideBetaDisclaimer, dispatch),
|
||||||
|
}),
|
||||||
|
)(BetaDisclaimer);
|
@ -108,8 +108,8 @@ class ConnectDevice extends PureComponent<Props> {
|
|||||||
<div>
|
<div>
|
||||||
<Title>
|
<Title>
|
||||||
<H2 claim>The private bank in your hands.</H2>
|
<H2 claim>The private bank in your hands.</H2>
|
||||||
<P>TREZOR Wallet is an easy-to-use interface for your TREZOR.</P>
|
<P>Trezor Wallet is an easy-to-use interface for your Trezor.</P>
|
||||||
<P>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</P>
|
<P>Trezor Wallet allows you to easily control your funds, manage your balance and initiate transfers.</P>
|
||||||
</Title>
|
</Title>
|
||||||
|
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
@ -118,7 +118,7 @@ class ConnectDevice extends PureComponent<Props> {
|
|||||||
{!this.props.showDisconnect && (
|
{!this.props.showDisconnect && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.getTrezorDeviceImage()}
|
{this.getTrezorDeviceImage()}
|
||||||
Connect TREZOR
|
Connect Trezor
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</ConnectTrezorWrapper>
|
</ConnectTrezorWrapper>
|
||||||
@ -141,13 +141,13 @@ class ConnectDevice extends PureComponent<Props> {
|
|||||||
<StyledLink
|
<StyledLink
|
||||||
to="/bridge"
|
to="/bridge"
|
||||||
isGreen
|
isGreen
|
||||||
>Try installing the TREZOR Bridge.
|
>Try installing the Trezor Bridge.
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
</P>
|
</P>
|
||||||
)}
|
)}
|
||||||
<P>
|
<P>
|
||||||
<FooterText>
|
<FooterText>
|
||||||
Don't have TREZOR?
|
Don't have Trezor?
|
||||||
</FooterText>
|
</FooterText>
|
||||||
<StyledLink
|
<StyledLink
|
||||||
href="https://trezor.io/"
|
href="https://trezor.io/"
|
||||||
|
@ -154,8 +154,8 @@ class InstallBridge extends PureComponent<Props, State> {
|
|||||||
<LandingWrapper loading={!target}>
|
<LandingWrapper loading={!target}>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Top>
|
<Top>
|
||||||
<TitleHeader>TREZOR Bridge<Version>{this.state.currentVersion}</Version></TitleHeader>
|
<TitleHeader>Trezor Bridge<Version>{this.state.currentVersion}</Version></TitleHeader>
|
||||||
<P>New communication tool to facilitate the connection between your TREZOR and your internet browser.</P>
|
<P>New communication tool to facilitate the connection between your Trezor and your internet browser.</P>
|
||||||
<Download>
|
<Download>
|
||||||
<SelectWrapper
|
<SelectWrapper
|
||||||
isSearchable={false}
|
isSearchable={false}
|
||||||
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||||||
import { isWebUSB } from 'utils/device';
|
import { isWebUSB } from 'utils/device';
|
||||||
|
|
||||||
import LandingWrapper from 'views/Landing/components/LandingWrapper';
|
import LandingWrapper from 'views/Landing/components/LandingWrapper';
|
||||||
|
import BetaDisclaimer from 'views/Landing/components/BetaDisclaimer';
|
||||||
import BrowserNotSupported from 'views/Landing/components/BrowserNotSupported';
|
import BrowserNotSupported from 'views/Landing/components/BrowserNotSupported';
|
||||||
import ConnectDevice from 'views/Landing/components/ConnectDevice';
|
import ConnectDevice from 'views/Landing/components/ConnectDevice';
|
||||||
import InstallBridge from 'views/Landing/views/InstallBridge/Container';
|
import InstallBridge from 'views/Landing/views/InstallBridge/Container';
|
||||||
@ -16,6 +17,8 @@ const Root = (props: Props) => {
|
|||||||
const localStorageError = props.localStorage.error;
|
const localStorageError = props.localStorage.error;
|
||||||
const connectError = props.connect.error;
|
const connectError = props.connect.error;
|
||||||
|
|
||||||
|
if (props.wallet.showBetaDisclaimer) return <BetaDisclaimer />;
|
||||||
|
|
||||||
const error = !initialized ? (localStorageError || connectError) : null;
|
const error = !initialized ? (localStorageError || connectError) : null;
|
||||||
const shouldShowUnsupportedBrowser = browserState.supported === false;
|
const shouldShowUnsupportedBrowser = browserState.supported === false;
|
||||||
const shouldShowInstallBridge = initialized && connectError;
|
const shouldShowInstallBridge = initialized && connectError;
|
||||||
|
@ -115,11 +115,11 @@ const AccountMenu = (props: Props) => {
|
|||||||
const baseUrl: string = urlParams.deviceInstance ? `/device/${urlParams.device}:${urlParams.deviceInstance}` : `/device/${urlParams.device}`;
|
const baseUrl: string = urlParams.deviceInstance ? `/device/${urlParams.device}:${urlParams.deviceInstance}` : `/device/${urlParams.device}`;
|
||||||
|
|
||||||
const { config } = props.localStorage;
|
const { config } = props.localStorage;
|
||||||
const selectedCoin = config.coins.find(c => c.network === location.state.network);
|
const network = config.networks.find(c => c.shortcut === location.state.network);
|
||||||
|
|
||||||
if (!selected || !selectedCoin) return null;
|
if (!selected || !network) return null;
|
||||||
|
|
||||||
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
|
const fiatRate = props.fiat.find(f => f.network === network.shortcut);
|
||||||
|
|
||||||
const deviceAccounts: Accounts = findDeviceAccounts(accounts, selected, location.state.network);
|
const deviceAccounts: Accounts = findDeviceAccounts(accounts, selected, location.state.network);
|
||||||
|
|
||||||
@ -130,14 +130,14 @@ const AccountMenu = (props: Props) => {
|
|||||||
let balance: string = 'Loading...';
|
let balance: string = 'Loading...';
|
||||||
if (account.balance !== '') {
|
if (account.balance !== '') {
|
||||||
const pending = stateUtils.getAccountPendingTx(props.pending, account);
|
const pending = stateUtils.getAccountPendingTx(props.pending, account);
|
||||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, selectedCoin.symbol);
|
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, network.symbol);
|
||||||
const availableBalance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10);
|
const availableBalance: string = new BigNumber(account.balance).minus(pendingAmount).toString(10);
|
||||||
|
|
||||||
balance = `${availableBalance} ${selectedCoin.symbol}`;
|
balance = `${availableBalance} ${network.symbol}`;
|
||||||
if (fiatRate) {
|
if (fiatRate) {
|
||||||
const accountBalance = new BigNumber(availableBalance);
|
const accountBalance = new BigNumber(availableBalance);
|
||||||
const fiat = accountBalance.times(fiatRate.value).toFixed(2);
|
const fiat = accountBalance.times(fiatRate.value).toFixed(2);
|
||||||
balance = `${availableBalance} ${selectedCoin.symbol} / $${fiat}`;
|
balance = `${availableBalance} ${network.symbol} / $${fiat}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,21 +233,19 @@ const AccountMenu = (props: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{selectedCoin && (
|
<NavLink to={baseUrl}>
|
||||||
<NavLink to={baseUrl}>
|
<RowCoin
|
||||||
<RowCoin
|
network={{
|
||||||
coin={{
|
name: network.name,
|
||||||
name: selectedCoin.name,
|
shortcut: network.shortcut,
|
||||||
network: selectedCoin.network,
|
}}
|
||||||
}}
|
iconLeft={{
|
||||||
iconLeft={{
|
type: ICONS.ARROW_LEFT,
|
||||||
type: ICONS.ARROW_LEFT,
|
color: colors.TEXT_PRIMARY,
|
||||||
color: colors.TEXT_PRIMARY,
|
size: 20,
|
||||||
size: 20,
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</NavLink>
|
||||||
</NavLink>
|
|
||||||
)}
|
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{selectedAccounts}
|
{selectedAccounts}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
@ -37,9 +37,9 @@ class CoinMenu extends PureComponent<Props> {
|
|||||||
return coins.map((coin) => {
|
return coins.map((coin) => {
|
||||||
const row = (
|
const row = (
|
||||||
<RowCoin
|
<RowCoin
|
||||||
coin={{
|
network={{
|
||||||
name: coin.coinName,
|
name: coin.coinName,
|
||||||
id: coin.id,
|
shortcut: coin.id,
|
||||||
}}
|
}}
|
||||||
iconRight={{
|
iconRight={{
|
||||||
type: ICONS.SKIP,
|
type: ICONS.SKIP,
|
||||||
@ -58,15 +58,15 @@ class CoinMenu extends PureComponent<Props> {
|
|||||||
const { config } = this.props.localStorage;
|
const { config } = this.props.localStorage;
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{config.coins.map(item => (
|
{config.networks.map(item => (
|
||||||
<NavLink
|
<NavLink
|
||||||
key={item.network}
|
key={item.shortcut}
|
||||||
to={`${this.getBaseUrl()}/network/${item.network}/account/0`}
|
to={`${this.getBaseUrl()}/network/${item.shortcut}/account/0`}
|
||||||
>
|
>
|
||||||
<RowCoin
|
<RowCoin
|
||||||
coin={{
|
network={{
|
||||||
name: item.name,
|
name: item.name,
|
||||||
network: item.network,
|
shortcut: item.shortcut,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
@ -38,7 +38,7 @@ const IconWrapper = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const RowCoin = ({
|
const RowCoin = ({
|
||||||
coin, iconLeft, iconRight,
|
network, iconLeft, iconRight,
|
||||||
}) => (
|
}) => (
|
||||||
<RowCoinWrapper>
|
<RowCoinWrapper>
|
||||||
<Row>
|
<Row>
|
||||||
@ -53,11 +53,8 @@ const RowCoin = ({
|
|||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
)}
|
)}
|
||||||
<CoinNameWrapper>
|
<CoinNameWrapper>
|
||||||
<CoinLogo
|
<CoinLogo network={network.shortcut} />
|
||||||
coinNetwork={coin.network}
|
<p>{network.name}</p>
|
||||||
coinId={coin.id}
|
|
||||||
/>
|
|
||||||
<p>{coin.name}</p>
|
|
||||||
</CoinNameWrapper>
|
</CoinNameWrapper>
|
||||||
</Left>
|
</Left>
|
||||||
{iconRight && (
|
{iconRight && (
|
||||||
@ -77,10 +74,9 @@ const iconShape = {
|
|||||||
size: PropTypes.number.isRequired,
|
size: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
RowCoin.propTypes = {
|
RowCoin.propTypes = {
|
||||||
coin: PropTypes.shape({
|
network: PropTypes.shape({
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
network: PropTypes.string,
|
shortcut: PropTypes.string,
|
||||||
id: PropTypes.string,
|
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
iconLeft: PropTypes.shape(iconShape),
|
iconLeft: PropTypes.shape(iconShape),
|
||||||
iconRight: PropTypes.shape(iconShape),
|
iconRight: PropTypes.shape(iconShape),
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import colors from 'config/colors';
|
import colors from 'config/colors';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
pathname: string;
|
||||||
|
wrapper: ?HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
style: {
|
style: {
|
||||||
width: number;
|
width: number;
|
||||||
left: number;
|
left: number;
|
||||||
}
|
},
|
||||||
|
shouldAnimate: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
@ -22,7 +24,9 @@ const Wrapper = styled.div`
|
|||||||
width: 100px;
|
width: 100px;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: ${colors.GREEN_PRIMARY};
|
background: ${colors.GREEN_PRIMARY};
|
||||||
transition: all 0.3s ease-in-out;
|
${props => props.animation && css`
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class Indicator extends PureComponent<Props, State> {
|
class Indicator extends PureComponent<Props, State> {
|
||||||
@ -34,52 +38,62 @@ class Indicator extends PureComponent<Props, State> {
|
|||||||
width: 0,
|
width: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
},
|
},
|
||||||
|
shouldAnimate: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.reposition = this.reposition.bind(this);
|
this.handleResize = this.handleResize.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.reposition();
|
window.addEventListener('resize', this.handleResize, false);
|
||||||
window.addEventListener('resize', this.reposition, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentWillReceiveProps(newProps: Props) {
|
||||||
this.reposition();
|
if (this.props.pathname !== newProps.pathname) {
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
window.removeEventListener('resize', this.reposition, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
reposition() {
|
|
||||||
const tabs = document.querySelector('.account-tabs');
|
|
||||||
if (!tabs) return;
|
|
||||||
const active = tabs.querySelector('.active');
|
|
||||||
if (!active) return;
|
|
||||||
const bounds = active.getBoundingClientRect();
|
|
||||||
|
|
||||||
const left = bounds.left - tabs.getBoundingClientRect().left;
|
|
||||||
|
|
||||||
if (this.state.style.left !== left) {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
style: {
|
shouldAnimate: true,
|
||||||
width: bounds.width,
|
|
||||||
left,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reposition: () => void;
|
componentDidUpdate() {
|
||||||
|
this.reposition(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('resize', this.handleResize, false);
|
||||||
|
}
|
||||||
|
|
||||||
handleResize() {
|
handleResize() {
|
||||||
this.reposition();
|
this.reposition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleResize: () => void;
|
||||||
|
|
||||||
|
reposition(resetAnimation: boolean = true) {
|
||||||
|
if (!this.props.wrapper) return;
|
||||||
|
const { wrapper } = this.props;
|
||||||
|
const active = wrapper.querySelector('.active');
|
||||||
|
if (!active) return;
|
||||||
|
const bounds = active.getBoundingClientRect();
|
||||||
|
const left = bounds.left - wrapper.getBoundingClientRect().left;
|
||||||
|
const { state } = this;
|
||||||
|
|
||||||
|
if (state.style.left !== left) {
|
||||||
|
this.setState({
|
||||||
|
style: {
|
||||||
|
width: bounds.width,
|
||||||
|
left,
|
||||||
|
},
|
||||||
|
shouldAnimate: resetAnimation ? false : state.shouldAnimate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (!this.props.wrapper) return null;
|
||||||
return (
|
return (
|
||||||
<Wrapper style={this.state.style} />
|
<Wrapper style={this.state.style} animation={this.state.shouldAnimate} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,22 +47,28 @@ const StyledNavLink = styled(NavLink)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
class TopNavigationAccount extends React.PureComponent<Props> {
|
||||||
|
wrapperRefCallback = (element: ?HTMLElement) => {
|
||||||
|
this.wrapper = element;
|
||||||
|
}
|
||||||
|
|
||||||
const TopNavigationAccount = (props: Props) => {
|
wrapper: ?HTMLElement;
|
||||||
const { state, pathname } = props.location;
|
|
||||||
if (!state) return null;
|
|
||||||
|
|
||||||
const basePath = `/device/${state.device}/network/${state.network}/account/${state.account}`;
|
render() {
|
||||||
|
const { state, pathname } = this.props.location;
|
||||||
|
if (!state) return null;
|
||||||
|
|
||||||
return (
|
const basePath = `/device/${state.device}/network/${state.network}/account/${state.account}`;
|
||||||
<Wrapper className="account-tabs">
|
return (
|
||||||
<StyledNavLink exact to={`${basePath}`}>Summary</StyledNavLink>
|
<Wrapper className="account-tabs" innerRef={this.wrapperRefCallback}>
|
||||||
<StyledNavLink to={`${basePath}/receive`}>Receive</StyledNavLink>
|
<StyledNavLink exact to={`${basePath}`}>Summary</StyledNavLink>
|
||||||
<StyledNavLink to={`${basePath}/send`}>Send</StyledNavLink>
|
<StyledNavLink to={`${basePath}/receive`}>Receive</StyledNavLink>
|
||||||
<StyledNavLink to={`${basePath}/signverify`}>Sign & Verify</StyledNavLink>
|
<StyledNavLink to={`${basePath}/send`}>Send</StyledNavLink>
|
||||||
<Indicator pathname={pathname} />
|
<StyledNavLink to={`${basePath}/signverify`}>Sign & Verify</StyledNavLink>
|
||||||
</Wrapper>
|
<Indicator pathname={pathname} wrapper={this.wrapper} />
|
||||||
);
|
</Wrapper>
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default TopNavigationAccount;
|
export default TopNavigationAccount;
|
@ -9,12 +9,12 @@ const VerifyAddressTooltip = ({ isConnected, isAvailable, addressUnverified }) =
|
|||||||
<Wrapper>
|
<Wrapper>
|
||||||
{addressUnverified && (
|
{addressUnverified && (
|
||||||
<Content>
|
<Content>
|
||||||
Unverified address. {isConnected && isAvailable ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.'}
|
Unverified address. {isConnected && isAvailable ? 'Show on Trezor' : 'Connect your Trezor to verify it.'}
|
||||||
</Content>
|
</Content>
|
||||||
)}
|
)}
|
||||||
{!addressUnverified && (
|
{!addressUnverified && (
|
||||||
<Content>
|
<Content>
|
||||||
{isConnected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.'}
|
{isConnected ? 'Show on Trezor' : 'Connect your Trezor to verify address.'}
|
||||||
</Content>
|
</Content>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { QRCode } from 'react-qr-svg';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import Button from 'components/Button';
|
import Button from 'components/Button';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import ICONS from 'config/icons';
|
|
||||||
import Content from 'views/Wallet/components/Content';
|
|
||||||
import colors from 'config/colors';
|
|
||||||
|
|
||||||
import Tooltip from 'components/Tooltip';
|
import Tooltip from 'components/Tooltip';
|
||||||
import { QRCode } from 'react-qr-svg';
|
|
||||||
|
|
||||||
|
import ICONS from 'config/icons';
|
||||||
|
import colors from 'config/colors';
|
||||||
import { FONT_SIZE, FONT_WEIGHT, FONT_FAMILY } from 'config/variables';
|
import { FONT_SIZE, FONT_WEIGHT, FONT_FAMILY } from 'config/variables';
|
||||||
|
import { CONTEXT_DEVICE } from 'actions/constants/modal';
|
||||||
|
|
||||||
|
import Content from 'views/Wallet/components/Content';
|
||||||
import Title from 'views/Wallet/components/Title';
|
import Title from 'views/Wallet/components/Title';
|
||||||
import VerifyAddressTooltip from './components/VerifyAddressTooltip';
|
import VerifyAddressTooltip from './components/VerifyAddressTooltip';
|
||||||
|
|
||||||
@ -147,7 +148,7 @@ const AccountReceive = (props: Props) => {
|
|||||||
addressUnverified,
|
addressUnverified,
|
||||||
} = props.receive;
|
} = props.receive;
|
||||||
|
|
||||||
const isAddressVerifying = props.modal.context === 'device' && props.modal.windowType === 'ButtonRequest_Address';
|
const isAddressVerifying = props.modal.context === CONTEXT_DEVICE && props.modal.windowType === 'ButtonRequest_Address';
|
||||||
const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified;
|
const isAddressHidden = !isAddressVerifying && !addressVerified && !addressUnverified;
|
||||||
|
|
||||||
let address = `${account.address.substring(0, 20)}...`;
|
let address = `${account.address.substring(0, 20)}...`;
|
||||||
@ -163,7 +164,7 @@ const AccountReceive = (props: Props) => {
|
|||||||
isShowingQrCode={addressVerified || addressUnverified}
|
isShowingQrCode={addressVerified || addressUnverified}
|
||||||
>
|
>
|
||||||
{isAddressVerifying && (
|
{isAddressVerifying && (
|
||||||
<AddressInfoText>Confirm address on TREZOR</AddressInfoText>
|
<AddressInfoText>Confirm address on Trezor</AddressInfoText>
|
||||||
)}
|
)}
|
||||||
{((addressVerified || addressUnverified) && !isAddressVerifying) && (
|
{((addressVerified || addressUnverified) && !isAddressVerifying) && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
@ -7,14 +7,14 @@ import { H2 } from 'components/Heading';
|
|||||||
import Link from 'components/Link';
|
import Link from 'components/Link';
|
||||||
import ScaleText from 'react-scale-text';
|
import ScaleText from 'react-scale-text';
|
||||||
|
|
||||||
import type { Coin } from 'reducers/LocalStorageReducer';
|
import type { Network } from 'reducers/LocalStorageReducer';
|
||||||
import type { Token } from 'reducers/TokensReducer';
|
import type { Token } from 'reducers/TokensReducer';
|
||||||
import type { Props as BaseProps } from '../../Container';
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
pending: $PropertyType<$ElementType<BaseProps, 'selectedAccount'>, 'pending'>,
|
pending: $PropertyType<$ElementType<BaseProps, 'selectedAccount'>, 'pending'>,
|
||||||
tokens: $PropertyType<$ElementType<BaseProps, 'selectedAccount'>, 'tokens'>,
|
tokens: $PropertyType<$ElementType<BaseProps, 'selectedAccount'>, 'tokens'>,
|
||||||
network: Coin
|
network: Network
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
|
@ -243,6 +243,7 @@ const AccountSend = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tokensSelectData = getTokensSelectData(tokens, network);
|
const tokensSelectData = getTokensSelectData(tokens, network);
|
||||||
|
const tokensSelectValue = tokensSelectData.find(t => t.value === currency);
|
||||||
const isAdvancedSettingsHidden = !advanced;
|
const isAdvancedSettingsHidden = !advanced;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -308,7 +309,7 @@ const AccountSend = (props: Props) => {
|
|||||||
key="currency"
|
key="currency"
|
||||||
isSearchable={false}
|
isSearchable={false}
|
||||||
isClearable={false}
|
isClearable={false}
|
||||||
defaultValue={tokensSelectData[0]}
|
value={tokensSelectValue}
|
||||||
isDisabled={tokensSelectData.length < 2}
|
isDisabled={tokensSelectData.length < 2}
|
||||||
onChange={onCurrencyChange}
|
onChange={onCurrencyChange}
|
||||||
options={tokensSelectData}
|
options={tokensSelectData}
|
||||||
@ -340,9 +341,9 @@ const AccountSend = (props: Props) => {
|
|||||||
options={feeLevels}
|
options={feeLevels}
|
||||||
formatOptionLabel={option => (
|
formatOptionLabel={option => (
|
||||||
<FeeOptionWrapper>
|
<FeeOptionWrapper>
|
||||||
<P>{option.value}</P>
|
<P>{option.value}</P>
|
||||||
<P>{option.label}</P>
|
<P>{option.label}</P>
|
||||||
</FeeOptionWrapper>
|
</FeeOptionWrapper>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</InputRow>
|
</InputRow>
|
||||||
|
@ -7,11 +7,11 @@ import colors from 'config/colors';
|
|||||||
import ICONS from 'config/icons';
|
import ICONS from 'config/icons';
|
||||||
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
|
||||||
|
|
||||||
import type { Coin } from 'reducers/LocalStorageReducer';
|
import type { Network } from 'flowtype';
|
||||||
import type { Props as BaseProps } from '../../Container';
|
import type { Props as BaseProps } from '../../Container';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
coin: Coin,
|
network: Network,
|
||||||
balance: string,
|
balance: string,
|
||||||
fiat: $ElementType<BaseProps, 'fiat'>,
|
fiat: $ElementType<BaseProps, 'fiat'>,
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ const BalanceRateWrapper = styled(BalanceWrapper)`
|
|||||||
padding-left: 50px;
|
padding-left: 50px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const CoinBalace = styled.div`
|
const CoinBalance = styled.div`
|
||||||
font-size: ${FONT_SIZE.SMALLER};
|
font-size: ${FONT_SIZE.SMALLER};
|
||||||
color: ${colors.TEXT_SECONDARY};
|
color: ${colors.TEXT_SECONDARY};
|
||||||
`;
|
`;
|
||||||
@ -97,7 +97,7 @@ class AccountBalance extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHideBallanceIconClick() {
|
handleHideBalanceIconClick() {
|
||||||
this.setState(previousState => ({
|
this.setState(previousState => ({
|
||||||
isHidden: !previousState.isHidden,
|
isHidden: !previousState.isHidden,
|
||||||
canAnimateHideBalanceIcon: true,
|
canAnimateHideBalanceIcon: true,
|
||||||
@ -105,8 +105,8 @@ class AccountBalance extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const selectedCoin = this.props.coin;
|
const { network } = this.props;
|
||||||
const fiatRate: any = this.props.fiat.find(f => f.network === selectedCoin.network);
|
const fiatRate: any = this.props.fiat.find(f => f.network === network.shortcut);
|
||||||
|
|
||||||
let accountBalance = '';
|
let accountBalance = '';
|
||||||
let fiatRateValue = '';
|
let fiatRateValue = '';
|
||||||
@ -121,7 +121,7 @@ class AccountBalance extends PureComponent<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<HideBalanceIconWrapper
|
<HideBalanceIconWrapper
|
||||||
onClick={() => this.handleHideBallanceIconClick()}
|
onClick={() => this.handleHideBalanceIconClick()}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
canAnimate={this.state.canAnimateHideBalanceIcon}
|
canAnimate={this.state.canAnimateHideBalanceIcon}
|
||||||
@ -138,13 +138,13 @@ class AccountBalance extends PureComponent<Props, State> {
|
|||||||
{fiatRate && (
|
{fiatRate && (
|
||||||
<FiatValue>${fiat}</FiatValue>
|
<FiatValue>${fiat}</FiatValue>
|
||||||
)}
|
)}
|
||||||
<CoinBalace>{this.props.balance} {selectedCoin.symbol}</CoinBalace>
|
<CoinBalance>{this.props.balance} {network.symbol}</CoinBalance>
|
||||||
</BalanceWrapper>
|
</BalanceWrapper>
|
||||||
{fiatRate && (
|
{fiatRate && (
|
||||||
<BalanceRateWrapper>
|
<BalanceRateWrapper>
|
||||||
<Label>Rate</Label>
|
<Label>Rate</Label>
|
||||||
<FiatValueRate>${fiatRateValue}</FiatValueRate>
|
<FiatValueRate>${fiatRateValue}</FiatValueRate>
|
||||||
<CoinBalace>1.00 {selectedCoin.symbol}</CoinBalace>
|
<CoinBalance>1.00 {network.symbol}</CoinBalance>
|
||||||
</BalanceRateWrapper>
|
</BalanceRateWrapper>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
@ -82,18 +82,15 @@ const AccountSummary = (props: Props) => {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<AccountHeading>
|
<AccountHeading>
|
||||||
<AccountName>
|
<AccountName>
|
||||||
<StyledCoinLogo coinNetwork={account.network} />
|
<StyledCoinLogo network={account.network} />
|
||||||
<H2>Account #{parseInt(account.index, 10) + 1}</H2>
|
<H2>Account #{parseInt(account.index, 10) + 1}</H2>
|
||||||
</AccountName>
|
</AccountName>
|
||||||
<Link href={explorerLink} isGray>See full transaction history</Link>
|
<Link href={explorerLink} isGray>See full transaction history</Link>
|
||||||
</AccountHeading>
|
</AccountHeading>
|
||||||
<AccountBalance
|
<AccountBalance
|
||||||
coin={network}
|
network={network}
|
||||||
summary={props.summary}
|
|
||||||
balance={balance}
|
balance={balance}
|
||||||
network={network.network}
|
|
||||||
fiat={props.fiat}
|
fiat={props.fiat}
|
||||||
localStorage={props.localStorage}
|
|
||||||
/>
|
/>
|
||||||
<H2Wrapper>
|
<H2Wrapper>
|
||||||
<H2>Tokens</H2>
|
<H2>Tokens</H2>
|
||||||
@ -109,11 +106,6 @@ const AccountSummary = (props: Props) => {
|
|||||||
/>
|
/>
|
||||||
</StyledTooltip>
|
</StyledTooltip>
|
||||||
</H2Wrapper>
|
</H2Wrapper>
|
||||||
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 Lahod */}
|
|
||||||
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 T01 */}
|
|
||||||
|
|
||||||
{/* TOOO: AsyncSelect is lagging when dropdown menu must show more than 200 items */}
|
|
||||||
{/* TODO: Input's box-shadow */}
|
|
||||||
<AsyncSelectWrapper>
|
<AsyncSelectWrapper>
|
||||||
<AsyncSelect
|
<AsyncSelect
|
||||||
isSearchable
|
isSearchable
|
||||||
|
Loading…
Reference in New Issue
Block a user