Fixed some eslint problems

pull/1/head
Vladimír Volek 6 years ago
parent e2c014f595
commit 7627aade89

@ -0,0 +1 @@
solidity

@ -19,7 +19,7 @@
"build": "rm -rf build && webpack --config ./webpack/config.prod.babel.js --progress", "build": "rm -rf build && webpack --config ./webpack/config.prod.babel.js --progress",
"flow": "flow check src/js", "flow": "flow check src/js",
"test": "", "test": "",
"lint": "npx eslint ./" "lint": "npx eslint ./ --fix"
}, },
"dependencies": { "dependencies": {
"color-hash": "^1.0.3", "color-hash": "^1.0.3",

@ -12,10 +12,10 @@ const replacePrefix = (path, opts = [], sourceFile) => {
} }
} }
return path; return path;
} };
export default ({ 'types': t }) => { export default ({ types: t }) => {
const visitor = { const visitor = {
CallExpression(path, state) { CallExpression(path, state) {
if (path.node.callee.name !== 'require') { if (path.node.callee.name !== 'require') {
@ -45,14 +45,14 @@ export default ({ 'types': t }) => {
if (path.node.source) { if (path.node.source) {
path.node.source.value = replacePrefix(path.node.source.value, state.opts, state.file.opts.filename); path.node.source.value = replacePrefix(path.node.source.value, state.opts, state.file.opts.filename);
} }
} },
}; };
return { return {
'visitor': { visitor: {
Program(path, state) { Program(path, state) {
path.traverse(visitor, state); path.traverse(visitor, state);
} },
} },
};
}; };
}

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
declare module CSSModule { declare module CSSModule {
declare var exports: { [key: string]: string }; declare var exports: { [key: string]: string };

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import type { import type {
Store as ReduxStore, Store as ReduxStore,
@ -9,7 +9,7 @@ import type {
ThunkAction as ReduxThunkAction, ThunkAction as ReduxThunkAction,
AsyncAction as ReduxAsyncAction, AsyncAction as ReduxAsyncAction,
ThunkDispatch as ReduxThunkDispatch, ThunkDispatch as ReduxThunkDispatch,
PlainDispatch as ReduxPlainDispatch PlainDispatch as ReduxPlainDispatch,
} from 'redux'; } from 'redux';
import type { Reducers, ReducersState } from '~/js/reducers'; import type { Reducers, ReducersState } from '~/js/reducers';

@ -51,9 +51,7 @@ declare module 'ethereum-types' {
value?: number value?: number
} }
declare export type EthereumSendTransactionT = EthereumSendTransactionOptionsT => Promise< declare export type EthereumSendTransactionT = EthereumSendTransactionOptionsT => Promise<EthereumTransactionHashT>
EthereumTransactionHashT
>
// TODO(mattgstevens): it would be nice to have an Generic type for a Contract instance // TODO(mattgstevens): it would be nice to have an Generic type for a Contract instance
// similar to the EthererumWatchEventT // similar to the EthererumWatchEventT
@ -69,4 +67,3 @@ declare module 'ethereum-types' {
// end contract data // end contract data
} }

@ -1,9 +1,9 @@
// flow-typed signature: 59b0c4be0e1408f21e2446be96c79804 // flow-typed signature: 59b0c4be0e1408f21e2446be96c79804
// flow-typed version: 9092387fd2/react-redux_v5.x.x/flow_>=v0.54.x // flow-typed version: 9092387fd2/react-redux_v5.x.x/flow_>=v0.54.x
import type { Dispatch, Store } from "redux"; import type { Dispatch, Store } from 'redux';
declare module "react-redux" { declare module 'react-redux' {
/* /*
S = State S = State
@ -32,9 +32,7 @@ declare module "react-redux" {
declare type Context = { store: Store<*, *> }; declare type Context = { store: Store<*, *> };
declare type ComponentWithDefaultProps<DP: {}, P: {}, CP: P> = Class< declare type ComponentWithDefaultProps<DP: {}, P: {}, CP: P> = Class<React$Component<CP>> & { defaultProps: DP };
React$Component<CP>
> & { defaultProps: DP };
declare class ConnectedComponentWithDefaultProps< declare class ConnectedComponentWithDefaultProps<
OP, OP,
@ -55,13 +53,9 @@ declare module "react-redux" {
state: void state: void
} }
declare type ConnectedComponentWithDefaultPropsClass<OP, DP, CP> = Class< declare type ConnectedComponentWithDefaultPropsClass<OP, DP, CP> = Class<ConnectedComponentWithDefaultProps<OP, DP, CP>>;
ConnectedComponentWithDefaultProps<OP, DP, CP>
>;
declare type ConnectedComponentClass<OP, P> = Class< declare type ConnectedComponentClass<OP, P> = Class<ConnectedComponent<OP, P>>;
ConnectedComponent<OP, P>
>;
declare type Connector<OP, P> = (<DP: {}, CP: {}>( declare type Connector<OP, P> = (<DP: {}, CP: {}>(
component: ComponentWithDefaultProps<DP, P, CP> component: ComponentWithDefaultProps<DP, P, CP>

@ -1,4 +1,4 @@
declare module "react-router-dom" { declare module 'react-router-dom' {
declare export class BrowserRouter extends React$Component<{ declare export class BrowserRouter extends React$Component<{
basename?: string, basename?: string,
forceRefresh?: boolean, forceRefresh?: boolean,

@ -1,10 +1,9 @@
import type { import type {
RouterHistory, RouterHistory,
Location as RouterLocation Location as RouterLocation,
} from 'react-router'; } from 'react-router';
declare module "react-router-redux" { declare module 'react-router-redux' {
// custom state for location // custom state for location
declare export type LocationState = {[key: string]: string}; declare export type LocationState = {[key: string]: string};

@ -1,4 +1,4 @@
declare module "react-router" { declare module 'react-router' {
// NOTE: many of these are re-exported by react-router-dom and // NOTE: many of these are re-exported by react-router-dom and
// react-router-native, so when making changes, please be sure to update those // react-router-native, so when making changes, please be sure to update those
// as well. // as well.

@ -1,5 +1,4 @@
declare module 'redux' { declare module 'redux' {
/* /*
S = State S = State

@ -124,12 +124,10 @@ declare module 'web3' {
} }
// //
// //
/*declare module 'web3' { /*declare module 'web3' {
module.exports = { module.exports = {

@ -1,5 +1,4 @@
/* @flow */ /* @flow */
'use strict';
import * as ACCOUNT from './constants/account'; import * as ACCOUNT from './constants/account';
import type { Action, TrezorDevice } from '~/flowtype'; import type { Action, TrezorDevice } from '~/flowtype';
@ -41,22 +40,18 @@ export type AccountSetNonceAction = {
nonce: number nonce: number
} }
export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => { export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => ({
return {
type: ACCOUNT.SET_BALANCE, type: ACCOUNT.SET_BALANCE,
address, address,
network, network,
deviceState, deviceState,
balance balance,
} });
}
export const setNonce = (address: string, network: string, deviceState: string, nonce: number): Action => { export const setNonce = (address: string, network: string, deviceState: string, nonce: number): Action => ({
return {
type: ACCOUNT.SET_NONCE, type: ACCOUNT.SET_NONCE,
address, address,
network, network,
deviceState, deviceState,
nonce nonce,
} });
}

@ -1,19 +1,21 @@
/* @flow */ /* @flow */
'use strict';
import TrezorConnect from 'trezor-connect'; import TrezorConnect from 'trezor-connect';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import * as DISCOVERY from './constants/discovery'; import * as DISCOVERY from './constants/discovery';
import * as ACCOUNT from './constants/account'; import * as ACCOUNT from './constants/account';
import * as TOKEN from './constants/token'; import * as TOKEN from './constants/token';
import * as NOTIFICATION from './constants/notification'; import * as NOTIFICATION from './constants/notification';
import * as AccountsActions from '../actions/AccountsActions'; import * as AccountsActions from './AccountsActions';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import { getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions'; import { getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions';
import { setBalance as setTokenBalance } from './TokenActions'; import { setBalance as setTokenBalance } from './TokenActions';
import type { ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice } from '~/flowtype'; import type {
ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice,
} from '~/flowtype';
import type { Discovery, State } from '../reducers/DiscoveryReducer'; import type { Discovery, State } from '../reducers/DiscoveryReducer';
export type DiscoveryAction = { export type DiscoveryAction = {
@ -50,64 +52,59 @@ export type DiscoveryCompleteAction = {
network: string network: string
} }
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => { export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
if (!selected) { if (!selected) {
// TODO: throw error // TODO: throw error
console.error("Start discovery: no selected device", device) console.error('Start discovery: no selected device', device);
return; return;
} else if (selected.path !== device.path) { } if (selected.path !== device.path) {
console.error("Start discovery: requested device is not selected", device, selected) console.error('Start discovery: requested device is not selected', device, selected);
return; return;
} else if (!selected.state) { } if (!selected.state) {
console.warn("Start discovery: Selected device wasn't authenticated yet...") console.warn("Start discovery: Selected device wasn't authenticated yet...");
return; return;
} else if (selected.connected && !selected.available) { } if (selected.connected && !selected.available) {
console.warn("Start discovery: Selected device is unavailable...") console.warn('Start discovery: Selected device is unavailable...');
return; return;
} }
const web3 = getState().web3.find(w3 => w3.network === network); const web3 = getState().web3.find(w3 => w3.network === network);
if (!web3) { if (!web3) {
console.error("Start discovery: Web3 does not exist", network) console.error('Start discovery: Web3 does not exist', network);
return; return;
} }
if (!web3.web3.currentProvider.isConnected()) { if (!web3.web3.currentProvider.isConnected()) {
console.error("Start discovery: Web3 is not connected", network) console.error('Start discovery: Web3 is not connected', network);
dispatch({ dispatch({
type: DISCOVERY.WAITING_FOR_BACKEND, type: DISCOVERY.WAITING_FOR_BACKEND,
device, device,
network network,
}); });
return; return;
} }
const discovery: State = getState().discovery; const discovery: State = getState().discovery;
let discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network); const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) { if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
dispatch({ dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE, type: DISCOVERY.WAITING_FOR_DEVICE,
device, device,
network network,
}); });
return; return;
} }
if (!discoveryProcess) { if (!discoveryProcess) {
dispatch(begin(device, network)); dispatch(begin(device, network));
return; } else if (discoveryProcess.completed && !ignoreCompleted) {
} else {
if (discoveryProcess.completed && !ignoreCompleted) {
dispatch({ dispatch({
type: DISCOVERY.COMPLETE, type: DISCOVERY.COMPLETE,
device, device,
network network,
}); });
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) { } else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
// discovery cycle was interrupted // discovery cycle was interrupted
@ -116,13 +113,9 @@ export const start = (device: TrezorDevice, network: string, ignoreCompleted?: b
} else { } else {
dispatch(discoverAccount(device, discoveryProcess)); dispatch(discoverAccount(device, discoveryProcess));
} }
} };
}
}
const begin = (device: TrezorDevice, network: string): AsyncAction => {
return 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 coinToDiscover = config.coins.find(c => c.network === network);
if (!coinToDiscover) return; if (!coinToDiscover) return;
@ -130,7 +123,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
dispatch({ dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE, type: DISCOVERY.WAITING_FOR_DEVICE,
device, device,
network network,
}); });
// get xpub from TREZOR // get xpub from TREZOR
@ -138,7 +131,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
device: { device: {
path: device.path, path: device.path,
instance: device.instance, instance: device.instance,
state: device.state state: device.state,
}, },
path: coinToDiscover.bip44, path: coinToDiscover.bip44,
keepSession: true, // acquire and hold session keepSession: true, // acquire and hold session
@ -148,7 +141,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
// handle TREZOR response error // handle TREZOR response error
if (!response.success) { if (!response.success) {
// TODO: check message // TODO: check message
console.warn("DISCOVERY ERROR", response) console.warn('DISCOVERY ERROR', response);
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
payload: { payload: {
@ -160,17 +153,17 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
{ {
label: 'Try again', label: 'Try again',
callback: () => { callback: () => {
dispatch(start(device, network)) dispatch(start(device, network));
} },
} },
] ],
} },
}) });
return; return;
} }
// check for interruption // check for interruption
let discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network); const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
if (discoveryProcess && discoveryProcess.interrupted) return; if (discoveryProcess && discoveryProcess.interrupted) return;
const basePath: Array<number> = response.payload.path; const basePath: Array<number> = response.payload.path;
@ -186,12 +179,9 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
}); });
dispatch(start(device, network)); dispatch(start(device, network));
} };
}
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const completed: boolean = discoveryProcess.completed; const completed: boolean = discoveryProcess.completed;
discoveryProcess.completed = false; discoveryProcess.completed = false;
@ -202,7 +192,6 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
const network = discoveryProcess.network; const network = discoveryProcess.network;
// TODO: check if address was created before // TODO: check if address was created before
// verify address with TREZOR // verify address with TREZOR
@ -210,7 +199,7 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
device: { device: {
path: device.path, path: device.path,
instance: device.instance, instance: device.instance,
state: device.state state: device.state,
}, },
path, path,
showOnTrezor: false, showOnTrezor: false,
@ -234,7 +223,7 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address); const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address);
if (trezorAddress !== ethAddress) { if (trezorAddress !== ethAddress) {
// throw inconsistent state error // throw inconsistent state error
console.warn("Inconsistent state", trezorAddress, ethAddress); console.warn('Inconsistent state', trezorAddress, ethAddress);
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
@ -247,11 +236,11 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
{ {
label: 'Try again', label: 'Try again',
callback: () => { callback: () => {
dispatch(start(device, discoveryProcess.network)) dispatch(start(device, discoveryProcess.network));
} },
} },
] ],
} },
}); });
return; return;
} }
@ -268,11 +257,11 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
{ {
label: 'Try again', label: 'Try again',
callback: () => { callback: () => {
dispatch(start(device, discoveryProcess.network)) dispatch(start(device, discoveryProcess.network));
} },
} },
] ],
} },
}); });
return; return;
} }
@ -294,15 +283,14 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
network, network,
index: discoveryProcess.accountIndex, index: discoveryProcess.accountIndex,
path, path,
address: ethAddress address: ethAddress,
}); });
dispatch( dispatch(
AccountsActions.setBalance(ethAddress, network, device.state || 'undefined', web3instance.web3.fromWei(balance.toString(), 'ether')) AccountsActions.setBalance(ethAddress, network, device.state || 'undefined', web3instance.web3.fromWei(balance.toString(), 'ether')),
); );
dispatch(AccountsActions.setNonce(ethAddress, network, device.state || 'undefined', nonce)); dispatch(AccountsActions.setNonce(ethAddress, network, device.state || 'undefined', nonce));
if (!completed) if (!completed) { dispatch(discoverAccount(device, discoveryProcess)); }
dispatch( discoverAccount(device, discoveryProcess) );
} }
if (addressIsEmpty) { if (addressIsEmpty) {
@ -311,7 +299,7 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
device: { device: {
path: device.path, path: device.path,
instance: device.instance, instance: device.instance,
state: device.state state: device.state,
}, },
keepSession: false, keepSession: false,
useEmptyPassphrase: !device.instance, useEmptyPassphrase: !device.instance,
@ -321,14 +309,12 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
dispatch({ dispatch({
type: DISCOVERY.COMPLETE, type: DISCOVERY.COMPLETE,
device, device,
network network,
}); });
} }
} };
}
export const restore = (): ThunkAction => { export const restore = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
if (selected && selected.connected && selected.features) { if (selected && selected.connected && selected.features) {
@ -337,15 +323,13 @@ export const restore = (): ThunkAction => {
dispatch(start(selected, discoveryProcess.network)); dispatch(start(selected, discoveryProcess.network));
} }
} }
} };
}
// TODO: rename method to something intuitive // TODO: rename method to something intuitive
// there is no discovery process but it should be // there is no discovery process but it should be
// this is possible race condition when "network" was changed in url but device was not authenticated yet // this is possible race condition when "network" was changed in url but device was not authenticated yet
// try to start discovery after CONNECT.AUTH_DEVICE action // try to start discovery after CONNECT.AUTH_DEVICE action
export const check = (): ThunkAction => { export const check = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
@ -356,14 +340,9 @@ export const check = (): ThunkAction => {
dispatch(start(selected, urlParams.network)); dispatch(start(selected, urlParams.network));
} }
} }
} };
}
export const stop = (device: TrezorDevice): Action => { export const stop = (device: TrezorDevice): Action => ({
// TODO: release devices session
// corner case switch /eth to /etc (discovery start stop - should not be async)
return {
type: DISCOVERY.STOP, type: DISCOVERY.STOP,
device device,
} });
}

@ -1,2 +1 @@
/* @flow */ /* @flow */
'use strict';

@ -1,15 +1,17 @@
/* @flow */ /* @flow */
'use strict';
import * as CONNECT from './constants/TrezorConnect'; import * as CONNECT from './constants/TrezorConnect';
import * as ACCOUNT from './constants/account'; import * as ACCOUNT from './constants/account';
import * as TOKEN from './constants/token'; import * as TOKEN from './constants/token';
import * as DISCOVERY from './constants/discovery'; import * as DISCOVERY from './constants/discovery';
import * as STORAGE from './constants/localStorage'; import * as STORAGE from './constants/localStorage';
import * as PENDING from '../actions/constants/pendingTx'; import * as PENDING from './constants/pendingTx';
import { JSONRequest, httpRequest } from '../utils/networkUtils'; import { JSONRequest, httpRequest } from '../utils/networkUtils';
import type { ThunkAction, AsyncAction, GetState, Dispatch, TrezorDevice } from '~/flowtype'; import type {
ThunkAction, AsyncAction, GetState, Dispatch, TrezorDevice,
} from '~/flowtype';
import type { Config, Coin, TokensCollection } from '../reducers/LocalStorageReducer'; import type { Config, Coin, TokensCollection } from '../reducers/LocalStorageReducer';
import AppConfigJSON from '~/data/appConfig.json'; import AppConfigJSON from '~/data/appConfig.json';
@ -28,9 +30,7 @@ export type StorageAction = {
error: string, error: string,
}; };
export const loadData = (): ThunkAction => { export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
// check if local storage is available // check if local storage is available
// let available: boolean = true; // let available: boolean = true;
// if (typeof window.localStorage === 'undefined') { // if (typeof window.localStorage === 'undefined') {
@ -44,8 +44,7 @@ export const loadData = (): ThunkAction => {
// } // }
dispatch(loadTokensFromJSON()); dispatch(loadTokensFromJSON());
} };
}
// const parseConfig = (json: JSON): Config => { // const parseConfig = (json: JSON): Config => {
@ -84,16 +83,15 @@ export const loadData = (): ThunkAction => {
export function loadTokensFromJSON(): AsyncAction { export function loadTokensFromJSON(): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): 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');
const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json'); const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json');
window.addEventListener('storage', event => { window.addEventListener('storage', (event) => {
dispatch(update(event)); dispatch(update(event));
}) });
// load tokens // load tokens
const tokens = await config.coins.reduce(async (promise: Promise<TokensCollection>, coin: Coin): Promise<TokensCollection> => { const tokens = await config.coins.reduce(async (promise: Promise<TokensCollection>, coin: Coin): Promise<TokensCollection> => {
@ -107,40 +105,40 @@ export function loadTokensFromJSON(): AsyncAction {
if (devices) { if (devices) {
dispatch({ dispatch({
type: CONNECT.DEVICE_FROM_STORAGE, type: CONNECT.DEVICE_FROM_STORAGE,
payload: JSON.parse(devices) payload: JSON.parse(devices),
}) });
} }
const accounts: ?string = get('accounts'); const accounts: ?string = get('accounts');
if (accounts) { if (accounts) {
dispatch({ dispatch({
type: ACCOUNT.FROM_STORAGE, type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(accounts) payload: JSON.parse(accounts),
}) });
} }
const userTokens: ?string = get('tokens'); const userTokens: ?string = get('tokens');
if (userTokens) { if (userTokens) {
dispatch({ dispatch({
type: TOKEN.FROM_STORAGE, type: TOKEN.FROM_STORAGE,
payload: JSON.parse(userTokens) payload: JSON.parse(userTokens),
}) });
} }
const pending: ?string = get('pending'); const pending: ?string = get('pending');
if (pending) { if (pending) {
dispatch({ dispatch({
type: PENDING.FROM_STORAGE, type: PENDING.FROM_STORAGE,
payload: JSON.parse(pending) payload: JSON.parse(pending),
}) });
} }
const discovery: ?string = get('discovery'); const discovery: ?string = get('discovery');
if (discovery) { if (discovery) {
dispatch({ dispatch({
type: DISCOVERY.FROM_STORAGE, type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(discovery) payload: JSON.parse(discovery),
}) });
} }
@ -148,21 +146,19 @@ export function loadTokensFromJSON(): AsyncAction {
type: STORAGE.READY, type: STORAGE.READY,
config, config,
tokens, tokens,
ERC20Abi ERC20Abi,
}) });
} catch (error) { } catch (error) {
dispatch({ dispatch({
type: STORAGE.ERROR, type: STORAGE.ERROR,
error error,
}) });
}
} }
};
} }
export function update(event: StorageEvent): AsyncAction { export function update(event: StorageEvent): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
if (!event.newValue) return; if (!event.newValue) return;
if (event.key === 'devices') { if (event.key === 'devices') {
@ -183,46 +179,44 @@ export function update(event: StorageEvent): AsyncAction {
if (event.key === 'accounts') { if (event.key === 'accounts') {
dispatch({ dispatch({
type: ACCOUNT.FROM_STORAGE, type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(event.newValue) payload: JSON.parse(event.newValue),
}); });
} }
if (event.key === 'tokens') { if (event.key === 'tokens') {
dispatch({ dispatch({
type: TOKEN.FROM_STORAGE, type: TOKEN.FROM_STORAGE,
payload: JSON.parse(event.newValue) payload: JSON.parse(event.newValue),
}); });
} }
if (event.key === 'pending') { if (event.key === 'pending') {
dispatch({ dispatch({
type: PENDING.FROM_STORAGE, type: PENDING.FROM_STORAGE,
payload: JSON.parse(event.newValue) payload: JSON.parse(event.newValue),
}); });
} }
if (event.key === 'discovery') { if (event.key === 'discovery') {
dispatch({ dispatch({
type: DISCOVERY.FROM_STORAGE, type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(event.newValue) payload: JSON.parse(event.newValue),
}); });
} }
} };
} }
export const save = (key: string, value: string): ThunkAction => { export const save = (key: string, value: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
if (typeof window.localStorage !== 'undefined') { if (typeof window.localStorage !== 'undefined') {
try { try {
window.localStorage.setItem(key, value); window.localStorage.setItem(key, value);
} catch (error) { } catch (error) {
// available = false; // available = false;
console.error("Local Storage ERROR: " + error) console.error(`Local Storage ERROR: ${error}`);
}
}
} }
} }
};
export const get = (key: string): ?string => { export const get = (key: string): ?string => {
try { try {
@ -231,4 +225,4 @@ export const get = (key: string): ?string => {
// available = false; // available = false;
return null; return null;
} }
} };

@ -1,9 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import * as LOG from './constants/log'; import * as LOG from './constants/log';
import type { Action, ThunkAction, GetState, Dispatch } from '~/flowtype'; import type {
Action, ThunkAction, GetState, Dispatch,
} from '~/flowtype';
import type { LogEntry } from '../reducers/LogReducer'; import type { LogEntry } from '../reducers/LogReducer';
export type LogAction = { export type LogAction = {
@ -15,31 +17,26 @@ export type LogAction = {
payload: LogEntry payload: LogEntry
}; };
export const toggle = (): ThunkAction => { export const toggle = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
if (!getState().log.opened) { if (!getState().log.opened) {
window.scrollTo(0, 0); window.scrollTo(0, 0);
dispatch({ dispatch({
type: LOG.OPEN type: LOG.OPEN,
}); });
} else { } else {
dispatch({ dispatch({
type: LOG.CLOSE type: LOG.CLOSE,
}); });
} }
} };
}
// export const add = (type: string, message: string): Action => { // export const add = (type: string, message: string): Action => {
export const add = (type: string, message: any): Action => { export const add = (type: string, message: any): Action => ({
return {
type: LOG.ADD, type: LOG.ADD,
payload: { payload: {
time: new Date().getTime(), time: new Date().getTime(),
type, type,
message message,
} },
} });
}

@ -1,13 +1,15 @@
/* @flow */ /* @flow */
'use strict';
import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect'; import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect';
import type { Device } from 'trezor-connect';
import * as MODAL from './constants/modal'; import * as MODAL from './constants/modal';
import * as CONNECT from './constants/TrezorConnect'; import * as CONNECT from './constants/TrezorConnect';
import type { ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice } from '~/flowtype'; import type {
ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice,
} from '~/flowtype';
import type { State } from '../reducers/ModalReducer'; import type { State } from '../reducers/ModalReducer';
import type { Device } from 'trezor-connect';
export type ModalAction = { export type ModalAction = {
type: typeof MODAL.CLOSE type: typeof MODAL.CLOSE
@ -19,25 +21,23 @@ export type ModalAction = {
export const onPinSubmit = (value: string): Action => { export const onPinSubmit = (value: string): Action => {
TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value }); TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value });
return { return {
type: MODAL.CLOSE type: MODAL.CLOSE,
} };
} };
export const onPassphraseSubmit = (passphrase: string): AsyncAction => { export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const resp = await TrezorConnect.uiResponse({ const resp = await TrezorConnect.uiResponse({
type: UI.RECEIVE_PASSPHRASE, type: UI.RECEIVE_PASSPHRASE,
payload: { payload: {
value: passphrase, value: passphrase,
save: true save: true,
} },
}); });
dispatch({ dispatch({
type: MODAL.CLOSE type: MODAL.CLOSE,
}); });
} };
}
// export const askForRemember = (device: TrezorDevice): Action => { // export const askForRemember = (device: TrezorDevice): Action => {
// return { // return {
@ -46,47 +46,35 @@ export const onPassphraseSubmit = (passphrase: string): AsyncAction => {
// } // }
// } // }
export const onRememberDevice = (device: TrezorDevice): Action => { export const onRememberDevice = (device: TrezorDevice): Action => ({
return {
type: CONNECT.REMEMBER, type: CONNECT.REMEMBER,
device device,
} });
}
export const onForgetDevice = (device: TrezorDevice): Action => { export const onForgetDevice = (device: TrezorDevice): Action => ({
return {
type: CONNECT.FORGET, type: CONNECT.FORGET,
device, device,
} });
}
export const onForgetSingleDevice = (device: TrezorDevice): Action => { export const onForgetSingleDevice = (device: TrezorDevice): Action => ({
return {
type: CONNECT.FORGET_SINGLE, type: CONNECT.FORGET_SINGLE,
device, device,
} });
}
export const onCancel = (): Action => {
return {
type: MODAL.CLOSE
}
}
export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => { export const onCancel = (): Action => ({
return (dispatch: Dispatch, getState: GetState): void => { type: MODAL.CLOSE,
});
export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
dispatch(onCancel()); dispatch(onCancel());
dispatch({ dispatch({
type: CONNECT.DUPLICATE, type: CONNECT.DUPLICATE,
device device,
}); });
} };
}
export const onRememberRequest = (prevState: State): ThunkAction => { export const onRememberRequest = (prevState: State): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const state: State = getState().modal; const state: State = getState().modal;
// handle case where forget modal is already opened // handle case where forget modal is already opened
// TODO: 2 modals at once (two devices disconnected in the same time) // TODO: 2 modals at once (two devices disconnected in the same time)
@ -95,21 +83,19 @@ export const onRememberRequest = (prevState: State): ThunkAction => {
if (state.opened) { if (state.opened) {
dispatch({ dispatch({
type: CONNECT.FORGET, type: CONNECT.FORGET,
device: state.device device: state.device,
}); });
} }
// forget previous (old) // forget previous (old)
dispatch({ dispatch({
type: CONNECT.FORGET, type: CONNECT.FORGET,
device: prevState.device device: prevState.device,
}); });
} }
} };
}
export const onDeviceConnect = (device: Device): ThunkAction => { export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
// interrupt process of remembering device (force forget) // interrupt process of remembering device (force forget)
// TODO: the same for disconnect more than 1 device at once // TODO: the same for disconnect more than 1 device at once
const { modal } = getState(); const { modal } = getState();
@ -121,13 +107,11 @@ export const onDeviceConnect = (device: Device): ThunkAction => {
} else { } else {
dispatch({ dispatch({
type: CONNECT.FORGET, type: CONNECT.FORGET,
device: modal.device device: modal.device,
}); });
} }
} }
};
}
}
export default { export default {
onPinSubmit, onPinSubmit,
@ -137,5 +121,5 @@ export default {
onForgetDevice, onForgetDevice,
onForgetSingleDevice, onForgetSingleDevice,
onCancel, onCancel,
onDuplicateDevice onDuplicateDevice,
} };

@ -1,9 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import * as NOTIFICATION from './constants/notification'; import * as NOTIFICATION from './constants/notification';
import type { Action, AsyncAction, GetState, Dispatch, RouterLocationState } from '~/flowtype'; import type {
Action, AsyncAction, GetState, Dispatch, RouterLocationState,
} from '~/flowtype';
import type { CallbackAction } from '../reducers/NotificationReducer'; import type { CallbackAction } from '../reducers/NotificationReducer';
export type NotificationAction = { export type NotificationAction = {
@ -23,31 +25,27 @@ export type NotificationAction = {
} }
} }
export const close = (payload: any = {}): Action => { export const close = (payload: any = {}): Action => ({
return {
type: NOTIFICATION.CLOSE, type: NOTIFICATION.CLOSE,
payload payload,
} });
}
// called from RouterService // called from RouterService
export const clear = (currentParams: RouterLocationState, requestedParams: RouterLocationState): AsyncAction => { export const clear = (currentParams: RouterLocationState, requestedParams: RouterLocationState): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// if route has been changed from device view into something else (like other device, settings...) // if route has been changed from device view into something else (like other device, settings...)
// try to remove all Notifications which are linked to previous device (they are not cancelable by user) // try to remove all Notifications which are linked to previous device (they are not cancelable by user)
if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
const entries = getState().notifications.filter(entry => typeof entry.devicePath === 'string'); const entries = getState().notifications.filter(entry => typeof entry.devicePath === 'string');
entries.forEach(entry => { entries.forEach((entry) => {
if (typeof entry.devicePath === 'string') { if (typeof entry.devicePath === 'string') {
dispatch({ dispatch({
type: NOTIFICATION.CLOSE, type: NOTIFICATION.CLOSE,
payload: { payload: {
devicePath: entry.devicePath devicePath: entry.devicePath,
} },
})
}
}); });
} }
});
} }
} };

@ -1,8 +1,8 @@
/* @flow */ /* @flow */
'use strict';
import * as PENDING from './constants/pendingTx'; import * as PENDING from './constants/pendingTx';
import type { State, PendingTx } from '../reducers/PendingTxReducer' import type { State, PendingTx } from '../reducers/PendingTxReducer';
export type PendingTxAction = { export type PendingTxAction = {
type: typeof PENDING.FROM_STORAGE, type: typeof PENDING.FROM_STORAGE,

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import TrezorConnect from 'trezor-connect'; import TrezorConnect from 'trezor-connect';
import * as RECEIVE from './constants/receive'; import * as RECEIVE from './constants/receive';
@ -8,7 +8,9 @@ import * as NOTIFICATION from './constants/notification';
import { initialState } from '../reducers/ReceiveReducer'; import { initialState } from '../reducers/ReceiveReducer';
import type { State } from '../reducers/ReceiveReducer'; import type { State } from '../reducers/ReceiveReducer';
import type { TrezorDevice, ThunkAction, AsyncAction, Action, GetState, Dispatch } from '~/flowtype'; import type {
TrezorDevice, ThunkAction, AsyncAction, Action, GetState, Dispatch,
} from '~/flowtype';
export type ReceiveAction = { export type ReceiveAction = {
type: typeof RECEIVE.INIT, type: typeof RECEIVE.INIT,
@ -26,43 +28,34 @@ export type ReceiveAction = {
type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS
} }
export const init = (): ThunkAction => { export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const state: State = { const state: State = {
...initialState, ...initialState,
}; };
dispatch({ dispatch({
type: RECEIVE.INIT, type: RECEIVE.INIT,
state: state state,
}); });
} };
}
export const dispose = (): Action => { export const dispose = (): Action => ({
return { type: RECEIVE.DISPOSE,
type: RECEIVE.DISPOSE });
}
}
export const showUnverifiedAddress = (): Action => { export const showUnverifiedAddress = (): Action => ({
return { type: RECEIVE.SHOW_UNVERIFIED_ADDRESS,
type: RECEIVE.SHOW_UNVERIFIED_ADDRESS });
}
}
//export const showAddress = (address_n: string): AsyncAction => { //export const showAddress = (address_n: string): AsyncAction => {
export const showAddress = (address_n: Array<number>): AsyncAction => { export const showAddress = (address_n: Array<number>): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
if (selected && (!selected.connected || !selected.available)) { if (selected && (!selected.connected || !selected.available)) {
dispatch({ dispatch({
type: RECEIVE.REQUEST_UNVERIFIED, type: RECEIVE.REQUEST_UNVERIFIED,
device: selected device: selected,
}); });
return; return;
} }
@ -71,7 +64,7 @@ export const showAddress = (address_n: Array<number>): AsyncAction => {
device: { device: {
path: selected.path, path: selected.path,
instance: selected.instance, instance: selected.instance,
state: selected.state state: selected.state,
}, },
path: address_n, path: address_n,
useEmptyPassphrase: !selected.instance, useEmptyPassphrase: !selected.instance,
@ -79,12 +72,12 @@ export const showAddress = (address_n: Array<number>): AsyncAction => {
if (response && response.success) { if (response && response.success) {
dispatch({ dispatch({
type: RECEIVE.SHOW_ADDRESS type: RECEIVE.SHOW_ADDRESS,
}) });
} else { } else {
dispatch({ dispatch({
type: RECEIVE.HIDE_ADDRESS type: RECEIVE.HIDE_ADDRESS,
}) });
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
@ -97,19 +90,18 @@ export const showAddress = (address_n: Array<number>): AsyncAction => {
{ {
label: 'Try again', label: 'Try again',
callback: () => { callback: () => {
dispatch(showAddress(address_n)) dispatch(showAddress(address_n));
} },
} },
] ],
} },
}) });
}
}
} }
};
export default { export default {
init, init,
dispose, dispose,
showAddress, showAddress,
showUnverifiedAddress showUnverifiedAddress,
} };

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'react-router-redux';
import * as ACCOUNT from './constants/account'; import * as ACCOUNT from './constants/account';
@ -32,15 +32,13 @@ export type SelectedAccountAction = {
payload: $ElementType<State, 'selectedAccount'> payload: $ElementType<State, 'selectedAccount'>
}; };
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => { export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const locationChange: boolean = action.type === LOCATION_CHANGE; const locationChange: boolean = action.type === LOCATION_CHANGE;
const state: State = getState(); const state: State = getState();
const location = state.router.location; const location = state.router.location;
const prevLocation = prevState.router.location; const prevLocation = prevState.router.location;
let needUpdate: boolean = false; const needUpdate: boolean = false;
// reset form to default // reset form to default
if (action.type === SEND.TX_COMPLETE) { if (action.type === SEND.TX_COMPLETE) {
@ -56,8 +54,6 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|| prevState.tokens !== state.tokens || prevState.tokens !== state.tokens
|| prevState.pending !== state.pending || prevState.pending !== state.pending
|| prevState.web3 !== state.web3) { || prevState.web3 !== state.web3) {
if (locationChange) { if (locationChange) {
// dispose current account view // dispose current account view
dispatch(dispose()); dispatch(dispose());
@ -77,8 +73,8 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
discovery, discovery,
tokens, tokens,
pending, pending,
web3 web3,
} };
let needUpdate: boolean = false; let needUpdate: boolean = false;
Object.keys(payload).forEach((key) => { Object.keys(payload).forEach((key) => {
@ -86,12 +82,10 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) { if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) {
needUpdate = true; needUpdate = true;
} }
} else { } else if (payload[key] !== state.selectedAccount[key]) {
if (payload[key] !== state.selectedAccount[key]) {
needUpdate = true; needUpdate = true;
} }
} });
})
if (needUpdate) { if (needUpdate) {
dispatch({ dispatch({
@ -100,18 +94,18 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
}); });
// initialize SendFormReducer // initialize SendFormReducer
if (location.state.send && getState().sendForm.currency === "") { if (location.state.send && getState().sendForm.currency === '') {
dispatch(SendFormActions.init()); dispatch(SendFormActions.init());
} }
if (location.state.send) { if (location.state.send) {
const rejectedTxs = pending.filter(tx => tx.rejected); const rejectedTxs = pending.filter(tx => tx.rejected);
rejectedTxs.forEach(tx => { rejectedTxs.forEach((tx) => {
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
payload: { payload: {
type: "warning", type: 'warning',
title: "Pending transaction rejected", title: 'Pending transaction rejected',
message: `Transaction with id: ${tx.id} not found.`, message: `Transaction with id: ${tx.id} not found.`,
cancelable: true, cancelable: true,
actions: [ actions: [
@ -122,20 +116,17 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
type: PENDING.TX_RESOLVED, type: PENDING.TX_RESOLVED,
tx, tx,
}); });
} },
} },
] ],
} },
}) });
}); });
} }
} }
} }
} };
}
export const dispose = (): Action => { export const dispose = (): Action => ({
return { type: ACCOUNT.DISPOSE,
type: ACCOUNT.DISPOSE });
}
}

@ -1,20 +1,17 @@
/* @flow */ /* @flow */
'use strict';
import * as SEND from './constants/send';
import * as NOTIFICATION from './constants/notification';
import * as SessionStorageActions from './SessionStorageActions';
import { estimateGas, getGasPrice, pushTx } from './Web3Actions';
import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsUnits from 'ethereumjs-units'; import EthereumjsUnits from 'ethereumjs-units';
import EthereumjsTx from 'ethereumjs-tx'; import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect'; import TrezorConnect from 'trezor-connect';
import { strip } from '../utils/ethUtils';
import { push } from 'react-router-redux'; import { push } from 'react-router-redux';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { strip } from '../utils/ethUtils';
import { estimateGas, getGasPrice, pushTx } from './Web3Actions';
import * as SessionStorageActions from './SessionStorageActions';
import * as NOTIFICATION from './constants/notification';
import * as SEND from './constants/send';
import { initialState } from '../reducers/SendFormReducer'; import { initialState } from '../reducers/SendFormReducer';
import { findAccount } from '../reducers/AccountsReducer'; import { findAccount } from '../reducers/AccountsReducer';
@ -30,7 +27,7 @@ import type {
ThunkAction, ThunkAction,
AsyncAction, AsyncAction,
RouterLocationState, RouterLocationState,
TrezorDevice TrezorDevice,
} from '~/flowtype'; } from '~/flowtype';
import type { State as AccountState } from '../reducers/SelectedAccountReducer'; import type { State as AccountState } from '../reducers/SelectedAccountReducer';
import type { Web3Instance } from '../reducers/Web3Reducer'; import type { Web3Instance } from '../reducers/Web3Reducer';
@ -128,7 +125,7 @@ export const calculateFee = (gasPrice: string, gasLimit: string): string => {
} catch (error) { } catch (error) {
return '0'; return '0';
} }
} };
export const calculateTotal = (amount: string, gasPrice: string, gasLimit: string): string => { export const calculateTotal = (amount: string, gasPrice: string, gasLimit: string): string => {
try { try {
@ -136,7 +133,7 @@ export const calculateTotal = (amount: string, gasPrice: string, gasLimit: strin
} catch (error) { } catch (error) {
return '0'; return '0';
} }
} };
export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimit: string): string => { export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimit: string): string => {
try { try {
@ -148,8 +145,7 @@ export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimi
} catch (error) { } catch (error) {
return '0'; return '0';
} }
};
}
export const calculate = (prevProps: Props, props: Props) => { export const calculate = (prevProps: Props, props: Props) => {
const { const {
@ -173,7 +169,6 @@ export const calculate = (prevProps: Props, props: Props) => {
if (state.setMax) { if (state.setMax) {
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, isToken); const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, isToken);
if (isToken) { if (isToken) {
@ -195,11 +190,11 @@ export const calculate = (prevProps: Props, props: Props) => {
state.selectedFeeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`; state.selectedFeeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`;
state.selectedFeeLevel.gasPrice = state.gasPrice; state.selectedFeeLevel.gasPrice = state.gasPrice;
} }
} };
export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLimit: string, selected?: FeeLevel): Array<FeeLevel> => { export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLimit: string, selected?: FeeLevel): Array<FeeLevel> => {
const price: BigNumber = typeof gasPrice === 'string' ? new BigNumber(gasPrice) : gasPrice const price: BigNumber = typeof gasPrice === 'string' ? new BigNumber(gasPrice) : gasPrice;
const quarter: BigNumber = price.dividedBy(4); const quarter: BigNumber = price.dividedBy(4);
const high: string = price.plus(quarter.times(2)).toString(10); const high: string = price.plus(quarter.times(2)).toString(10);
const low: string = price.minus(quarter.times(2)).toString(10); const low: string = price.minus(quarter.times(2)).toString(10);
@ -208,42 +203,40 @@ export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLi
value: 'Custom', value: 'Custom',
gasPrice: selected.gasPrice, gasPrice: selected.gasPrice,
// label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }` // label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }`
label: `${ calculateFee(selected.gasPrice, gasLimit) } ${ symbol }` label: `${calculateFee(selected.gasPrice, gasLimit)} ${symbol}`,
} : { } : {
value: 'Custom', value: 'Custom',
gasPrice: low, gasPrice: low,
label: '' label: '',
} };
return [ return [
{ {
value: 'High', value: 'High',
gasPrice: high, gasPrice: high,
label: `${ calculateFee(high, gasLimit) } ${ symbol }` label: `${calculateFee(high, gasLimit)} ${symbol}`,
}, },
{ {
value: 'Normal', value: 'Normal',
gasPrice: gasPrice.toString(), gasPrice: gasPrice.toString(),
label: `${ calculateFee(price.toString(10), gasLimit) } ${ symbol }` label: `${calculateFee(price.toString(10), gasLimit)} ${symbol}`,
}, },
{ {
value: 'Low', value: 'Low',
gasPrice: low, gasPrice: low,
label: `${ calculateFee(low, gasLimit) } ${ symbol }` label: `${calculateFee(low, gasLimit)} ${symbol}`,
}, },
customLevel customLevel,
] ];
} };
// initialize component // initialize component
export const init = (): ThunkAction => { export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const { const {
account, account,
network, network,
web3 web3,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account || !network || !web3) return; if (!account || !network || !web3) return;
@ -252,7 +245,7 @@ export const init = (): ThunkAction => {
if (stateFromStorage) { if (stateFromStorage) {
dispatch({ dispatch({
type: SEND.INIT, type: SEND.INIT,
state: stateFromStorage state: stateFromStorage,
}); });
return; return;
} }
@ -280,20 +273,16 @@ export const init = (): ThunkAction => {
dispatch({ dispatch({
type: SEND.INIT, type: SEND.INIT,
state state,
}); });
} };
}
export const toggleAdvanced = (address: string): Action => { export const toggleAdvanced = (address: string): Action => ({
return { type: SEND.TOGGLE_ADVANCED,
type: SEND.TOGGLE_ADVANCED });
}
}
const addressValidation = (): ThunkAction => { const addressValidation = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const { const {
account, account,
network, network,
@ -322,7 +311,7 @@ const addressValidation = (): ThunkAction => {
const otherNetworkAccount = savedAccounts[0]; const otherNetworkAccount = savedAccounts[0];
const device: ?TrezorDevice = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState); const device: ?TrezorDevice = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState);
const coins = getState().localStorage.config.coins; const coins = getState().localStorage.config.coins;
const otherNetwork: ?Coin = coins.find(c => c.network === otherNetworkAccount.network) const otherNetwork: ?Coin = coins.find(c => c.network === otherNetworkAccount.network);
if (device && otherNetwork) { if (device && otherNetwork) {
warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`; warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`;
} }
@ -337,15 +326,13 @@ const addressValidation = (): ThunkAction => {
state: { state: {
...state, ...state,
infos, infos,
warnings warnings,
} },
}) });
} };
}
export const validation = (props: Props): void => { export const validation = (props: Props): void => {
const { const {
account, account,
network, network,
@ -388,7 +375,6 @@ export const validation = (props: Props): void => {
} else if (state.amount.length > 0 && !state.amount.match(numberRegExp)) { } else if (state.amount.length > 0 && !state.amount.match(numberRegExp)) {
errors.amount = 'Amount is not a number'; errors.amount = 'Amount is not a number';
} else { } else {
let decimalRegExp: RegExp; let decimalRegExp: RegExp;
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, state.currency !== state.networkSymbol); const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, state.currency !== state.networkSymbol);
@ -397,7 +383,7 @@ export const validation = (props: Props): void => {
if (token) { if (token) {
if (parseInt(token.decimals) > 0) { if (parseInt(token.decimals) > 0) {
//decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$'); //decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$');
decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9][0-9]*\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$'); decimalRegExp = new RegExp(`^(0|0\\.([0-9]{0,${token.decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${token.decimals}})?|\\.[0-9]{1,${token.decimals}})$`);
} else { } else {
// decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$'); // decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$');
decimalRegExp = new RegExp('^[0-9]+$'); decimalRegExp = new RegExp('^[0-9]+$');
@ -413,11 +399,10 @@ export const validation = (props: Props): void => {
errors.amount = 'Amount is too low'; errors.amount = 'Amount is too low';
} }
} }
} else { } else {
decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$'); decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$');
if (!state.amount.match(decimalRegExp)) { if (!state.amount.match(decimalRegExp)) {
errors.amount = `Maximum 18 decimals allowed`; errors.amount = 'Maximum 18 decimals allowed';
} else if (new BigNumber(state.total).greaterThan(new BigNumber(account.balance).minus(pendingAmount))) { } else if (new BigNumber(state.total).greaterThan(new BigNumber(account.balance).minus(pendingAmount))) {
errors.amount = 'Not enough funds'; errors.amount = 'Not enough funds';
} }
@ -487,14 +472,10 @@ export const validation = (props: Props): void => {
state.errors = errors; state.errors = errors;
state.warnings = warnings; state.warnings = warnings;
state.infos = infos; state.infos = infos;
};
}
export const onAddressChange = (address: string): ThunkAction => { export const onAddressChange = (address: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const state: State = getState().sendForm; const state: State = getState().sendForm;
const touched = { ...state.touched }; const touched = { ...state.touched };
touched.address = true; touched.address = true;
@ -505,16 +486,14 @@ export const onAddressChange = (address: string): ThunkAction => {
...state, ...state,
untouched: false, untouched: false,
touched, touched,
address address,
} },
}); });
dispatch(addressValidation()); dispatch(addressValidation());
} };
}
export const onAmountChange = (amount: string): ThunkAction => { export const onAmountChange = (amount: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const state = getState().sendForm; const state = getState().sendForm;
const touched = { ...state.touched }; const touched = { ...state.touched };
touched.amount = true; touched.amount = true;
@ -527,16 +506,14 @@ export const onAmountChange = (amount: string): ThunkAction => {
touched, touched,
setMax: false, setMax: false,
amount, amount,
} },
}); });
} };
}
export const onCurrencyChange = (currency: { value: string, label: string }): ThunkAction => { export const onCurrencyChange = (currency: { value: string, label: string }): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const { const {
account, account,
network network,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account || !network) return; if (!account || !network) return;
@ -560,13 +537,11 @@ export const onCurrencyChange = (currency: { value: string, label: string }): Th
dispatch({ dispatch({
type: SEND.CURRENCY_CHANGE, type: SEND.CURRENCY_CHANGE,
state state,
}); });
} };
}
export const onSetMax = (): ThunkAction => { export const onSetMax = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const state = getState().sendForm; const state = getState().sendForm;
const touched = { ...state.touched }; const touched = { ...state.touched };
touched.amount = true; touched.amount = true;
@ -578,15 +553,13 @@ export const onSetMax = (): ThunkAction => {
untouched: false, untouched: false,
touched, touched,
setMax: !state.setMax, setMax: !state.setMax,
} },
}); });
} };
}
export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => { export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const { const {
network network,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!network) return; if (!network) return;
@ -605,11 +578,10 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
feeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`; feeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`;
} else { } else {
const customLevel: ?FeeLevel = state.feeLevels.find(f => f.value === 'Custom'); const customLevel: ?FeeLevel = state.feeLevels.find(f => f.value === 'Custom');
if (customLevel) if (customLevel) customLevel.label = '';
customLevel.label = '';
state.gasPrice = feeLevel.gasPrice; state.gasPrice = feeLevel.gasPrice;
if (isToken) { if (isToken) {
state.gasLimit = network.defaultGasLimitTokens.toString() state.gasLimit = network.defaultGasLimitTokens.toString();
} else { } else {
state.gasLimit = state.data.length > 0 ? state.gasLimit : network.defaultGasLimit.toString(); state.gasLimit = state.data.length > 0 ? state.gasLimit : network.defaultGasLimit.toString();
} }
@ -617,19 +589,17 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
dispatch({ dispatch({
type: SEND.FEE_LEVEL_CHANGE, type: SEND.FEE_LEVEL_CHANGE,
state state,
}); });
} };
}
// Manually triggered from user // Manually triggered from user
// Update gasPrice to recommended value // Update gasPrice to recommended value
export const updateFeeLevels = (): ThunkAction => { export const updateFeeLevels = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const { const {
account, account,
network network,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account || !network) return; if (!account || !network) return;
@ -659,14 +629,11 @@ export const updateFeeLevels = (): ThunkAction => {
dispatch({ dispatch({
type: SEND.UPDATE_FEE_LEVELS, type: SEND.UPDATE_FEE_LEVELS,
state state,
}); });
} };
}
export const onGasPriceChange = (gasPrice: string): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
export const onGasPriceChange = (gasPrice: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const currentState: State = getState().sendForm; const currentState: State = getState().sendForm;
const isToken: boolean = currentState.currency !== currentState.networkSymbol; const isToken: boolean = currentState.currency !== currentState.networkSymbol;
@ -677,7 +644,7 @@ export const onGasPriceChange = (gasPrice: string): ThunkAction => {
...currentState, ...currentState,
untouched: false, untouched: false,
touched, touched,
gasPrice: gasPrice, gasPrice,
}; };
if (currentState.selectedFeeLevel.value !== 'Custom') { if (currentState.selectedFeeLevel.value !== 'Custom') {
@ -688,14 +655,11 @@ export const onGasPriceChange = (gasPrice: string): ThunkAction => {
dispatch({ dispatch({
type: SEND.GAS_PRICE_CHANGE, type: SEND.GAS_PRICE_CHANGE,
state state,
}); });
} };
}
export const onGasLimitChange = (gasLimit: string, updateFeeLevels: boolean = false): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
export const onGasLimitChange = (gasLimit: string, updateFeeLevels: boolean = false): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const currentState: State = getState().sendForm; const currentState: State = getState().sendForm;
const isToken: boolean = currentState.currency !== currentState.networkSymbol; const isToken: boolean = currentState.currency !== currentState.networkSymbol;
@ -718,13 +682,11 @@ export const onGasLimitChange = (gasLimit: string, updateFeeLevels: boolean = fa
dispatch({ dispatch({
type: SEND.GAS_LIMIT_CHANGE, type: SEND.GAS_LIMIT_CHANGE,
state state,
}); });
} };
}
export const onNonceChange = (nonce: string): AsyncAction => { export const onNonceChange = (nonce: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const currentState: State = getState().sendForm; const currentState: State = getState().sendForm;
const touched = { ...currentState.touched }; const touched = { ...currentState.touched };
touched.nonce = true; touched.nonce = true;
@ -738,13 +700,11 @@ export const onNonceChange = (nonce: string): AsyncAction => {
dispatch({ dispatch({
type: SEND.NONCE_CHANGE, type: SEND.NONCE_CHANGE,
state state,
}); });
} };
}
export const onDataChange = (data: string): AsyncAction => { export const onDataChange = (data: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const currentState: State = getState().sendForm; const currentState: State = getState().sendForm;
const touched = { ...currentState.touched }; const touched = { ...currentState.touched };
touched.data = true; touched.data = true;
@ -765,20 +725,17 @@ export const onDataChange = (data: string): AsyncAction => {
dispatch({ dispatch({
type: SEND.DATA_CHANGE, type: SEND.DATA_CHANGE,
state state,
}); });
dispatch(estimateGasPrice()); dispatch(estimateGasPrice());
} };
}
const estimateGasPrice = (): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { const {
web3, web3,
network network,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!web3 || !network) return; if (!web3 || !network) return;
@ -801,7 +758,7 @@ const estimateGasPrice = (): AsyncAction => {
} }
// TODO: allow data starting with 0x ... // TODO: allow data starting with 0x ...
const data: string = '0x' + (state.data.length % 2 === 0 ? state.data : '0' + state.data); const data: string = `0x${state.data.length % 2 === 0 ? state.data : `0${state.data}`}`;
const gasLimit = await estimateGas(w3, { const gasLimit = await estimateGas(w3, {
to: '0x0000000000000000000000000000000000000000', to: '0x0000000000000000000000000000000000000000',
data, data,
@ -812,18 +769,14 @@ const estimateGasPrice = (): AsyncAction => {
if (getState().sendForm.data === requestedData) { if (getState().sendForm.data === requestedData) {
dispatch(onGasLimitChange(gasLimit.toString())); dispatch(onGasLimitChange(gasLimit.toString()));
} }
} };
}
export const onSend = (): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { const {
account, account,
network, network,
web3, web3,
pending pending,
} = getState().selectedAccount; } = getState().selectedAccount;
if (!account || !web3 || !network) return; if (!account || !web3 || !network) return;
@ -834,7 +787,7 @@ export const onSend = (): AsyncAction => {
const address_n = account.addressPath; const address_n = account.addressPath;
let data: string = '0x' + currentState.data; let data: string = `0x${currentState.data}`;
let txAmount: string = w3.toHex(w3.toWei(currentState.amount, 'ether')); let txAmount: string = w3.toHex(w3.toWei(currentState.amount, 'ether'));
let txAddress: string = currentState.address; let txAddress: string = currentState.address;
if (isToken) { if (isToken) {
@ -847,7 +800,7 @@ export const onSend = (): AsyncAction => {
data = contract.transfer.getData(currentState.address, amountValue, { data = contract.transfer.getData(currentState.address, amountValue, {
from: account.address, from: account.address,
gasLimit: currentState.gasLimit, gasLimit: currentState.gasLimit,
gasPrice: currentState.gasPrice gasPrice: currentState.gasPrice,
}); });
txAmount = '0x00'; txAmount = '0x00';
txAddress = token.address; txAddress = token.address;
@ -856,7 +809,7 @@ export const onSend = (): AsyncAction => {
const pendingNonce: number = stateUtils.getPendingNonce(pending); const pendingNonce: number = stateUtils.getPendingNonce(pending);
const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce; const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce;
console.warn("NONCE", nonce, account.nonce, pendingNonce) console.warn('NONCE', nonce, account.nonce, pendingNonce);
const txData = { const txData = {
address_n, address_n,
@ -870,17 +823,17 @@ export const onSend = (): AsyncAction => {
gasPrice: w3.toHex(EthereumjsUnits.convert(currentState.gasPrice, 'gwei', 'wei')), gasPrice: w3.toHex(EthereumjsUnits.convert(currentState.gasPrice, 'gwei', 'wei')),
r: '', r: '',
s: '', s: '',
v: '' v: '',
} };
const selected: ?TrezorDevice = getState().wallet.selectedDevice; const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
let signedTransaction = await TrezorConnect.ethereumSignTransaction({ const signedTransaction = await TrezorConnect.ethereumSignTransaction({
device: { device: {
path: selected.path, path: selected.path,
instance: selected.instance, instance: selected.instance,
state: selected.state state: selected.state,
}, },
useEmptyPassphrase: !selected.instance, useEmptyPassphrase: !selected.instance,
path: txData.address_n, path: txData.address_n,
@ -890,11 +843,10 @@ export const onSend = (): AsyncAction => {
to: strip(txData.to), to: strip(txData.to),
value: strip(txData.value), value: strip(txData.value),
data: strip(txData.data), data: strip(txData.data),
chainId: txData.chainId chainId: txData.chainId,
}); });
if (!signedTransaction || !signedTransaction.success) { if (!signedTransaction || !signedTransaction.success) {
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
payload: { payload: {
@ -902,26 +854,25 @@ export const onSend = (): AsyncAction => {
title: 'Transaction error', title: 'Transaction error',
message: signedTransaction.payload.error, message: signedTransaction.payload.error,
cancelable: true, cancelable: true,
actions: [ ] actions: [],
} },
}) });
return; return;
} }
txData.r = '0x' + signedTransaction.payload.r; txData.r = `0x${signedTransaction.payload.r}`;
txData.s = '0x' + signedTransaction.payload.s; txData.s = `0x${signedTransaction.payload.s}`;
txData.v = w3.toHex(signedTransaction.payload.v); txData.v = w3.toHex(signedTransaction.payload.v);
try { try {
const tx = new EthereumjsTx(txData); const tx = new EthereumjsTx(txData);
const serializedTx = '0x' + tx.serialize().toString('hex'); const serializedTx = `0x${tx.serialize().toString('hex')}`;
const txid: string = await pushTx(w3, serializedTx); const txid: string = await pushTx(w3, serializedTx);
dispatch({ dispatch({
type: SEND.TX_COMPLETE, type: SEND.TX_COMPLETE,
account: account, account,
selectedCurrency: currentState.currency, selectedCurrency: currentState.currency,
amount: currentState.amount, amount: currentState.amount,
total: currentState.total, total: currentState.total,
@ -945,12 +896,10 @@ export const onSend = (): AsyncAction => {
title: 'Transaction success', title: 'Transaction success',
message: `<a href="${network.explorer.tx}${txid}" class="green" target="_blank" rel="noreferrer noopener">See transaction detail</a>`, message: `<a href="${network.explorer.tx}${txid}" class="green" target="_blank" rel="noreferrer noopener">See transaction detail</a>`,
cancelable: true, cancelable: true,
actions: [] actions: [],
} },
}); });
} catch (error) { } catch (error) {
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
payload: { payload: {
@ -958,12 +907,11 @@ export const onSend = (): AsyncAction => {
title: 'Transaction error', title: 'Transaction error',
message: error.message || error, message: error.message || error,
cancelable: true, cancelable: true,
actions: [ ] actions: [],
} },
}); });
} }
} };
}
export default { export default {
toggleAdvanced, toggleAdvanced,
@ -978,4 +926,4 @@ export default {
onNonceChange, onNonceChange,
onDataChange, onDataChange,
onSend, onSend,
} };

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import type { State as SendFormState } from '../reducers/SendFormReducer'; import type { State as SendFormState } from '../reducers/SendFormReducer';
import type { import type {
@ -10,8 +10,7 @@ import type {
const PREFIX: string = 'trezor:draft-tx:'; const PREFIX: string = 'trezor:draft-tx:';
export const save = (): ThunkAction => { export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
if (typeof window.localStorage === 'undefined') return; if (typeof window.localStorage === 'undefined') return;
const location = getState().router.location.pathname; const location = getState().router.location.pathname;
@ -20,14 +19,12 @@ export const save = (): ThunkAction => {
try { try {
window.sessionStorage.setItem(`${PREFIX}${location}`, JSON.stringify(state)); window.sessionStorage.setItem(`${PREFIX}${location}`, JSON.stringify(state));
} catch (error) { } catch (error) {
console.error("Saving sessionStorage error: " + error) console.error(`Saving sessionStorage error: ${error}`);
}
}
} }
} }
};
export const load = (location: string): ?SendFormState => { export const load = (location: string): ?SendFormState => {
if (typeof window.localStorage === 'undefined') return; if (typeof window.localStorage === 'undefined') return;
try { try {
@ -39,20 +36,16 @@ export const load = (location: string): ?SendFormState => {
} }
return state; return state;
} catch (error) { } catch (error) {
console.error("Loading sessionStorage error: " + error) console.error(`Loading sessionStorage error: ${error}`);
} }
};
return; export const clear = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
}
export const clear = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
if (typeof window.localStorage === 'undefined') return; if (typeof window.localStorage === 'undefined') return;
const location = getState().router.location.pathname; const location = getState().router.location.pathname;
try { try {
window.sessionStorage.removeItem(`${PREFIX}${location}`); window.sessionStorage.removeItem(`${PREFIX}${location}`);
} catch (error) { } catch (error) {
console.error("Clearing sessionStorage error: " + error) console.error(`Clearing sessionStorage error: ${error}`);
}
}
} }
};

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUtil from 'ethereumjs-util';
import * as SUMMARY from './constants/summary'; import * as SUMMARY from './constants/summary';
@ -7,7 +7,9 @@ import * as TOKEN from './constants/token';
import { resolveAfter } from '../utils/promiseUtils'; import { resolveAfter } from '../utils/promiseUtils';
import { initialState } from '../reducers/SummaryReducer'; import { initialState } from '../reducers/SummaryReducer';
import type { ThunkAction, AsyncAction, Action, GetState, Dispatch } from '~/flowtype'; import type {
ThunkAction, AsyncAction, Action, GetState, Dispatch,
} from '~/flowtype';
import type { State } from '../reducers/SummaryReducer'; import type { State } from '../reducers/SummaryReducer';
import type { Token } from '../reducers/TokensReducer'; import type { Token } from '../reducers/TokensReducer';
@ -20,29 +22,21 @@ export type SummaryAction = {
type: typeof SUMMARY.DETAILS_TOGGLE type: typeof SUMMARY.DETAILS_TOGGLE
} }
export const init = (): ThunkAction => { export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const state: State = { const state: State = {
...initialState, ...initialState,
}; };
dispatch({ dispatch({
type: SUMMARY.INIT, type: SUMMARY.INIT,
state: state state,
}); });
} };
}
export const dispose = (): Action => {
return {
type: SUMMARY.DISPOSE
}
}
export const onDetailsToggle = (): Action => { export const dispose = (): Action => ({
return { type: SUMMARY.DISPOSE,
type: SUMMARY.DETAILS_TOGGLE });
}
}
export const onDetailsToggle = (): Action => ({
type: SUMMARY.DETAILS_TOGGLE,
});

@ -1,10 +1,12 @@
/* @flow */ /* @flow */
'use strict';
import * as TOKEN from './constants/token'; import * as TOKEN from './constants/token';
import { getTokenInfoAsync, getTokenBalanceAsync } from './Web3Actions'; import { getTokenInfoAsync, getTokenBalanceAsync } from './Web3Actions';
import type { GetState, AsyncAction, Action, Dispatch } from '~/flowtype'; import type {
GetState, AsyncAction, Action, Dispatch,
} from '~/flowtype';
import type { State, Token } from '../reducers/TokensReducer'; import type { State, Token } from '../reducers/TokensReducer';
import type { Account } from '../reducers/AccountsReducer'; import type { Account } from '../reducers/AccountsReducer';
import type { NetworkToken } from '../reducers/LocalStorageReducer'; import type { NetworkToken } from '../reducers/LocalStorageReducer';
@ -29,42 +31,32 @@ type SelectOptions = {
// action from component <reactSelect> // action from component <reactSelect>
export const load = (input: string, network: string): AsyncAction => { export const load = (input: string, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<any> => {
return async (dispatch: Dispatch, getState: GetState): Promise<any> => {
if (input.length < 1) return; if (input.length < 1) return;
const tokens = getState().localStorage.tokens[network]; const tokens = getState().localStorage.tokens[network];
const value = input.toLowerCase(); const value = input.toLowerCase();
const result = tokens.filter(t => const result = tokens.filter(t => t.symbol.toLowerCase().indexOf(value) >= 0
t.symbol.toLowerCase().indexOf(value) >= 0 || || t.address.toLowerCase().indexOf(value) >= 0
t.address.toLowerCase().indexOf(value) >= 0 || || t.name.toLowerCase().indexOf(value) >= 0);
t.name.toLowerCase().indexOf(value) >= 0
);
if (result.length > 0) { if (result.length > 0) {
return { options: result }; return { options: result };
} else { }
const web3instance = getState().web3.find(w3 => w3.network === network); const web3instance = getState().web3.find(w3 => w3.network === network);
if (!web3instance) return; if (!web3instance) return;
const info = await getTokenInfoAsync(web3instance.erc20, input); const info = await getTokenInfoAsync(web3instance.erc20, input);
if (info) { if (info) {
return { return {
options: [ info ] options: [info],
} };
} }
//await resolveAfter(300000); //await resolveAfter(300000);
//await resolveAfter(3000); //await resolveAfter(3000);
};
} export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return;
}
}
export const add = (token: NetworkToken, account: Account): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.find(w3 => w3.network === account.network); const web3instance = getState().web3.find(w3 => w3.network === account.network);
if (!web3instance) return; if (!web3instance) return;
@ -77,23 +69,21 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => {
address: token.address, address: token.address,
ethAddress: account.address, ethAddress: account.address,
decimals: token.decimals, decimals: token.decimals,
balance: '0' balance: '0',
} };
dispatch({ dispatch({
type: TOKEN.ADD, type: TOKEN.ADD,
payload: tkn payload: tkn,
}); });
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, tkn); const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, tkn);
dispatch( setBalance(token.address, account.address, tokenBalance) ) dispatch(setBalance(token.address, account.address, tokenBalance));
} };
}
export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => { export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const newState: Array<Token> = [...getState().tokens]; const newState: Array<Token> = [...getState().tokens];
let 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; token.loaded = true;
token.balance = balance; token.balance = balance;
@ -101,14 +91,11 @@ export const setBalance = (tokenAddress: string, ethAddress: string, balance: st
dispatch({ dispatch({
type: TOKEN.SET_BALANCE, type: TOKEN.SET_BALANCE,
payload: newState payload: newState,
}) });
} };
}
export const remove = (token: Token): Action => { export const remove = (token: Token): Action => ({
return {
type: TOKEN.REMOVE, type: TOKEN.REMOVE,
token token,
} });
}

@ -1,7 +1,9 @@
/* @flow */ /* @flow */
'use strict';
import TrezorConnect, { UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT } from 'trezor-connect';
import TrezorConnect, {
UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT,
} from 'trezor-connect';
import * as TOKEN from './constants/token'; import * as TOKEN from './constants/token';
import * as CONNECT from './constants/TrezorConnect'; import * as CONNECT from './constants/TrezorConnect';
import * as NOTIFICATION from './constants/notification'; import * as NOTIFICATION from './constants/notification';
@ -20,7 +22,7 @@ import type {
TransportMessage, TransportMessage,
DeviceMessageType, DeviceMessageType,
TransportMessageType, TransportMessageType,
UiMessageType UiMessageType,
} from 'trezor-connect'; } from 'trezor-connect';
import type { import type {
@ -30,7 +32,7 @@ import type {
ThunkAction, ThunkAction,
AsyncAction, AsyncAction,
TrezorDevice, TrezorDevice,
RouterLocationState RouterLocationState,
} from '~/flowtype'; } from '~/flowtype';
@ -81,15 +83,14 @@ export type TrezorConnectAction = {
}; };
export const init = (): AsyncAction => { export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// set listeners // set listeners
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => { TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
// post event to reducers // post event to reducers
const type: DeviceMessageType = event.type; // assert flow type const type: DeviceMessageType = event.type; // assert flow type
dispatch({ dispatch({
type, type,
device: event.payload device: event.payload,
}); });
}); });
@ -98,7 +99,7 @@ export const init = (): AsyncAction => {
const type: UiMessageType = event.type; // assert flow type const type: UiMessageType = event.type; // assert flow type
dispatch({ dispatch({
type, type,
payload: event.payload payload: event.payload,
}); });
}); });
@ -107,7 +108,7 @@ export const init = (): AsyncAction => {
const type: TransportMessageType = event.type; // assert flow type const type: TransportMessageType = event.type; // assert flow type
dispatch({ dispatch({
type, type,
payload: event.payload payload: event.payload,
}); });
}); });
@ -119,7 +120,7 @@ export const init = (): AsyncAction => {
debug: false, debug: false,
popup: false, popup: false,
webusb: true, webusb: true,
pendingTransportEvent: (getState().devices.length < 1) pendingTransportEvent: (getState().devices.length < 1),
}); });
} catch (error) { } catch (error) {
// dispatch({ // dispatch({
@ -127,17 +128,14 @@ export const init = (): AsyncAction => {
// error // error
// }) // })
} }
} };
}
// called after backend was initialized // called after backend was initialized
// set listeners for connect/disconnect // set listeners for connect/disconnect
export const postInit = (): ThunkAction => { export const postInit = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const handleDeviceConnect = (device: Device) => { const handleDeviceConnect = (device: Device) => {
dispatch(initConnectedDevice(device)); dispatch(initConnectedDevice(device));
} };
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect); TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect); TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
@ -168,7 +166,7 @@ export const postInit = (): ThunkAction => {
// TODO // TODO
if (initialParams) { if (initialParams) {
if (!initialParams.hasOwnProperty("network") && initialPathname !== getState().router.location.pathname) { if (!initialParams.hasOwnProperty('network') && initialPathname !== getState().router.location.pathname) {
// dispatch( push(initialPathname) ); // dispatch( push(initialPathname) );
} else { } else {
@ -176,21 +174,16 @@ export const postInit = (): ThunkAction => {
} }
} }
} }
} };
}
const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => { const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => devices.sort((a, b) => {
return devices.sort((a, b) => {
if (!a.ts || !b.ts) { if (!a.ts || !b.ts) {
return -1; return -1;
} else {
return a.ts > b.ts ? -1 : 1;
} }
return a.ts > b.ts ? -1 : 1;
}); });
}
export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => { export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
// if (!selected || (selected && selected.state)) { // if (!selected || (selected && selected.state)) {
dispatch(onSelectDevice(device)); dispatch(onSelectDevice(device));
@ -200,15 +193,13 @@ export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction
// } else if (!selected) { // } else if (!selected) {
// dispatch( onSelectDevice(device) ); // dispatch( onSelectDevice(device) );
// } // }
} };
}
// selection from Aside dropdown button // selection from Aside dropdown button
// after device_connect event // after device_connect event
// or after acquiring device // or after acquiring device
// device type could be local TrezorDevice or Device (from trezor-connect device_connect event) // device type could be local TrezorDevice or Device (from trezor-connect device_connect event)
export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => { export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
// || device.isUsedElsewhere // || device.isUsedElsewhere
// switch to initial url and reset this value // switch to initial url and reset this value
@ -222,7 +213,6 @@ export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => {
} else if (typeof device.instance === 'number') { } else if (typeof device.instance === 'number') {
dispatch(push(`/device/${device.features.device_id}:${device.instance}`)); dispatch(push(`/device/${device.features.device_id}:${device.instance}`));
} else { } else {
const deviceId: string = device.features.device_id; const deviceId: string = device.features.device_id;
const urlParams: RouterLocationState = getState().router.location.state; const urlParams: RouterLocationState = getState().router.location.state;
// let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`; // let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`;
@ -247,12 +237,9 @@ export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => {
dispatch(push(url)); dispatch(push(url));
} }
} }
} };
}
export const switchToFirstAvailableDevice = (): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const switchToFirstAvailableDevice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { devices } = getState(); const { devices } = getState();
if (devices.length > 0) { if (devices.length > 0) {
// TODO: Priority: // TODO: Priority:
@ -270,23 +257,20 @@ export const switchToFirstAvailableDevice = (): AsyncAction => {
} else { } else {
dispatch(push('/')); dispatch(push('/'));
} }
} };
}
export const getSelectedDeviceState = (): AsyncAction => { export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
if (selected if (selected
&& selected.connected && selected.connected
&& (selected.features && !selected.features.bootloader_mode && selected.features.initialized) && (selected.features && !selected.features.bootloader_mode && selected.features.initialized)
&& !selected.state) { && !selected.state) {
const response = await TrezorConnect.getDeviceState({ const response = await TrezorConnect.getDeviceState({
device: { device: {
path: selected.path, path: selected.path,
instance: selected.instance, instance: selected.instance,
state: selected.state state: selected.state,
}, },
useEmptyPassphrase: !selected.instance, useEmptyPassphrase: !selected.instance,
}); });
@ -295,7 +279,7 @@ export const getSelectedDeviceState = (): AsyncAction => {
dispatch({ dispatch({
type: CONNECT.AUTH_DEVICE, type: CONNECT.AUTH_DEVICE,
device: selected, device: selected,
state: response.payload.state state: response.payload.state,
}); });
} else { } else {
dispatch({ dispatch({
@ -312,22 +296,19 @@ export const getSelectedDeviceState = (): AsyncAction => {
callback: () => { callback: () => {
dispatch({ dispatch({
type: NOTIFICATION.CLOSE, type: NOTIFICATION.CLOSE,
payload: { devicePath: selected.path } payload: { devicePath: selected.path },
}); });
dispatch(getSelectedDeviceState()); dispatch(getSelectedDeviceState());
} },
} },
] ],
} },
}); });
} }
} }
} };
}
export const deviceDisconnect = (device: Device): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected: ?TrezorDevice = getState().wallet.selectedDevice; const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (device && device.features) { if (device && device.features) {
@ -344,11 +325,9 @@ export const deviceDisconnect = (device: Device): AsyncAction => {
}); });
} }
} }
} };
}
export const coinChanged = (network: ?string): ThunkAction => { export const coinChanged = (network: ?string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected: ?TrezorDevice = getState().wallet.selectedDevice; const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
@ -357,23 +336,21 @@ export const coinChanged = (network: ?string): ThunkAction => {
if (network) { if (network) {
dispatch(DiscoveryActions.start(selected, network)); dispatch(DiscoveryActions.start(selected, network));
} }
} };
}
export function reload(): AsyncAction { export function reload(): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
} };
} }
export function acquire(): AsyncAction { export function acquire(): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected: ?TrezorDevice = getState().wallet.selectedDevice; const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
dispatch({ dispatch({
type: CONNECT.START_ACQUIRING, type: CONNECT.START_ACQUIRING,
}) });
const response = await TrezorConnect.getFeatures({ const response = await TrezorConnect.getFeatures({
device: { device: {
@ -398,41 +375,35 @@ export function acquire(): AsyncAction {
// } // }
// } // }
// ] // ]
} },
}) });
} }
dispatch({ dispatch({
type: CONNECT.STOP_ACQUIRING, type: CONNECT.STOP_ACQUIRING,
}) });
} };
} }
export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => { export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
if (device.features) { if (device.features) {
const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`; const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`;
dispatch(push(`/device/${devUrl}/settings`)); dispatch(push(`/device/${devUrl}/settings`));
} }
} };
}
// called from Aside - device menu (forget single instance) // called from Aside - device menu (forget single instance)
export const forget = (device: TrezorDevice): Action => { export const forget = (device: TrezorDevice): Action => ({
return {
type: CONNECT.FORGET_REQUEST, type: CONNECT.FORGET_REQUEST,
device device,
}; });
}
export const duplicateDevice = (device: TrezorDevice): AsyncAction => { export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
dispatch({ dispatch({
type: CONNECT.TRY_TO_DUPLICATE, type: CONNECT.TRY_TO_DUPLICATE,
device device,
}) });
} };
}
export function addAccount(): ThunkAction { export function addAccount(): ThunkAction {
@ -440,5 +411,5 @@ export function addAccount(): ThunkAction {
const selected = getState().wallet.selectedDevice; const selected = getState().wallet.selectedDevice;
if (!selected) return; if (!selected) return;
dispatch(DiscoveryActions.start(selected, getState().router.location.state.network, true)); // TODO: network nicer dispatch(DiscoveryActions.start(selected, getState().router.location.state.network, true)); // TODO: network nicer
} };
} }

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'react-router-redux';
import * as WALLET from './constants/wallet'; import * as WALLET from './constants/wallet';
@ -21,7 +21,7 @@ import type
Action, Action,
Dispatch, Dispatch,
GetState, GetState,
State State,
} from '~/flowtype'; } from '~/flowtype';
export type WalletAction = { export type WalletAction = {
@ -47,62 +47,48 @@ export type WalletAction = {
devices: Array<TrezorDevice> devices: Array<TrezorDevice>
} }
export const init = (): ThunkAction => { export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
const updateOnlineStatus = (event) => { const updateOnlineStatus = (event) => {
dispatch({ dispatch({
type: WALLET.ONLINE_STATUS, type: WALLET.ONLINE_STATUS,
online: navigator.onLine online: navigator.onLine,
}) });
} };
window.addEventListener('online', updateOnlineStatus); window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus); window.addEventListener('offline', updateOnlineStatus);
} };
}
export const onBeforeUnload = (): WalletAction => { export const onBeforeUnload = (): WalletAction => ({
return { type: WALLET.ON_BEFORE_UNLOAD,
type: WALLET.ON_BEFORE_UNLOAD });
}
}
export const toggleDeviceDropdown = (opened: boolean): WalletAction => { export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({
return {
type: WALLET.TOGGLE_DEVICE_DROPDOWN, type: WALLET.TOGGLE_DEVICE_DROPDOWN,
opened opened,
} });
}
// This method will be called after each DEVICE.CONNECT action // This method will be called after each DEVICE.CONNECT action
// if connected device has different "passphrase_protection" settings than saved instances // if connected device has different "passphrase_protection" settings than saved instances
// all saved instances will be removed immediately inside DevicesReducer // all saved instances will be removed immediately inside DevicesReducer
// This method will clear leftovers associated with removed instances from reducers. // This method will clear leftovers associated with removed instances from reducers.
// (DiscoveryReducer, AccountReducer, TokensReducer) // (DiscoveryReducer, AccountReducer, TokensReducer)
export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => { export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
return (dispatch: Dispatch, getState: GetState): void => {
if (!device.features) return; if (!device.features) return;
const affectedDevices = prevState.devices.filter(d => const affectedDevices = prevState.devices.filter(d => d.features
d.features
&& d.features.device_id === device.features.device_id && d.features.device_id === device.features.device_id
&& d.features.passphrase_protection !== device.features.passphrase_protection && d.features.passphrase_protection !== device.features.passphrase_protection);
);
if (affectedDevices.length > 0) { if (affectedDevices.length > 0) {
dispatch({ dispatch({
type: WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA, type: WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
devices: affectedDevices, devices: affectedDevices,
}) });
}
}
} }
};
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => { export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const locationChange: boolean = action.type === LOCATION_CHANGE; const locationChange: boolean = action.type === LOCATION_CHANGE;
const state: State = getState(); const state: State = getState();
@ -113,16 +99,14 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) { if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
dispatch({ dispatch({
type: WALLET.UPDATE_SELECTED_DEVICE, type: WALLET.UPDATE_SELECTED_DEVICE,
device device,
}); });
} else { } else {
dispatch({ dispatch({
type: WALLET.SET_SELECTED_DEVICE, type: WALLET.SET_SELECTED_DEVICE,
device device,
}); });
} }
} }
} }
};
}
}

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import Web3 from 'web3'; import Web3 from 'web3';
import HDKey from 'hdkey'; import HDKey from 'hdkey';
@ -7,11 +7,14 @@ import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx'; import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect'; import TrezorConnect from 'trezor-connect';
import type { ContractFactory, EstimateGasOptions } from 'web3';
import type BigNumber from 'bignumber.js';
import type { TransactionStatus, TransactionReceipt } from 'web3';
import { strip } from '../utils/ethUtils'; import { strip } from '../utils/ethUtils';
import * as WEB3 from './constants/web3'; import * as WEB3 from './constants/web3';
import * as PENDING from './constants/pendingTx'; import * as PENDING from './constants/pendingTx';
import * as AccountsActions from '../actions/AccountsActions'; import * as AccountsActions from './AccountsActions';
import * as TokenActions from '../actions/TokenActions'; import * as TokenActions from './TokenActions';
import type { import type {
Dispatch, Dispatch,
@ -19,15 +22,12 @@ import type {
Action, Action,
AsyncAction, AsyncAction,
} from '~/flowtype'; } from '~/flowtype';
import type { ContractFactory, EstimateGasOptions } from 'web3';
import type BigNumber from 'bignumber.js';
import type { Account } from '../reducers/AccountsReducer'; import type { Account } from '../reducers/AccountsReducer';
import type { PendingTx } from '../reducers/PendingTxReducer'; import type { PendingTx } from '../reducers/PendingTxReducer';
import type { Web3Instance } from '../reducers/Web3Reducer'; import type { Web3Instance } from '../reducers/Web3Reducer';
import type { Token } from '../reducers/TokensReducer'; import type { Token } from '../reducers/TokensReducer';
import type { NetworkToken } from '../reducers/LocalStorageReducer'; import type { NetworkToken } from '../reducers/LocalStorageReducer';
import type { TransactionStatus, TransactionReceipt } from 'web3';
export type Web3Action = { export type Web3Action = {
type: typeof WEB3.READY, type: typeof WEB3.READY,
@ -53,7 +53,6 @@ export type Web3UpdateGasPriceAction = {
export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction { export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { config, ERC20Abi } = getState().localStorage; const { config, ERC20Abi } = getState().localStorage;
const coin = config.coins[coinIndex]; const coin = config.coins[coinIndex];
@ -72,12 +71,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
if (instance) { if (instance) {
const currentHost = instance.currentProvider.host; const currentHost = instance.currentProvider.host;
let currentHostIndex: number = urls.indexOf(currentHost); const currentHostIndex: number = urls.indexOf(currentHost);
if (currentHostIndex + 1 < urls.length) { if (currentHostIndex + 1 < urls.length) {
web3host = urls[currentHostIndex + 1]; web3host = urls[currentHostIndex + 1];
} else { } else {
console.error("TODO: Backend " + network + " not working", instance.currentProvider ); console.error(`TODO: Backend ${network} not working`, instance.currentProvider);
dispatch({ dispatch({
type: WEB3.CREATE, type: WEB3.CREATE,
@ -88,7 +87,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
erc20: instance.eth.contract(ERC20Abi), erc20: instance.eth.contract(ERC20Abi),
latestBlock: '0', latestBlock: '0',
gasPrice: '0', gasPrice: '0',
} },
}); });
// try next coin // try next coin
@ -122,12 +121,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
type: WEB3.CREATE, type: WEB3.CREATE,
instance: { instance: {
network, network,
web3: web3, web3,
chainId: coin.chainId, chainId: coin.chainId,
erc20, erc20,
latestBlock: '0', latestBlock: '0',
gasPrice: '0', gasPrice: '0',
} },
}); });
// dispatch({ // dispatch({
@ -137,8 +136,6 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
// }); // });
// console.log("GET CHAIN", instance.version.network) // console.log("GET CHAIN", instance.version.network)
// instance.version.getWhisper((err, shh) => { // instance.version.getWhisper((err, shh) => {
@ -157,10 +154,9 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
const onBlockMined = async (error: ?Error, blockHash: ?string) => { const onBlockMined = async (error: ?Error, blockHash: ?string) => {
if (error) { if (error) {
window.setTimeout(() => { window.setTimeout(() => {
// try again // try again
onBlockMined(new Error("manually_triggered_error"), undefined); onBlockMined(new Error('manually_triggered_error'), undefined);
}, 30000); }, 30000);
} }
@ -168,7 +164,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
dispatch({ dispatch({
type: WEB3.BLOCK_UPDATED, type: WEB3.BLOCK_UPDATED,
network, network,
blockHash blockHash,
}); });
} }
@ -187,7 +183,6 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
} }
dispatch(getBalance(account)); dispatch(getBalance(account));
// dispatch( getNonce(account) ); // dispatch( getNonce(account) );
} }
const tokens = getState().tokens.filter(t => t.network === network); const tokens = getState().tokens.filter(t => t.network === network);
@ -201,11 +196,10 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
for (const tx of pending) { for (const tx of pending) {
dispatch(getTransactionReceipt(tx)); dispatch(getTransactionReceipt(tx));
} }
};
}
// latestBlockFilter.watch(onBlockMined); // latestBlockFilter.watch(onBlockMined);
onBlockMined(new Error("manually_triggered_error"), undefined); onBlockMined(new Error('manually_triggered_error'), undefined);
// init next coin // init next coin
@ -217,13 +211,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
// instance2.eth.getGasPrice((error, gasPrice) => { // instance2.eth.getGasPrice((error, gasPrice) => {
// console.log("---gasss price from UBQ", gasPrice) // console.log("---gasss price from UBQ", gasPrice)
// }); // });
} };
} }
export function getGasPrice(network: string): AsyncAction { export function getGasPrice(network: string): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const index: number = getState().web3.findIndex(w3 => w3.network === network); const index: number = getState().web3.findIndex(w3 => w3.network === network);
const web3instance = getState().web3[index]; const web3instance = getState().web3[index];
@ -233,18 +226,17 @@ export function getGasPrice(network: string): AsyncAction {
if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) { if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) {
dispatch({ dispatch({
type: WEB3.GAS_PRICE_UPDATED, type: WEB3.GAS_PRICE_UPDATED,
network: network, network,
gasPrice gasPrice,
}); });
} }
} }
}); });
} };
} }
export function getBalance(account: Account): AsyncAction { export function getBalance(account: Account): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0]; const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
const web3: Web3 = web3instance.web3; const web3: Web3 = web3instance.web3;
@ -256,19 +248,18 @@ export function getBalance(account: Account): AsyncAction {
account.address, account.address,
account.network, account.network,
account.deviceState, account.deviceState,
newBalance newBalance,
)); ));
// dispatch( loadHistory(addr) ); // dispatch( loadHistory(addr) );
} }
} }
}); });
} };
} }
export function getTokenBalance(token: Token): AsyncAction { export function getTokenBalance(token: Token): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === token.network)[0]; const web3instance = getState().web3.filter(w3 => w3.network === token.network)[0];
const web3 = web3instance.web3; const web3 = web3instance.web3;
const contract = web3instance.erc20.at(token.address); const contract = web3instance.erc20.at(token.address);
@ -280,18 +271,16 @@ export function getTokenBalance(token: Token): AsyncAction {
dispatch(TokenActions.setBalance( dispatch(TokenActions.setBalance(
token.address, token.address,
token.ethAddress, token.ethAddress,
newBalance newBalance,
)); ));
} }
} }
}); });
} };
} }
export function getNonce(account: Account): AsyncAction { export function getNonce(account: Account): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0]; const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
const web3 = web3instance.web3; const web3 = web3instance.web3;
@ -302,12 +291,10 @@ export function getNonce(account: Account): AsyncAction {
} }
} }
}); });
};
} }
}
export const getTransactionReceipt = (tx: PendingTx): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const getTransactionReceipt = (tx: PendingTx): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0]; const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0];
const web3 = web3instance.web3; const web3 = web3instance.web3;
@ -316,31 +303,28 @@ export const getTransactionReceipt = (tx: PendingTx): AsyncAction => {
dispatch({ dispatch({
type: PENDING.TX_NOT_FOUND, type: PENDING.TX_NOT_FOUND,
tx, tx,
}) });
} else if (status && status.blockNumber) { } else if (status && status.blockNumber) {
web3.eth.getTransactionReceipt(tx.id, (error: Error, receipt: TransactionReceipt) => { web3.eth.getTransactionReceipt(tx.id, (error: Error, receipt: TransactionReceipt) => {
if (receipt) { if (receipt) {
if (status.gas !== receipt.gasUsed) { if (status.gas !== receipt.gasUsed) {
dispatch({ dispatch({
type: PENDING.TX_TOKEN_ERROR, type: PENDING.TX_TOKEN_ERROR,
tx tx,
}) });
} }
dispatch({ dispatch({
type: PENDING.TX_RESOLVED, type: PENDING.TX_RESOLVED,
tx, tx,
receipt receipt,
})
}
}); });
} }
}); });
} }
} });
};
export const getTransaction = (web3: Web3, txid: string): Promise<any> => { export const getTransaction = (web3: Web3, txid: string): Promise<any> => new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
web3.eth.getTransaction(txid, (error, result) => { web3.eth.getTransaction(txid, (error, result) => {
if (error) { if (error) {
reject(error); reject(error);
@ -349,10 +333,8 @@ export const getTransaction = (web3: Web3, txid: string): Promise<any> => {
} }
}); });
}); });
}
export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber> => { export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber> => new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
web3.eth.getBalance(address, (error: Error, result: BigNumber) => { web3.eth.getBalance(address, (error: Error, result: BigNumber) => {
if (error) { if (error) {
reject(error); reject(error);
@ -361,11 +343,8 @@ export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber>
} }
}); });
}); });
}
export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Promise<string> => {
return new Promise((resolve, reject) => {
export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Promise<string> => new Promise((resolve, reject) => {
const contract = erc20.at(token.address); const contract = erc20.at(token.address);
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => { contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
if (error) { if (error) {
@ -376,10 +355,8 @@ export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Prom
} }
}); });
}); });
}
export const getNonceAsync = (web3: Web3, address: string): Promise<number> => { export const getNonceAsync = (web3: Web3, address: string): Promise<number> => new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
web3.eth.getTransactionCount(address, (error: Error, result: number) => { web3.eth.getTransactionCount(address, (error: Error, result: number) => {
if (error) { if (error) {
reject(error); reject(error);
@ -388,12 +365,9 @@ export const getNonceAsync = (web3: Web3, address: string): Promise<number> => {
} }
}); });
}); });
}
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => { export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
const contract = erc20.at(address, (error, res) => { const contract = erc20.at(address, (error, res) => {
// console.warn("callback", error, res) // console.warn("callback", error, res)
}); });
@ -402,24 +376,24 @@ export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Prom
address, address,
name: '', name: '',
symbol: '', symbol: '',
decimals: 0 decimals: 0,
}; };
contract.name.call((error: Error, name: string) => { contract.name.call((error: Error, name: string) => {
if (error) { if (error) {
resolve(null); resolve(null);
return; return;
} else {
info.name = name;
} }
info.name = name;
contract.symbol.call((error: Error, symbol: string) => { contract.symbol.call((error: Error, symbol: string) => {
if (error) { if (error) {
resolve(null); resolve(null);
return; return;
} else {
info.symbol = symbol;
} }
info.symbol = symbol;
contract.decimals.call((error: Error, decimals: BigNumber) => { contract.decimals.call((error: Error, decimals: BigNumber) => {
if (decimals) { if (decimals) {
@ -429,13 +403,11 @@ export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Prom
resolve(null); resolve(null);
} }
}); });
})
}); });
}); });
} });
export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => { export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => { web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => {
if (error) { if (error) {
reject(error); reject(error);
@ -443,11 +415,9 @@ export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<nu
resolve(gas); resolve(gas);
} }
}); });
}) });
}
export const pushTx = (web3: Web3, tx: any): Promise<string> => { export const pushTx = (web3: Web3, tx: any): Promise<string> => new Promise((resolve, reject) => {
return new Promise((resolve, reject) => {
web3.eth.sendRawTransaction(tx, (error: Error, result: string) => { web3.eth.sendRawTransaction(tx, (error: Error, result: string) => {
if (error) { if (error) {
reject(error); reject(error);
@ -455,5 +425,4 @@ export const pushTx = (web3: Web3, tx: any): Promise<string> => {
resolve(result); resolve(result);
} }
}); });
}) });
}

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
//regExp1 : string = '(.*)' //regExp1 : string = '(.*)'
//regExp2 : '$1' = '$1' //regExp2 : '$1' = '$1'

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const INIT: 'account__init' = 'account__init'; export const INIT: 'account__init' = 'account__init';
export const DISPOSE: 'account__dispose' = 'account__dispose'; export const DISPOSE: 'account__dispose' = 'account__dispose';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const START: 'discovery__start' = 'discovery__start'; export const START: 'discovery__start' = 'discovery__start';
export const STOP: 'discovery__stop' = 'discovery__stop'; export const STOP: 'discovery__stop' = 'discovery__stop';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const SAVE: 'storage__save' = 'storage__save'; export const SAVE: 'storage__save' = 'storage__save';
export const READY: 'storage__ready' = 'storage__ready'; export const READY: 'storage__ready' = 'storage__ready';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const OPEN: 'log__open' = 'log__open'; export const OPEN: 'log__open' = 'log__open';
export const CLOSE: 'log__close' = 'log__close'; export const CLOSE: 'log__close' = 'log__close';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const ON_PASSPHRASE_CHANGE: 'action__on_passphrase_change' = 'action__on_passphrase_change'; export const ON_PASSPHRASE_CHANGE: 'action__on_passphrase_change' = 'action__on_passphrase_change';
export const ON_PASSPHRASE_SHOW: 'action__on_passphrase_show' = 'action__on_passphrase_show'; export const ON_PASSPHRASE_SHOW: 'action__on_passphrase_show' = 'action__on_passphrase_show';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const ADD: 'notification__add' = 'notification__add'; export const ADD: 'notification__add' = 'notification__add';
export const CLOSE: 'notification__close' = 'notification__close'; export const CLOSE: 'notification__close' = 'notification__close';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage'; export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage';
export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved'; export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const INIT: 'receive__init' = 'receive__init'; export const INIT: 'receive__init' = 'receive__init';
export const DISPOSE: 'receive__dispose' = 'receive__dispose'; export const DISPOSE: 'receive__dispose' = 'receive__dispose';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const INIT: 'send__init' = 'send__init'; export const INIT: 'send__init' = 'send__init';
export const DISPOSE: 'send__dispose' = 'send__dispose'; export const DISPOSE: 'send__dispose' = 'send__dispose';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const INIT: 'summary__init' = 'summary__init'; export const INIT: 'summary__init' = 'summary__init';
export const DISPOSE: 'summary__dispose' = 'summary__dispose'; export const DISPOSE: 'summary__dispose' = 'summary__dispose';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const ADD: 'token__add' = 'token__add'; export const ADD: 'token__add' = 'token__add';
export const REMOVE: 'token__remove' = 'token__remove'; export const REMOVE: 'token__remove' = 'token__remove';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const ON_BEFORE_UNLOAD: 'wallet__on_before_unload' = 'wallet__on_before_unload'; export const ON_BEFORE_UNLOAD: 'wallet__on_before_unload' = 'wallet__on_before_unload';
export const TOGGLE_DEVICE_DROPDOWN: 'wallet__toggle_dropdown' = 'wallet__toggle_dropdown'; export const TOGGLE_DEVICE_DROPDOWN: 'wallet__toggle_dropdown' = 'wallet__toggle_dropdown';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
export const START: 'web3__start' = 'web3__start'; export const START: 'web3__start' = 'web3__start';
export const STOP: 'web3__stop' = 'web3__stop'; export const STOP: 'web3__stop' = 'web3__stop';

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -12,8 +12,7 @@ type Props = {
toggle: typeof LogActions.toggle toggle: typeof LogActions.toggle
} }
const Footer = (props: Props): React$Element<string> => { const Footer = (props: Props): React$Element<string> => (
return (
<footer> <footer>
<span>© 2018</span> <span>© 2018</span>
<a href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" className="satoshi green">SatoshiLabs</a> <a href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" className="satoshi green">SatoshiLabs</a>
@ -21,17 +20,12 @@ const Footer = (props: Props): React$Element<string> => {
<a onClick={props.toggle} className="green">Show Log</a> <a onClick={props.toggle} className="green">Show Log</a>
</footer> </footer>
); );
}
export default connect( export default connect(
(state: State) => { (state: State) => ({
return {
} }),
}, (dispatch: Dispatch) => ({
(dispatch: Dispatch) => {
return {
toggle: bindActionCreators(LogActions.toggle, dispatch), toggle: bindActionCreators(LogActions.toggle, dispatch),
}; }),
}
)(Footer); )(Footer);

@ -1,10 +1,9 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
const Header = (): React$Element<string> => { const Header = (): React$Element<string> => (
return (
<header> <header>
<div className="layout-wrapper"> <div className="layout-wrapper">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 163.7 41.9" width="100%" height="100%" preserveAspectRatio="xMinYMin meet"> <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 163.7 41.9" width="100%" height="100%" preserveAspectRatio="xMinYMin meet">
@ -25,6 +24,5 @@ const Header = (): React$Element<string> => {
</div> </div>
</header> </header>
); );
}
export default Header; export default Header;

@ -1,14 +1,13 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
export default (props: { size: string, label?: string }): React$Element<string> => { export default (props: { size: string, label?: string }): React$Element<string> => {
const style = { const style = {
width: `${props.size}px`, width: `${props.size}px`,
height: `${props.size}px`, height: `${props.size}px`,
} };
return ( return (
<div className="loader-circle" style={style}> <div className="loader-circle" style={style}>
@ -19,4 +18,4 @@ export default (props: { size: string, label?: string }): React$Element<string>
</svg> </svg>
</div> </div>
); );
} };

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -14,8 +14,7 @@ type Props = {
} }
const Log = (props: Props): ?React$Element<string> => { const Log = (props: Props): ?React$Element<string> => {
if (!props.log.opened) if (!props.log.opened) return null;
return null;
// const entries = props.log.entries.map(entry => { // const entries = props.log.entries.map(entry => {
// return ( // return (
@ -25,23 +24,19 @@ const Log = (props: Props): ?React$Element<string> => {
return ( return (
<div className="log"> <div className="log">
<button className="log-close transparent" onClick={ props.toggle }></button> <button className="log-close transparent" onClick={props.toggle} />
<h2>Log</h2> <h2>Log</h2>
<p>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</p> <p>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</p>
<textarea value={ JSON.stringify(props.log.entries) } readOnly></textarea> <textarea value={JSON.stringify(props.log.entries)} readOnly />
</div> </div>
) );
} };
export default connect( export default connect(
(state: State) => { (state: State) => ({
return { log: state.log,
log: state.log }),
}; (dispatch: Dispatch) => ({
},
(dispatch: Dispatch) => {
return {
toggle: bindActionCreators(LogActions.toggle, dispatch), toggle: bindActionCreators(LogActions.toggle, dispatch),
}; }),
}
)(Log); )(Log);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -27,21 +27,21 @@ type NProps = {
export const Notification = (props: NProps): React$Element<string> => { export const Notification = (props: NProps): React$Element<string> => {
const className = `notification ${props.className}`; const className = `notification ${props.className}`;
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
const actionButtons = props.actions ? props.actions.map((a, i) => { const actionButtons = props.actions ? props.actions.map((a, i) => (
return ( <button key={i} onClick={(event) => { close(); a.callback(); }} className="transparent">{ a.label }</button>
<button key={ i } onClick={ event => { close(); a.callback(); } } className="transparent">{ a.label }</button> )) : null;
)
}) : null;
return ( return (
<div className={className}> <div className={className}>
{ props.cancelable ? ( { props.cancelable ? (
<button className="notification-close transparent" <button
onClick={ event => close() }></button> className="notification-close transparent"
onClick={event => close()}
/>
) : null } ) : null }
<div className="notification-body"> <div className="notification-body">
<h2>{ props.title }</h2> <h2>{ props.title }</h2>
{ props.message ? (<p dangerouslySetInnerHTML={{__html: props.message }}></p>) : null } { props.message ? (<p dangerouslySetInnerHTML={{ __html: props.message }} />) : null }
</div> </div>
{ props.actions && props.actions.length > 0 ? ( { props.actions && props.actions.length > 0 ? (
<div className="notification-action"> <div className="notification-action">
@ -50,13 +50,12 @@ export const Notification = (props: NProps): React$Element<string> => {
) : null } ) : null }
</div> </div>
) );
} };
export const NotificationGroup = (props: Props) => { export const NotificationGroup = (props: Props) => {
const { notifications, close } = props; const { notifications, close } = props;
return notifications.map((n, i) => { return notifications.map((n, i) => (
return (
<Notification <Notification
key={i} key={i}
className={n.type} className={n.type}
@ -66,19 +65,14 @@ export const NotificationGroup = (props: Props) => {
actions={n.actions} actions={n.actions}
close={close} close={close}
/> />
) ));
}); };
}
export default connect( export default connect(
(state: State) => { (state: State) => ({
return { notifications: state.notifications,
notifications: state.notifications }),
}; (dispatch: Dispatch) => ({
},
(dispatch: Dispatch) => {
return {
close: bindActionCreators(NotificationActions.close, dispatch), close: bindActionCreators(NotificationActions.close, dispatch),
}; }),
}
)(NotificationGroup); )(NotificationGroup);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import TrezorConnect from 'trezor-connect'; import TrezorConnect from 'trezor-connect';
@ -25,14 +25,12 @@ const DisconnectDevice = (props: Props) => {
</span> </span>
</p> </p>
</div> </div>
<div className="image"> <div className="image" />
</div>
</main> </main>
) );
} };
const ConnectHIDDevice = (props: Props) => { const ConnectHIDDevice = (props: Props) => (
return (
<main> <main>
<h2 className="claim">The private bank in your hands.</h2> <h2 className="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>
@ -42,10 +40,10 @@ const ConnectHIDDevice = (props: Props) => {
<span> <span>
<svg width="12px" height="35px" viewBox="0 0 20 57"> <svg width="12px" height="35px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)"> <g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5"></rect> <rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect> <rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z"></path> <path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></rect> <rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
</g> </g>
</svg> </svg>
Connect TREZOR to continue Connect TREZOR to continue
@ -59,7 +57,6 @@ const ConnectHIDDevice = (props: Props) => {
</div> </div>
</main> </main>
); );
}
class ConnectWebUsbDevice extends Component<Props> { class ConnectWebUsbDevice extends Component<Props> {
componentDidMount(): void { componentDidMount(): void {
@ -81,10 +78,10 @@ class ConnectWebUsbDevice extends Component<Props> {
<span> <span>
<svg width="12px" height="35px" viewBox="0 0 20 57"> <svg width="12px" height="35px" viewBox="0 0 20 57">
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)"> <g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5"></rect> <rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect> <rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z"></path> <path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></rect> <rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
</g> </g>
</svg> </svg>
Connect TREZOR Connect TREZOR
@ -107,12 +104,11 @@ class ConnectWebUsbDevice extends Component<Props> {
const ConnectDevice = (props: Props) => { const ConnectDevice = (props: Props) => {
const { transport, disconnectRequest } = props; const { transport, disconnectRequest } = props;
if (disconnectRequest) { if (disconnectRequest) {
return <DisconnectDevice {...props} /> return <DisconnectDevice {...props} />;
} else if (transport && transport.version.indexOf('webusb') >= 0) { } if (transport && transport.version.indexOf('webusb') >= 0) {
return <ConnectWebUsbDevice {...props} /> return <ConnectWebUsbDevice {...props} />;
} else {
return <ConnectHIDDevice {...props} />
}
} }
return <ConnectHIDDevice {...props} />;
};
export default ConnectDevice; export default ConnectDevice;

@ -1,9 +1,9 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Preloader from './Preloader';
import Select from 'react-select'; import Select from 'react-select';
import Preloader from './Preloader';
type State = { type State = {
version: string; version: string;
@ -35,7 +35,6 @@ type Props = {
} }
export default class InstallBridge extends Component<Props, State> { export default class InstallBridge extends Component<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
@ -49,7 +48,7 @@ export default class InstallBridge extends Component<Props, State> {
onChange(value: InstallTarget) { onChange(value: InstallTarget) {
this.setState({ this.setState({
target: value target: value,
}); });
} }
@ -57,8 +56,8 @@ export default class InstallBridge extends Component<Props, State> {
if (this.props.browserState.osname && !this.state.target) { if (this.props.browserState.osname && !this.state.target) {
const currentTarget: ?InstallTarget = installers.find(i => i.id === this.props.browserState.osname); const currentTarget: ?InstallTarget = installers.find(i => i.id === this.props.browserState.osname);
this.setState({ this.setState({
target: currentTarget target: currentTarget,
}) });
} }
} }
@ -81,7 +80,8 @@ export default class InstallBridge extends Component<Props, State> {
clearable={false} clearable={false}
value={this.state.target} value={this.state.target}
onChange={this.onChange.bind(this)} onChange={this.onChange.bind(this)}
options={ installers } /> options={installers}
/>
<a href={url} className="button">Download for { label }</a> <a href={url} className="button">Download for { label }</a>
</div> </div>
<p>Learn more about latest version in <a href="https://github.com/trezor/trezord-go/blob/master/CHANGELOG.md" className="green" target="_blank" rel="noreferrer noopener">Changelog</a></p> <p>Learn more about latest version in <a href="https://github.com/trezor/trezord-go/blob/master/CHANGELOG.md" className="green" target="_blank" rel="noreferrer noopener">Changelog</a></p>

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import Preloader from './Preloader'; import Preloader from './Preloader';
@ -14,8 +14,7 @@ import Notifications, { Notification } from '../common/Notification';
import type { Props } from './index'; import type { Props } from './index';
const BrowserNotSupported = (props: {}): React$Element<string> => { const BrowserNotSupported = (props: {}): React$Element<string> => (
return (
<main> <main>
<h2>Your browser is not supported</h2> <h2>Your browser is not supported</h2>
<p>Please choose one of the supported browsers</p> <p>Please choose one of the supported browsers</p>
@ -30,13 +29,10 @@ const BrowserNotSupported = (props: {}): React$Element<string> => {
</div> </div>
</div> </div>
</main> </main>
) );
}
export default (props: Props) => { export default (props: Props) => {
const web3 = props.web3; const web3 = props.web3;
const { devices } = props; const { devices } = props;
const { browserState, transport } = props.connect; const { browserState, transport } = props.connect;
@ -49,14 +45,16 @@ export default (props: Props) => {
const bridgeRoute: boolean = props.router.location.state.hasOwnProperty('bridge'); const bridgeRoute: boolean = props.router.location.state.hasOwnProperty('bridge');
if (localStorageError) { if (localStorageError) {
notification = (<Notification notification = (
<Notification
title="Initialization error" title="Initialization error"
message="Config files are missing" message="Config files are missing"
className="error" className="error"
/>); />
);
css += ' config-error'; css += ' config-error';
} else if (browserState.supported === false) { } else if (browserState.supported === false) {
css += ' browser-not-supported' css += ' browser-not-supported';
body = <BrowserNotSupported />; body = <BrowserNotSupported />;
} else if (connectError || bridgeRoute) { } else if (connectError || bridgeRoute) {
css += ' install-bridge'; css += ' install-bridge';
@ -77,7 +75,6 @@ export default (props: Props) => {
<Footer /> <Footer />
</div> </div>
); );
} else {
return (<Preloader />);
}
} }
return (<Preloader />);
};

@ -1,12 +1,10 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
export default (props: {}): React$Element<string> => { export default (props: {}): React$Element<string> => (
return (
<section className="landing"> <section className="landing">
localstorage ERROR localstorage ERROR
</section> </section>
); );
}

@ -1,13 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import Loader from '../common/LoaderCircle'; import Loader from '../common/LoaderCircle';
export default (props: {}): React$Element<string> => { export default (props: {}): React$Element<string> => (
return (
<section className="landing"> <section className="landing">
<Loader label="Loading" size="100" /> <Loader label="Loading" size="100" />
</section> </section>
); );
}

@ -1,12 +1,10 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
export default (props: {}): React$Element<string> => { export default (props: {}): React$Element<string> => (
return (
<section className="landing"> <section className="landing">
connect ERROR connect ERROR
</section> </section>
); );
}

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -31,8 +31,7 @@ type OwnProps = {
export type Props = StateProps & DispatchProps; export type Props = StateProps & DispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => { const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => ({
return {
localStorage: state.localStorage, localStorage: state.localStorage,
modal: state.modal, modal: state.modal,
web3: state.web3, web3: state.web3,
@ -41,13 +40,10 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
router: state.router, router: state.router,
wallet: state.wallet, wallet: state.wallet,
devices: state.devices, devices: state.devices,
}; });
}
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => { const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
return {
}; });
}
export default connect(mapStateToProps, mapDispatchToProps)(LandingPage); export default connect(mapStateToProps, mapDispatchToProps)(LandingPage);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { findAccount } from '~/js/reducers/AccountsReducer'; import { findAccount } from '~/js/reducers/AccountsReducer';
@ -7,10 +7,9 @@ import { findAccount } from '~/js/reducers/AccountsReducer';
import type { Props } from './index'; import type { Props } from './index';
const ConfirmAddress = (props: Props) => { const ConfirmAddress = (props: Props) => {
const { const {
account, account,
network network,
} = props.selectedAccount; } = props.selectedAccount;
if (!account || !network) return null; if (!account || !network) return null;
@ -26,11 +25,10 @@ const ConfirmAddress = (props: Props) => {
</div> </div>
</div> </div>
); );
} };
export default ConfirmAddress; export default ConfirmAddress;
export class ConfirmUnverifiedAddress extends Component<Props> { export class ConfirmUnverifiedAddress extends Component<Props> {
keyboardHandler: (event: KeyboardEvent) => void; keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void { keyboardHandler(event: KeyboardEvent): void {
@ -52,13 +50,12 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
verifyAddress() { verifyAddress() {
if (!this.props.modal.opened) return; if (!this.props.modal.opened) return;
const { const {
account account,
} = this.props.selectedAccount; } = this.props.selectedAccount;
if (!account) return null; if (!account) return null;
this.props.modalActions.onCancel(); this.props.modalActions.onCancel();
this.props.receiveActions.showAddress(account.addressPath); this.props.receiveActions.showAddress(account.addressPath);
} }
showUnverifiedAddress() { showUnverifiedAddress() {
@ -71,11 +68,11 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
render() { render() {
if (!this.props.modal.opened) return null; if (!this.props.modal.opened) return null;
const { const {
device device,
} = this.props.modal; } = this.props.modal;
const { const {
onCancel onCancel,
} = this.props.modalActions; } = this.props.modalActions;
let deviceStatus: string; let deviceStatus: string;
@ -83,7 +80,7 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
if (!device.connected) { if (!device.connected) {
deviceStatus = `${device.label} is not connected`; deviceStatus = `${device.label} is not connected`;
claim = 'Please connect your device' claim = 'Please connect your device';
} else { } else {
// corner-case where device is connected but it is unavailable because it was created with different "passphrase_protection" settings // corner-case where device is connected but it is unavailable because it was created with different "passphrase_protection" settings
const enable: string = device.features && device.features.passphrase_protection ? 'enable' : 'disable'; const enable: string = device.features && device.features.passphrase_protection ? 'enable' : 'disable';
@ -93,7 +90,7 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
return ( return (
<div className="confirm-address-unverified"> <div className="confirm-address-unverified">
<button className="close-modal transparent" onClick={ onCancel }></button> <button className="close-modal transparent" onClick={onCancel} />
<h3>{ deviceStatus }</h3> <h3>{ deviceStatus }</h3>
<p>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</p> <p>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</p>
<button onClick={event => this.verifyAddress()}>Try again</button> <button onClick={event => this.verifyAddress()}>Try again</button>

@ -1,11 +1,10 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import type { Props } from './index'; import type { Props } from './index';
const Confirmation = (props: Props) => { const Confirmation = (props: Props) => {
if (!props.modal.opened) return null; if (!props.modal.opened) return null;
const { device } = props.modal; const { device } = props.modal;
@ -14,7 +13,7 @@ const Confirmation = (props: Props) => {
address, address,
currency, currency,
total, total,
selectedFeeLevel selectedFeeLevel,
} = props.sendForm; } = props.sendForm;
return ( return (
@ -33,6 +32,6 @@ const Confirmation = (props: Props) => {
</div> </div>
</div> </div>
); );
} };
export default Confirmation; export default Confirmation;

@ -1,8 +1,8 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { getNewInstance } from '~/js/reducers/DevicesReducer' import { getNewInstance } from '~/js/reducers/DevicesReducer';
import type { Props } from './index'; import type { Props } from './index';
type State = { type State = {
@ -13,9 +13,10 @@ type State = {
} }
export default class DuplicateDevice extends Component<Props, State> { export default class DuplicateDevice extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void; keyboardHandler: (event: KeyboardEvent) => void;
state: State; state: State;
input: ?HTMLInputElement; input: ?HTMLInputElement;
constructor(props: Props) { constructor(props: Props) {
@ -31,7 +32,7 @@ export default class DuplicateDevice extends Component<Props, State> {
instance, instance,
instanceName: null, instanceName: null,
isUsed: false, isUsed: false,
} };
} }
keyboardHandler(event: KeyboardEvent): void { keyboardHandler(event: KeyboardEvent): void {
@ -43,8 +44,7 @@ export default class DuplicateDevice extends Component<Props, State> {
componentDidMount(): void { componentDidMount(): void {
// one time autofocus // one time autofocus
if (this.input) if (this.input) this.input.focus();
this.input.focus();
this.keyboardHandler = this.keyboardHandler.bind(this); this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false); window.addEventListener('keydown', this.keyboardHandler, false);
} }
@ -54,7 +54,6 @@ export default class DuplicateDevice extends Component<Props, State> {
} }
onNameChange = (value: string): void => { onNameChange = (value: string): void => {
let isUsed: boolean = false; let isUsed: boolean = false;
if (value.length > 0) { if (value.length > 0) {
isUsed = (this.props.devices.find(d => d.instanceName === value) !== undefined); isUsed = (this.props.devices.find(d => d.instanceName === value) !== undefined);
@ -62,7 +61,7 @@ export default class DuplicateDevice extends Component<Props, State> {
this.setState({ this.setState({
instanceName: value.length > 0 ? value : null, instanceName: value.length > 0 ? value : null,
isUsed isUsed,
}); });
} }
@ -72,7 +71,6 @@ export default class DuplicateDevice extends Component<Props, State> {
} }
render() { render() {
if (!this.props.modal.opened) return null; if (!this.props.modal.opened) return null;
const { device } = this.props.modal; const { device } = this.props.modal;
@ -80,12 +78,12 @@ export default class DuplicateDevice extends Component<Props, State> {
const { const {
defaultName, defaultName,
instanceName, instanceName,
isUsed isUsed,
} = this.state; } = this.state;
return ( return (
<div className="duplicate"> <div className="duplicate">
<button className="close-modal transparent" onClick={ onCancel }></button> <button className="close-modal transparent" onClick={onCancel} />
<h3>Clone { device.label }?</h3> <h3>Clone { device.label }?</h3>
<p>This will create new instance of device which can be used with different passphrase</p> <p>This will create new instance of device which can be used with different passphrase</p>
<div className="row"> <div className="row">
@ -100,7 +98,8 @@ export default class DuplicateDevice extends Component<Props, State> {
placeholder={defaultName} placeholder={defaultName}
ref={(element) => { this.input = element; }} ref={(element) => { this.input = element; }}
onChange={event => this.onNameChange(event.currentTarget.value)} onChange={event => this.onNameChange(event.currentTarget.value)}
defaultValue={ instanceName } /> defaultValue={instanceName}
/>
{ isUsed ? <span className="error">Instance name is already in use</span> : null } { isUsed ? <span className="error">Instance name is already in use</span> : null }
</div> </div>
<button disabled={isUsed} onClick={event => this.submit()}>Create new instance</button> <button disabled={isUsed} onClick={event => this.submit()}>Create new instance</button>

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import type { Props } from './index'; import type { Props } from './index';
@ -14,6 +14,6 @@ const InvalidPin = (props: Props) => {
<p>Retrying...</p> <p>Retrying...</p>
</div> </div>
); );
} };
export default InvalidPin; export default InvalidPin;

@ -1,10 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import raf from 'raf'; import raf from 'raf';
import type { Props } from './index'; import type { Props } from './index';
type State = { type State = {
deviceLabel: string; deviceLabel: string;
singleInput: boolean; singleInput: boolean;
@ -18,10 +19,12 @@ type State = {
} }
export default class PinModal extends Component<Props, State> { export default class PinModal extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void; keyboardHandler: (event: KeyboardEvent) => void;
state: State; state: State;
passphraseInput: ?HTMLInputElement; passphraseInput: ?HTMLInputElement;
passphraseRevisionInput: ?HTMLInputElement; passphraseRevisionInput: ?HTMLInputElement;
constructor(props: Props) { constructor(props: Props) {
@ -48,13 +51,11 @@ export default class PinModal extends Component<Props, State> {
passphraseRevisionFocused: false, passphraseRevisionFocused: false,
passphraseRevisionTouched: false, passphraseRevisionTouched: false,
match: true, match: true,
visible: false visible: false,
} };
} }
keyboardHandler(event: KeyboardEvent): void { keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) { if (event.keyCode === 13) {
event.preventDefault(); event.preventDefault();
//this.passphraseInput.blur(); //this.passphraseInput.blur();
@ -73,13 +74,11 @@ export default class PinModal extends Component<Props, State> {
componentDidMount(): void { componentDidMount(): void {
// one time autofocus // one time autofocus
if (this.passphraseInput) if (this.passphraseInput) this.passphraseInput.focus();
this.passphraseInput.focus();
this.keyboardHandler = this.keyboardHandler.bind(this); this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false); window.addEventListener('keydown', this.keyboardHandler, false);
// document.oncontextmenu = (event) => { // document.oncontextmenu = (event) => {
// const el = window.event.srcElement || event.target; // const el = window.event.srcElement || event.target;
// const type = el.tagName.toLowerCase() || ''; // const type = el.tagName.toLowerCase() || '';
@ -106,7 +105,7 @@ export default class PinModal extends Component<Props, State> {
passphraseRevision, passphraseRevision,
passphraseFocused, passphraseFocused,
passphraseRevisionFocused, passphraseRevisionFocused,
visible visible,
} = this.state; } = this.state;
// } = this.props.modal; // } = this.props.modal;
@ -121,11 +120,11 @@ export default class PinModal extends Component<Props, State> {
if (this.passphraseInput) { if (this.passphraseInput) {
this.passphraseInput.value = passphraseInputValue; this.passphraseInput.value = passphraseInputValue;
this.passphraseInput.setAttribute("type", visible || (!visible && !passphraseFocused) ? "text" : "password"); this.passphraseInput.setAttribute('type', visible || (!visible && !passphraseFocused) ? 'text' : 'password');
} }
if (this.passphraseRevisionInput) { if (this.passphraseRevisionInput) {
this.passphraseRevisionInput.value = passphraseRevisionInputValue; this.passphraseRevisionInput.value = passphraseRevisionInputValue;
this.passphraseRevisionInput.setAttribute("type", visible || (!visible && !passphraseRevisionFocused) ? "text" : "password"); this.passphraseRevisionInput.setAttribute('type', visible || (!visible && !passphraseRevisionFocused) ? 'text' : 'password');
} }
} }
@ -136,13 +135,13 @@ export default class PinModal extends Component<Props, State> {
if (input === 'passphrase') { if (input === 'passphrase') {
this.setState({ this.setState({
match: this.state.singleInput || this.state.passphraseRevision === value, match: this.state.singleInput || this.state.passphraseRevision === value,
passphrase: value passphrase: value,
}); });
} else { } else {
this.setState({ this.setState({
match: this.state.passphrase === value, match: this.state.passphrase === value,
passphraseRevision: value, passphraseRevision: value,
passphraseRevisionTouched: true passphraseRevisionTouched: true,
}); });
} }
} }
@ -150,11 +149,11 @@ export default class PinModal extends Component<Props, State> {
onPassphraseFocus = (input: string): void => { onPassphraseFocus = (input: string): void => {
if (input === 'passphrase') { if (input === 'passphrase') {
this.setState({ this.setState({
passphraseFocused: true passphraseFocused: true,
}); });
} else { } else {
this.setState({ this.setState({
passphraseRevisionFocused: true passphraseRevisionFocused: true,
}); });
} }
} }
@ -162,24 +161,24 @@ export default class PinModal extends Component<Props, State> {
onPassphraseBlur = (input: string): void => { onPassphraseBlur = (input: string): void => {
if (input === 'passphrase') { if (input === 'passphrase') {
this.setState({ this.setState({
passphraseFocused: false passphraseFocused: false,
}); });
} else { } else {
this.setState({ this.setState({
passphraseRevisionFocused: false passphraseRevisionFocused: false,
}); });
} }
} }
onPassphraseShow = (): void => { onPassphraseShow = (): void => {
this.setState({ this.setState({
visible: true visible: true,
}); });
} }
onPassphraseHide = (): void => { onPassphraseHide = (): void => {
this.setState({ this.setState({
visible: false visible: false,
}); });
} }
@ -203,14 +202,13 @@ export default class PinModal extends Component<Props, State> {
passphraseRevision: '', passphraseRevision: '',
passphraseFocused: false, passphraseFocused: false,
passphraseRevisionFocused: false, passphraseRevisionFocused: false,
visible: false visible: false,
}) });
raf(() => onPassphraseSubmit(empty ? '' : passphrase)); raf(() => onPassphraseSubmit(empty ? '' : passphrase));
} }
render() { render() {
if (!this.props.modal.opened) return null; if (!this.props.modal.opened) return null;
const { const {
@ -229,9 +227,9 @@ export default class PinModal extends Component<Props, State> {
passphraseRevisionTouched, passphraseRevisionTouched,
} = this.state; } = this.state;
let passphraseInputType: string = visible || (!visible && !passphraseFocused) ? "text" : "password"; let passphraseInputType: string = visible || (!visible && !passphraseFocused) ? 'text' : 'password';
let passphraseRevisionInputType: string = visible || (!visible && !passphraseRevisionFocused) ? "text" : "password"; let passphraseRevisionInputType: string = visible || (!visible && !passphraseRevisionFocused) ? 'text' : 'password';
passphraseInputType = passphraseRevisionInputType = "text"; passphraseInputType = passphraseRevisionInputType = 'text';
//let passphraseInputType: string = visible || passphraseFocused ? "text" : "password"; //let passphraseInputType: string = visible || passphraseFocused ? "text" : "password";
//let passphraseRevisionInputType: string = visible || passphraseRevisionFocused ? "text" : "password"; //let passphraseRevisionInputType: string = visible || passphraseRevisionFocused ? "text" : "password";
@ -257,7 +255,8 @@ export default class PinModal extends Component<Props, State> {
onFocus={event => this.onPassphraseFocus('passphrase')} onFocus={event => this.onPassphraseFocus('passphrase')}
onBlur={event => this.onPassphraseBlur('passphrase')} onBlur={event => this.onPassphraseBlur('passphrase')}
tabIndex="1" /> tabIndex="1"
/>
</div> </div>
{ singleInput ? null : ( { singleInput ? null : (
<div className="row"> <div className="row">
@ -274,7 +273,8 @@ export default class PinModal extends Component<Props, State> {
onFocus={event => this.onPassphraseFocus('revision')} onFocus={event => this.onPassphraseFocus('revision')}
onBlur={event => this.onPassphraseBlur('revision')} onBlur={event => this.onPassphraseBlur('revision')}
tabIndex="2" /> tabIndex="2"
/>
{ !match && passphraseRevisionTouched ? <span className="error">Passphrases do not match</span> : null } { !match && passphraseRevisionTouched ? <span className="error">Passphrases do not match</span> : null }
</div> </div>
) } ) }
@ -283,7 +283,7 @@ export default class PinModal extends Component<Props, State> {
<div className="row"> <div className="row">
<label className="custom-checkbox"> <label className="custom-checkbox">
<input type="checkbox" tabIndex="3" onChange={showPassphraseCheckboxFn} checked={visible} /> <input type="checkbox" tabIndex="3" onChange={showPassphraseCheckboxFn} checked={visible} />
<span className="indicator"></span> <span className="indicator" />
Show passphrase Show passphrase
</label> </label>
{/* <label className="custom-checkbox"> {/* <label className="custom-checkbox">

@ -1,11 +1,10 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import type { Props } from './index'; import type { Props } from './index';
const Confirmation = (props: Props) => { const Confirmation = (props: Props) => {
if (!props.modal.opened) return null; if (!props.modal.opened) return null;
const { device } = props.modal; const { device } = props.modal;
@ -16,6 +15,6 @@ const Confirmation = (props: Props) => {
</div> </div>
</div> </div>
); );
} };
export default Confirmation; export default Confirmation;

@ -1,16 +1,17 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import type { Props } from './index'; import type { Props } from './index';
type State = { type State = {
pin: string; pin: string;
} }
export default class Pin extends Component<Props, State> { export default class Pin extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void; keyboardHandler: (event: KeyboardEvent) => void;
state: State; state: State;
constructor(props: Props) { constructor(props: Props) {
@ -18,7 +19,7 @@ export default class Pin extends Component<Props, State> {
this.state = { this.state = {
pin: '', pin: '',
} };
} }
onPinAdd = (input: number): void => { onPinAdd = (input: number): void => {
@ -26,7 +27,7 @@ export default class Pin extends Component<Props, State> {
if (pin.length < 9) { if (pin.length < 9) {
pin += input; pin += input;
this.setState({ this.setState({
pin: pin pin,
}); });
} }
} }
@ -93,7 +94,6 @@ export default class Pin extends Component<Props, State> {
} }
componentWillMount(): void { componentWillMount(): void {
this.keyboardHandler = this.keyboardHandler.bind(this); this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false); window.addEventListener('keydown', this.keyboardHandler, false);
@ -104,7 +104,6 @@ export default class Pin extends Component<Props, State> {
} }
render() { render() {
if (!this.props.modal.opened) return null; if (!this.props.modal.opened) return null;
const { onPinSubmit } = this.props.modalActions; const { onPinSubmit } = this.props.modalActions;
@ -119,7 +118,7 @@ export default class Pin extends Component<Props, State> {
<div className="pin-input-row"> <div className="pin-input-row">
<input type="password" autoComplete="off" maxLength="9" disabled value={pin} /> <input type="password" autoComplete="off" maxLength="9" disabled value={pin} />
<button type="button" className="pin-backspace transparent" onClick={ event => this.onPinBackspace() }></button> <button type="button" className="pin-backspace transparent" onClick={event => this.onPinBackspace()} />
</div> </div>
<div className="pin-row"> <div className="pin-row">

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Loader from '../common/LoaderCircle'; import Loader from '../common/LoaderCircle';
@ -12,8 +12,8 @@ type State = {
} }
export default class RememberDevice extends Component<Props, State> { export default class RememberDevice extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void; keyboardHandler: (event: KeyboardEvent) => void;
state: State; state: State;
constructor(props: Props) { constructor(props: Props) {
@ -21,7 +21,7 @@ export default class RememberDevice extends Component<Props, State> {
this.state = { this.state = {
countdown: 10, countdown: 10,
} };
} }
keyboardHandler(event: KeyboardEvent): void { keyboardHandler(event: KeyboardEvent): void {
@ -32,7 +32,6 @@ export default class RememberDevice extends Component<Props, State> {
} }
componentDidMount(): void { componentDidMount(): void {
const ticker = () => { const ticker = () => {
if (this.state.countdown - 1 <= 0) { if (this.state.countdown - 1 <= 0) {
// TODO: possible race condition, // TODO: possible race condition,
@ -43,14 +42,14 @@ export default class RememberDevice extends Component<Props, State> {
} }
} else { } else {
this.setState({ this.setState({
countdown: this.state.countdown - 1 countdown: this.state.countdown - 1,
}); });
} }
} };
this.setState({ this.setState({
countdown: 10, countdown: 10,
ticker: window.setInterval(ticker, 1000) ticker: window.setInterval(ticker, 1000),
}); });
this.keyboardHandler = this.keyboardHandler.bind(this); this.keyboardHandler = this.keyboardHandler.bind(this);
@ -76,7 +75,7 @@ export default class RememberDevice extends Component<Props, State> {
const { onForgetDevice, onRememberDevice } = this.props.modalActions; const { onForgetDevice, onRememberDevice } = this.props.modalActions;
let label = device.label; let label = device.label;
const devicePlural: string = instances && instances.length > 1 ? "devices or to remember them" : "device or to remember it"; const devicePlural: string = instances && instances.length > 1 ? 'devices or to remember them' : 'device or to remember it';
if (instances && instances.length > 0) { if (instances && instances.length > 0) {
label = instances.map((instance, index) => { label = instances.map((instance, index) => {
let comma: string = ''; let comma: string = '';
@ -98,7 +97,6 @@ export default class RememberDevice extends Component<Props, State> {
} }
export class ForgetDevice extends Component<Props> { export class ForgetDevice extends Component<Props> {
keyboardHandler: (event: KeyboardEvent) => void; keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void { keyboardHandler(event: KeyboardEvent): void {

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -57,14 +57,14 @@ const Fade = ({ children, ...props }) => (
<CSSTransition <CSSTransition
{...props} {...props}
timeout={1000} timeout={1000}
classNames="fade"> classNames="fade"
>
{ children } { children }
</CSSTransition> </CSSTransition>
); );
class Modal extends Component<Props> { class Modal extends Component<Props> {
render() { render() {
if (!this.props.modal.opened) return null; if (!this.props.modal.opened) return null;
const { opened, windowType } = this.props.modal; const { opened, windowType } = this.props.modal;
@ -80,29 +80,29 @@ class Modal extends Component<Props> {
case UI.REQUEST_PASSPHRASE: case UI.REQUEST_PASSPHRASE:
component = (<Passphrase {...this.props} />); component = (<Passphrase {...this.props} />);
break; break;
case "ButtonRequest_SignTx" : case 'ButtonRequest_SignTx':
component = (<ConfirmSignTx { ...this.props } />) component = (<ConfirmSignTx {...this.props} />);
break; break;
// case "ButtonRequest_Address" : // case "ButtonRequest_Address" :
// component = (<ConfirmAddress { ...this.props } />) // component = (<ConfirmAddress { ...this.props } />)
// break; // break;
case "ButtonRequest_PassphraseType" : case 'ButtonRequest_PassphraseType':
component = (<PassphraseType { ...this.props } />) component = (<PassphraseType {...this.props} />);
break; break;
case RECEIVE.REQUEST_UNVERIFIED: case RECEIVE.REQUEST_UNVERIFIED:
component = (<ConfirmUnverifiedAddress { ...this.props } />) component = (<ConfirmUnverifiedAddress {...this.props} />);
break; break;
case CONNECT.REMEMBER_REQUEST: case CONNECT.REMEMBER_REQUEST:
component = (<RememberDevice { ...this.props } />) component = (<RememberDevice {...this.props} />);
break; break;
case CONNECT.FORGET_REQUEST: case CONNECT.FORGET_REQUEST:
component = (<ForgetDevice { ...this.props } />) component = (<ForgetDevice {...this.props} />);
break; break;
case CONNECT.TRY_TO_DUPLICATE: case CONNECT.TRY_TO_DUPLICATE:
component = (<DuplicateDevice { ...this.props } />) component = (<DuplicateDevice {...this.props} />);
break; break;
} }
@ -123,8 +123,7 @@ class Modal extends Component<Props> {
} }
} }
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => { const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
return {
modal: state.modal, modal: state.modal,
accounts: state.accounts, accounts: state.accounts,
devices: state.devices, devices: state.devices,
@ -133,18 +132,15 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
sendForm: state.sendForm, sendForm: state.sendForm,
receive: state.receive, receive: state.receive,
localStorage: state.localStorage, localStorage: state.localStorage,
wallet: state.wallet wallet: state.wallet,
}; });
}
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => { const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
return {
modalActions: bindActionCreators(ModalActions, dispatch), modalActions: bindActionCreators(ModalActions, dispatch),
receiveActions: bindActionCreators(ReceiveActions, dispatch), receiveActions: bindActionCreators(ReceiveActions, dispatch),
}; });
}
// export default connect(mapStateToProps, mapDispatchToProps)(Modal); // export default connect(mapStateToProps, mapDispatchToProps)(Modal);
export default withRouter( export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Modal) connect(mapStateToProps, mapDispatchToProps)(Modal),
); );

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
@ -16,8 +16,8 @@ type State = {
} }
class Indicator extends Component<Props, State> { class Indicator extends Component<Props, State> {
reposition: () => void; reposition: () => void;
state: State; state: State;
constructor(props: Props) { constructor(props: Props) {
@ -26,9 +26,9 @@ class Indicator extends Component<Props, State> {
this.state = { this.state = {
style: { style: {
width: 0, width: 0,
left: 0 left: 0,
}, },
} };
this.reposition = this.reposition.bind(this); this.reposition = this.reposition.bind(this);
} }
@ -63,9 +63,9 @@ class Indicator extends Component<Props, State> {
this.setState({ this.setState({
style: { style: {
width: bounds.width, width: bounds.width,
left: left, left,
} },
}) });
} }
} }
@ -77,7 +77,6 @@ class Indicator extends Component<Props, State> {
} }
const AccountTabs = (props: any) => { const AccountTabs = (props: any) => {
const urlParams = props.match.params; const urlParams = props.match.params;
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`; const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
@ -101,6 +100,6 @@ const AccountTabs = (props: any) => {
<Indicator pathname={props.match.pathname} /> <Indicator pathname={props.match.pathname} />
</div> </div>
); );
} };
export default AccountTabs; export default AccountTabs;

@ -1,10 +1,12 @@
/* @flow */ /* @flow */
'use strict';
import * as React from 'react'; import * as React from 'react';
import { Notification } from '~/js/components/common/Notification'; import { Notification } from '~/js/components/common/Notification';
import type { State, TrezorDevice, Action, ThunkAction } from '~/flowtype'; import type {
State, TrezorDevice, Action, ThunkAction,
} from '~/flowtype';
import type { Account } from '~/js/reducers/AccountsReducer'; import type { Account } from '~/js/reducers/AccountsReducer';
import type { Discovery } from '~/js/reducers/DiscoveryReducer'; import type { Discovery } from '~/js/reducers/DiscoveryReducer';
@ -31,7 +33,7 @@ const SelectedAccount = (props: Props) => {
const { const {
account, account,
discovery discovery,
} = accountState; } = accountState;
// account not found (yet). checking why... // account not found (yet). checking why...
@ -45,7 +47,7 @@ const SelectedAccount = (props: Props) => {
<Notification className="info" title="Loading accounts..." /> <Notification className="info" title="Loading accounts..." />
</section> </section>
); );
} else { }
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed // case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
return ( return (
<section> <section>
@ -57,7 +59,6 @@ const SelectedAccount = (props: Props) => {
</section> </section>
); );
} }
} else {
// case 3: device is disconnected // case 3: device is disconnected
return ( return (
<section> <section>
@ -68,22 +69,21 @@ const SelectedAccount = (props: Props) => {
/> />
</section> </section>
); );
} } if (discovery.waitingForBackend) {
} else if (discovery.waitingForBackend) {
// case 4: backend is not working // case 4: backend is not working
return ( return (
<section> <section>
<Notification className="warning" title="Backend not working" /> <Notification className="warning" title="Backend not working" />
</section> </section>
); );
} else if (discovery.completed) { } if (discovery.completed) {
// case 5: account not found and discovery is completed // case 5: account not found and discovery is completed
return ( return (
<section> <section>
<Notification className="warning" title="Account does not exist" /> <Notification className="warning" title="Account does not exist" />
</section> </section>
); );
} else { }
// case 6: discovery is not completed yet // case 6: discovery is not completed yet
return ( return (
<section> <section>
@ -91,7 +91,6 @@ const SelectedAccount = (props: Props) => {
</section> </section>
); );
} }
} else {
let notification: ?React$Element<typeof Notification> = null; let notification: ?React$Element<typeof Notification> = null;
if (!device.connected) { if (!device.connected) {
@ -109,9 +108,7 @@ const SelectedAccount = (props: Props) => {
{ notification } { notification }
{ props.children } { props.children }
</section> </section>
) );
} };
}
export default SelectedAccount; export default SelectedAccount;

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
@ -12,9 +12,7 @@ import { Notification } from '~/js/components/common/Notification';
import type { Props } from './index'; import type { Props } from './index';
const Receive = (props: Props) => { const Receive = (props: Props) => {
const device = props.wallet.selectedDevice; const device = props.wallet.selectedDevice;
const { const {
account, account,
@ -52,24 +50,24 @@ const Receive = (props: Props) => {
address = account.address; address = account.address;
className = addressUnverified ? 'address unverified' : 'address'; className = addressUnverified ? 'address unverified' : 'address';
const tooltip = addressUnverified ? const tooltip = addressUnverified
(<div>Unverified address.<br/>{ device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }</div>) ? (<div>Unverified address.<br />{ device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }</div>)
: : (<div>{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }</div>);
(<div>{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }</div>);
button = ( button = (
<Tooltip <Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>} arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={tooltip} overlay={tooltip}
placement="bottomRight"> placement="bottomRight"
>
<button className="white" onClick={event => props.showAddress(account.addressPath)}> <button className="white" onClick={event => props.showAddress(account.addressPath)}>
<span></span> <span />
</button> </button>
</Tooltip> </Tooltip>
); );
} }
let ver = null; const ver = null;
if (props.modal.opened && props.modal.windowType === 'ButtonRequest_Address') { if (props.modal.opened && props.modal.windowType === 'ButtonRequest_Address') {
className = 'address verifying'; className = 'address verifying';
address = account.address; address = account.address;
@ -91,12 +89,10 @@ const Receive = (props: Props) => {
{ qrCode } { qrCode }
</div> </div>
); );
} };
export default (props: Props) => { export default (props: Props) => (
return (
<SelectedAccount {...props}> <SelectedAccount {...props}>
<Receive {...props} /> <Receive {...props} />
</SelectedAccount> </SelectedAccount>
); );
}

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -7,13 +7,13 @@ import { connect } from 'react-redux';
import { default as ReceiveActions } from '~/js/actions/ReceiveActions'; import { default as ReceiveActions } from '~/js/actions/ReceiveActions';
import * as TokenActions from '~/js/actions/TokenActions'; import * as TokenActions from '~/js/actions/TokenActions';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import Receive from './Receive'; import Receive from './Receive';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from '~/flowtype'; import type { State, Dispatch } from '~/flowtype';
import type { import type {
StateProps as BaseStateProps, StateProps as BaseStateProps,
DispatchProps as BaseDispatchProps DispatchProps as BaseDispatchProps,
} from '../SelectedAccount'; } from '../SelectedAccount';
type OwnProps = { } type OwnProps = { }
@ -29,21 +29,17 @@ type DispatchProps = BaseDispatchProps & {
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps; export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => { const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
return { className: 'receive',
className: "receive",
selectedAccount: state.selectedAccount, selectedAccount: state.selectedAccount,
wallet: state.wallet, wallet: state.wallet,
receive: state.receive, receive: state.receive,
modal: state.modal, modal: state.modal,
}; });
}
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => { const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
return {
showAddress: bindActionCreators(ReceiveActions.showAddress, dispatch), showAddress: bindActionCreators(ReceiveActions.showAddress, dispatch),
}; });
}
export default connect(mapStateToProps, mapDispatchToProps)(Receive); export default connect(mapStateToProps, mapDispatchToProps)(Receive);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import Tooltip from 'rc-tooltip'; import Tooltip from 'rc-tooltip';
@ -13,10 +13,9 @@ type Props = {
}; };
const AdvancedForm = (props: Props) => { const AdvancedForm = (props: Props) => {
const { const {
account, account,
network network,
} = props.selectedAccount; } = props.selectedAccount;
const { const {
@ -31,7 +30,7 @@ const AdvancedForm = (props: Props) => {
errors, errors,
warnings, warnings,
infos, infos,
advanced advanced,
} = props.sendForm; } = props.sendForm;
const { const {
@ -39,15 +38,17 @@ const AdvancedForm = (props: Props) => {
onGasPriceChange, onGasPriceChange,
onGasLimitChange, onGasLimitChange,
onNonceChange, onNonceChange,
onDataChange onDataChange,
} = props.sendFormActions; } = props.sendFormActions;
if (!advanced) return ( if (!advanced) {
return (
<div className="advanced-container"> <div className="advanced-container">
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a> <a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
{ props.children } { props.children }
</div> </div>
); );
}
const nonceTooltip = ( const nonceTooltip = (
<div className="tooltip-wrapper"> <div className="tooltip-wrapper">
@ -90,7 +91,6 @@ const AdvancedForm = (props: Props) => {
); );
return ( return (
<div className="advanced-container opened"> <div className="advanced-container opened">
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a> <a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
@ -120,10 +120,11 @@ const AdvancedForm = (props: Props) => {
<label> <label>
Gas limit Gas limit
<Tooltip <Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>} arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={gasLimitTooltip} overlay={gasLimitTooltip}
placement="top"> placement="top"
<span className="what-is-it"></span> >
<span className="what-is-it" />
</Tooltip> </Tooltip>
</label> </label>
<input <input
@ -134,7 +135,8 @@ const AdvancedForm = (props: Props) => {
spellCheck="false" spellCheck="false"
value={gasLimit} value={gasLimit}
disabled={networkSymbol === currency && data.length > 0} disabled={networkSymbol === currency && data.length > 0}
onChange={ event => onGasLimitChange(event.target.value) } /> onChange={event => onGasLimitChange(event.target.value)}
/>
{ errors.gasLimit ? (<span className="error">{ errors.gasLimit }</span>) : null } { errors.gasLimit ? (<span className="error">{ errors.gasLimit }</span>) : null }
{ warnings.gasLimit ? (<span className="warning">{ warnings.gasLimit }</span>) : null } { warnings.gasLimit ? (<span className="warning">{ warnings.gasLimit }</span>) : null }
{ calculatingGasLimit ? (<span className="info">Calculating...</span>) : null } { calculatingGasLimit ? (<span className="info">Calculating...</span>) : null }
@ -143,10 +145,11 @@ const AdvancedForm = (props: Props) => {
<label> <label>
Gas price Gas price
<Tooltip <Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>} arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={gasPriceTooltip} overlay={gasPriceTooltip}
placement="top"> placement="top"
<span className="what-is-it"></span> >
<span className="what-is-it" />
</Tooltip> </Tooltip>
</label> </label>
<input <input
@ -156,7 +159,8 @@ const AdvancedForm = (props: Props) => {
autoCapitalize="off" autoCapitalize="off"
spellCheck="false" spellCheck="false"
value={gasPrice} value={gasPrice}
onChange={ event => onGasPriceChange(event.target.value) } /> onChange={event => onGasPriceChange(event.target.value)}
/>
{ errors.gasPrice ? (<span className="error">{ errors.gasPrice }</span>) : null } { errors.gasPrice ? (<span className="error">{ errors.gasPrice }</span>) : null }
</div> </div>
</div> </div>
@ -165,13 +169,14 @@ const AdvancedForm = (props: Props) => {
<label> <label>
Data Data
<Tooltip <Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>} arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={dataTooltip} overlay={dataTooltip}
placement="top"> placement="top"
<span className="what-is-it"></span> >
<span className="what-is-it" />
</Tooltip> </Tooltip>
</label> </label>
<textarea disabled={ networkSymbol !== currency } value={ networkSymbol !== currency ? '' : data } onChange={ event => onDataChange(event.target.value) }></textarea> <textarea disabled={networkSymbol !== currency} value={networkSymbol !== currency ? '' : data} onChange={event => onDataChange(event.target.value)} />
{ errors.data ? (<span className="error">{ errors.data }</span>) : null } { errors.data ? (<span className="error">{ errors.data }</span>) : null }
</div> </div>
@ -180,7 +185,7 @@ const AdvancedForm = (props: Props) => {
</div> </div>
</div> </div>
) );
} };
export default AdvancedForm; export default AdvancedForm;

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import * as React from 'react'; import * as React from 'react';
@ -37,11 +37,13 @@ export default class CoinSelectOption extends React.Component<Props> {
render() { render() {
const css = `${this.props.className} ${this.props.option.value}`; const css = `${this.props.className} ${this.props.option.value}`;
return ( return (
<div className={ css } <div
className={css}
onMouseDown={this.handleMouseDown.bind(this)} onMouseDown={this.handleMouseDown.bind(this)}
onMouseEnter={this.handleMouseEnter.bind(this)} onMouseEnter={this.handleMouseEnter.bind(this)}
onMouseMove={this.handleMouseMove.bind(this)} onMouseMove={this.handleMouseMove.bind(this)}
title={ this.props.option.label }> title={this.props.option.label}
>
<span>{ this.props.children }</span> <span>{ this.props.children }</span>
</div> </div>
); );

@ -1,12 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import * as React from 'react'; import * as React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export const FeeSelectValue = (props: any): any => { export const FeeSelectValue = (props: any): any => (
return (
<div> <div>
<div className="Select-value fee-option"> <div className="Select-value fee-option">
<span className="fee-value">{ props.value.value }</span> <span className="fee-value">{ props.value.value }</span>
@ -14,7 +13,6 @@ export const FeeSelectValue = (props: any): any => {
</div> </div>
</div> </div>
); );
}
type Props = { type Props = {
children: React.Node, children: React.Node,
@ -49,10 +47,12 @@ export class FeeSelectOption extends React.Component<Props> {
render() { render() {
return ( return (
<div className={ this.props.className } <div
className={this.props.className}
onMouseDown={this.handleMouseDown.bind(this)} onMouseDown={this.handleMouseDown.bind(this)}
onMouseEnter={this.handleMouseEnter.bind(this)} onMouseEnter={this.handleMouseEnter.bind(this)}
onMouseMove={ this.handleMouseMove.bind(this) }> onMouseMove={this.handleMouseMove.bind(this)}
>
<span className="fee-value">{ this.props.option.value }</span> <span className="fee-value">{ this.props.option.value }</span>
<span className="fee-label">{ this.props.option.label }</span> <span className="fee-label">{ this.props.option.label }</span>
</div> </div>

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import ColorHash from 'color-hash'; import ColorHash from 'color-hash';
@ -24,7 +24,6 @@ type Style = {
} }
const PendingTransactions = (props: Props) => { const PendingTransactions = (props: Props) => {
const pending = props.pending.filter(tx => !tx.rejected); const pending = props.pending.filter(tx => !tx.rejected);
if (pending.length < 1) return null; if (pending.length < 1) return null;
@ -34,7 +33,6 @@ const PendingTransactions = (props: Props) => {
const textColorFactory = new ColorHash(); const textColorFactory = new ColorHash();
const pendingTxs: React$Element<string> = pending.map((tx, i) => { const pendingTxs: React$Element<string> = pending.map((tx, i) => {
let iconColor: Style; let iconColor: Style;
let symbol: string; let symbol: string;
let name: string; let name: string;
@ -45,17 +43,17 @@ const PendingTransactions = (props: Props) => {
iconColor = { iconColor = {
color: '#ffffff', color: '#ffffff',
background: '#000000', background: '#000000',
borderColor: '#000000' borderColor: '#000000',
} };
symbol = "Unknown"; symbol = 'Unknown';
name = "Unknown"; name = 'Unknown';
} else { } else {
const bgColor: string = bgColorFactory.hex(token.name); const bgColor: string = bgColorFactory.hex(token.name);
iconColor = { iconColor = {
color: textColorFactory.hex(token.name), color: textColorFactory.hex(token.name),
background: bgColor, background: bgColor,
borderColor: bgColor borderColor: bgColor,
} };
symbol = token.symbol.toUpperCase(); symbol = token.symbol.toUpperCase();
name = token.name; name = token.name;
} }
@ -63,8 +61,8 @@ const PendingTransactions = (props: Props) => {
iconColor = { iconColor = {
color: textColorFactory.hex(tx.network), color: textColorFactory.hex(tx.network),
background: bgColorFactory.hex(tx.network), background: bgColorFactory.hex(tx.network),
borderColor: bgColorFactory.hex(tx.network) borderColor: bgColorFactory.hex(tx.network),
} };
symbol = props.network.symbol; symbol = props.network.symbol;
name = props.network.name; name = props.network.name;
} }
@ -81,7 +79,7 @@ const PendingTransactions = (props: Props) => {
</div> </div>
<div className="amount">{ isSmartContractTx ? tx.amount : tx.total } { symbol }</div> <div className="amount">{ isSmartContractTx ? tx.amount : tx.total } { symbol }</div>
</div> </div>
) );
}); });
@ -90,7 +88,7 @@ const PendingTransactions = (props: Props) => {
<h2>Pending transactions</h2> <h2>Pending transactions</h2>
{ pendingTxs } { pendingTxs }
</div> </div>
) );
} };
export default PendingTransactions; export default PendingTransactions;

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Select from 'react-select'; import Select from 'react-select';
@ -17,7 +17,6 @@ import type { Token } from '~/flowtype';
export default class SendContainer extends Component<Props> { export default class SendContainer extends Component<Props> {
componentWillReceiveProps(newProps: Props) { componentWillReceiveProps(newProps: Props) {
calculate(this.props, newProps); calculate(this.props, newProps);
validation(newProps); validation(newProps);
@ -36,13 +35,12 @@ export default class SendContainer extends Component<Props> {
const Send = (props: Props) => { const Send = (props: Props) => {
const device = props.wallet.selectedDevice; const device = props.wallet.selectedDevice;
const { const {
account, account,
network, network,
discovery, discovery,
tokens tokens,
} = props.selectedAccount; } = props.selectedAccount;
if (!device || !account || !discovery || !network) return null; if (!device || !account || !discovery || !network) return null;
@ -77,9 +75,7 @@ const Send = (props: Props) => {
const fiatRate = props.fiat.find(f => f.network === network); const fiatRate = props.fiat.find(f => f.network === network);
const tokensSelectData = tokens.map(t => { const tokensSelectData = tokens.map(t => ({ value: t.symbol, label: t.symbol }));
return { value: t.symbol, label: t.symbol };
});
tokensSelectData.unshift({ value: network.symbol, label: network.symbol }); tokensSelectData.unshift({ value: network.symbol, label: network.symbol });
const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max'; const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max';
@ -88,7 +84,7 @@ const Send = (props: Props) => {
if (gasPriceNeedsUpdate) { if (gasPriceNeedsUpdate) {
updateFeeLevelsButton = ( updateFeeLevelsButton = (
<span className="update-fee-levels">Recommended fees updated. <a onClick={updateFeeLevels}>Click here to use them</a></span> <span className="update-fee-levels">Recommended fees updated. <a onClick={updateFeeLevels}>Click here to use them</a></span>
) );
} }
let addressClassName: ?string; let addressClassName: ?string;
@ -103,7 +99,7 @@ const Send = (props: Props) => {
let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending; let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
let buttonLabel: string = 'Send'; let buttonLabel: string = 'Send';
if (networkSymbol !== currency && amount.length > 0 && !errors.amount) { if (networkSymbol !== currency && amount.length > 0 && !errors.amount) {
buttonLabel += ` ${amount} ${ currency.toUpperCase() }` buttonLabel += ` ${amount} ${currency.toUpperCase()}`;
} else if (networkSymbol === currency && total !== '0') { } else if (networkSymbol === currency && total !== '0') {
buttonLabel += ` ${total} ${network.symbol}`; buttonLabel += ` ${total} ${network.symbol}`;
} }
@ -132,8 +128,9 @@ const Send = (props: Props) => {
spellCheck="false" spellCheck="false"
value={address} value={address}
className={addressClassName} className={addressClassName}
onChange={ event => onAddressChange(event.target.value) } /> onChange={event => onAddressChange(event.target.value)}
<span className="input-icon"></span> />
<span className="input-icon" />
{ errors.address ? (<span className="error">{ errors.address }</span>) : null } { errors.address ? (<span className="error">{ errors.address }</span>) : null }
{ warnings.address ? (<span className="warning">{ warnings.address }</span>) : null } { warnings.address ? (<span className="warning">{ warnings.address }</span>) : null }
{ infos.address ? (<span className="info">{ infos.address }</span>) : null } { infos.address ? (<span className="info">{ infos.address }</span>) : null }
@ -150,7 +147,8 @@ const Send = (props: Props) => {
spellCheck="false" spellCheck="false"
value={amount} value={amount}
className={errors.amount ? 'not-valid' : null} className={errors.amount ? 'not-valid' : null}
onChange={ event => onAmountChange(event.target.value) } /> onChange={event => onAmountChange(event.target.value)}
/>
<a className={setMaxClassName} onClick={onSetMax}>Set max</a> <a className={setMaxClassName} onClick={onSetMax}>Set max</a>
@ -163,7 +161,8 @@ const Send = (props: Props) => {
value={currency} value={currency}
disabled={tokensSelectData.length < 2} disabled={tokensSelectData.length < 2}
onChange={onCurrencyChange} onChange={onCurrencyChange}
options={ tokensSelectData } /> options={tokensSelectData}
/>
</div> </div>
{ errors.amount ? (<span className="error">{ errors.amount }</span>) : null } { errors.amount ? (<span className="error">{ errors.amount }</span>) : null }
{ warnings.amount ? (<span className="warning">{ warnings.amount }</span>) : null } { warnings.amount ? (<span className="warning">{ warnings.amount }</span>) : null }
@ -182,21 +181,24 @@ const Send = (props: Props) => {
optionComponent={FeeSelectOption} optionComponent={FeeSelectOption}
disabled={networkSymbol === currency && data.length > 0} disabled={networkSymbol === currency && data.length > 0}
optionClassName="fee-option" optionClassName="fee-option"
options={ feeLevels } /> options={feeLevels}
/>
</div> </div>
<AdvancedForm <AdvancedForm
selectedAccount={props.selectedAccount} selectedAccount={props.selectedAccount}
sendForm={props.sendForm} sendForm={props.sendForm}
sendFormActions={ props.sendFormActions }> sendFormActions={props.sendFormActions}
>
<button disabled={buttonDisabled} onClick={event => onSend()}>{ buttonLabel }</button> <button disabled={buttonDisabled} onClick={event => onSend()}>{ buttonLabel }</button>
</AdvancedForm> </AdvancedForm>
<PendingTransactions <PendingTransactions
pending={props.selectedAccount.pending} pending={props.selectedAccount.pending}
tokens={props.selectedAccount.tokens} tokens={props.selectedAccount.tokens}
network={ network } /> network={network}
/>
</section> </section>
); );
} };

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import * as React from 'react'; import * as React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -7,9 +7,9 @@ import { connect } from 'react-redux';
import { default as SendFormActions } from '~/js/actions/SendFormActions'; import { default as SendFormActions } from '~/js/actions/SendFormActions';
import * as SessionStorageActions from '~/js/actions/SessionStorageActions'; import * as SessionStorageActions from '~/js/actions/SessionStorageActions';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import SendForm from './SendForm'; import SendForm from './SendForm';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from '~/flowtype'; import type { State, Dispatch } from '~/flowtype';
import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount'; import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount';
@ -29,23 +29,19 @@ export type DispatchProps = BaseDispatchProps & {
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps; export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => { const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
return { className: 'send-from',
className: "send-from",
selectedAccount: state.selectedAccount, selectedAccount: state.selectedAccount,
wallet: state.wallet, wallet: state.wallet,
sendForm: state.sendForm, sendForm: state.sendForm,
fiat: state.fiat, fiat: state.fiat,
localStorage: state.localStorage localStorage: state.localStorage,
}; });
}
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => { const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
return {
sendFormActions: bindActionCreators(SendFormActions, dispatch), sendFormActions: bindActionCreators(SendFormActions, dispatch),
saveSessionStorage: bindActionCreators(SessionStorageActions.save, dispatch), saveSessionStorage: bindActionCreators(SessionStorageActions.save, dispatch),
}; });
}
export default connect(mapStateToProps, mapDispatchToProps)(SendForm) export default connect(mapStateToProps, mapDispatchToProps)(SendForm);

@ -1,33 +1,31 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
const SignVerify = () => { const SignVerify = () => (
return (
<section className="signverify"> <section className="signverify">
<div className="sign"> <div className="sign">
<h2>Sign message</h2> <h2>Sign message</h2>
<label>Message</label> <label>Message</label>
<textarea rows="4" maxLength="255"></textarea> <textarea rows="4" maxLength="255" />
<label>Address</label> <label>Address</label>
<input type="text" /> <input type="text" />
<label>Signature</label> <label>Signature</label>
<textarea rows="4" maxLength="255" readOnly="readonly"></textarea> <textarea rows="4" maxLength="255" readOnly="readonly" />
</div> </div>
<div className="verify"> <div className="verify">
<h2>Verify message</h2> <h2>Verify message</h2>
<label>Message</label> <label>Message</label>
<textarea rows="4" maxLength="255"></textarea> <textarea rows="4" maxLength="255" />
<label>Address</label> <label>Address</label>
<input type="text" /> <input type="text" />
<label>Signature</label> <label>Signature</label>
<textarea rows="4" maxLength="255"></textarea> <textarea rows="4" maxLength="255" />
</div> </div>
</section> </section>
); );
}
export default connect(null, null)(SignVerify); export default connect(null, null)(SignVerify);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
@ -22,7 +22,7 @@ const Summary = (props: Props) => {
account, account,
network, network,
tokens, tokens,
pending pending,
} = props.selectedAccount; } = props.selectedAccount;
// flow // flow
@ -52,15 +52,17 @@ const Summary = (props: Props) => {
network={network.network} network={network.network}
fiat={props.fiat} fiat={props.fiat}
localStorage={props.localStorage} localStorage={props.localStorage}
onToggle={ props.onDetailsToggle } /> onToggle={props.onDetailsToggle}
/>
<h2> <h2>
Tokens Tokens
<Tooltip <Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>} arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={tokensTooltip} overlay={tokensTooltip}
placement="top"> placement="top"
<span className="what-is-it"></span> >
<span className="what-is-it" />
</Tooltip> </Tooltip>
</h2> </h2>
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 Lahod */} {/* 0x58cda554935e4a1f2acbe15f8757400af275e084 Lahod */}
@ -70,49 +72,45 @@ const Summary = (props: Props) => {
className="token-select" className="token-select"
multi={false} multi={false}
autoload={false} autoload={false}
ignoreCase={ true } ignoreCase
backspaceRemoves={ true } backspaceRemoves
value={null} value={null}
onChange={token => props.addToken(token, account)} onChange={token => props.addToken(token, account)}
loadOptions={input => props.loadTokens(input, account.network)} loadOptions={input => props.loadTokens(input, account.network)}
filterOptions={ filterOptions={
(options: Array<NetworkToken>, search: string, values: Array<NetworkToken>) => { (options: Array<NetworkToken>, search: string, values: Array<NetworkToken>) => options.map((o) => {
return options.map(o => {
const added = tokens.find(t => t.symbol === o.symbol); const added = tokens.find(t => t.symbol === o.symbol);
if (added) { if (added) {
return { return {
...o, ...o,
name: `${o.name} (Already added)`, name: `${o.name} (Already added)`,
disabled: true disabled: true,
}; };
} else {
return o;
}
});
} }
return o;
})
} }
valueKey="symbol" valueKey="symbol"
labelKey="name" labelKey="name"
placeholder="Search for token" placeholder="Search for token"
searchPromptText="Type token name or address" searchPromptText="Type token name or address"
noResultsText="Token not found" /> noResultsText="Token not found"
/>
</div> </div>
<SummaryTokens <SummaryTokens
pending={pending} pending={pending}
tokens={tokens} tokens={tokens}
removeToken={ props.removeToken } /> removeToken={props.removeToken}
/>
</div> </div>
) );
};
}
export default (props: Props) => { export default (props: Props) => (
return (
<SelectedAccount {...props}> <SelectedAccount {...props}>
<Summary {...props} /> <Summary {...props} />
</SelectedAccount> </SelectedAccount>
); );
}

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
@ -18,12 +18,13 @@ type Props = {
} }
const SummaryDetails = (props: Props): ?React$Element<string> => { const SummaryDetails = (props: Props): ?React$Element<string> => {
if (!props.summary.details) {
if (!props.summary.details) return ( return (
<div className="summary-details"> <div className="summary-details">
<div className="toggle" onClick={ props.onToggle }></div> <div className="toggle" onClick={props.onToggle} />
</div> </div>
); );
}
const selectedCoin = props.coin; const selectedCoin = props.coin;
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network); const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
@ -32,7 +33,6 @@ const SummaryDetails = (props: Props): ?React$Element<string> => {
let rateColumn = null; let rateColumn = null;
if (fiatRate) { if (fiatRate) {
const accountBalance = new BigNumber(props.balance); const accountBalance = new BigNumber(props.balance);
const fiatValue = new BigNumber(fiatRate.value); const fiatValue = new BigNumber(fiatRate.value);
const fiat = accountBalance.times(fiatValue).toFixed(2); const fiat = accountBalance.times(fiatValue).toFixed(2);
@ -51,7 +51,7 @@ const SummaryDetails = (props: Props): ?React$Element<string> => {
<div className="fiat-value">${ fiatValue.toFixed(2) }</div> <div className="fiat-value">${ fiatValue.toFixed(2) }</div>
<div className="value">1.00 { selectedCoin.symbol }</div> <div className="value">1.00 { selectedCoin.symbol }</div>
</div> </div>
) );
} else { } else {
balanceColumn = ( balanceColumn = (
<div className="column"> <div className="column">
@ -63,13 +63,13 @@ const SummaryDetails = (props: Props): ?React$Element<string> => {
return ( return (
<div className="summary-details opened"> <div className="summary-details opened">
<div className="toggle" onClick={ props.onToggle }></div> <div className="toggle" onClick={props.onToggle} />
<div className="content"> <div className="content">
{ balanceColumn } { balanceColumn }
{ rateColumn } { rateColumn }
</div> </div>
</div> </div>
); );
} };
export default SummaryDetails; export default SummaryDetails;

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import ColorHash from 'color-hash'; import ColorHash from 'color-hash';
@ -16,18 +16,17 @@ type Props = {
} }
const SummaryTokens = (props: Props) => { const SummaryTokens = (props: Props) => {
if (!props.tokens || props.tokens.length < 1) return null; if (!props.tokens || props.tokens.length < 1) return null;
const bgColor = new ColorHash({ lightness: 0.7 }); const bgColor = new ColorHash({ lightness: 0.7 });
const textColor = new ColorHash(); const textColor = new ColorHash();
const tokens = props.tokens.map((token, index) => { const tokens = props.tokens.map((token, index) => {
let iconColor = { const iconColor = {
color: textColor.hex(token.name), color: textColor.hex(token.name),
background: bgColor.hex(token.name), background: bgColor.hex(token.name),
borderColor: bgColor.hex(token.name) borderColor: bgColor.hex(token.name),
} };
const pendingAmount: BigNumber = stateUtils.getPendingAmount(props.pending, token.symbol, true); const pendingAmount: BigNumber = stateUtils.getPendingAmount(props.pending, token.symbol, true);
const balance: string = new BigNumber(token.balance).minus(pendingAmount).toString(10); const balance: string = new BigNumber(token.balance).minus(pendingAmount).toString(10);
@ -40,16 +39,16 @@ const SummaryTokens = (props: Props) => {
</div> </div>
<div className="name">{ token.name }</div> <div className="name">{ token.name }</div>
<div className="balance">{ balance } { token.symbol }</div> <div className="balance">{ balance } { token.symbol }</div>
<button className="transparent" onClick={ event => props.removeToken(token) }></button> <button className="transparent" onClick={event => props.removeToken(token)} />
</div> </div>
) );
}); });
return ( return (
<div> <div>
{ tokens } { tokens }
</div> </div>
) );
} };
export default SummaryTokens; export default SummaryTokens;

@ -1,15 +1,15 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import Summary from './Summary'; import Summary from './Summary';
import * as SummaryActions from '~/js/actions/SummaryActions'; import * as SummaryActions from '~/js/actions/SummaryActions';
import * as TokenActions from '~/js/actions/TokenActions'; import * as TokenActions from '~/js/actions/TokenActions';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from '~/flowtype'; import type { State, Dispatch } from '~/flowtype';
import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount'; import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount';
@ -31,9 +31,8 @@ type DispatchProps = BaseDispatchProps & {
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps; export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => { const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
return { className: 'summary',
className: "summary",
selectedAccount: state.selectedAccount, selectedAccount: state.selectedAccount,
wallet: state.wallet, wallet: state.wallet,
@ -41,16 +40,13 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
summary: state.summary, summary: state.summary,
fiat: state.fiat, fiat: state.fiat,
localStorage: state.localStorage, localStorage: state.localStorage,
}; });
}
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => { const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
return {
onDetailsToggle: bindActionCreators(SummaryActions.onDetailsToggle, dispatch), onDetailsToggle: bindActionCreators(SummaryActions.onDetailsToggle, dispatch),
addToken: bindActionCreators(TokenActions.add, dispatch), addToken: bindActionCreators(TokenActions.add, dispatch),
loadTokens: bindActionCreators(TokenActions.load, dispatch), loadTokens: bindActionCreators(TokenActions.load, dispatch),
removeToken: bindActionCreators(TokenActions.remove, dispatch), removeToken: bindActionCreators(TokenActions.remove, dispatch),
}; });
}
export default connect(mapStateToProps, mapDispatchToProps)(Summary) export default connect(mapStateToProps, mapDispatchToProps)(Summary);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Link, NavLink } from 'react-router-dom'; import { Link, NavLink } from 'react-router-dom';
@ -14,7 +14,6 @@ import type { Props } from './index';
import type { TrezorDevice, Accounts } from '~/flowtype'; import type { TrezorDevice, Accounts } from '~/flowtype';
const AccountSelection = (props: Props): ?React$Element<string> => { const AccountSelection = (props: Props): ?React$Element<string> => {
const selected = props.wallet.selectedDevice; const selected = props.wallet.selectedDevice;
if (!selected) return null; if (!selected) return null;
@ -53,20 +52,20 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
return ( return (
<NavLink key={i} activeClassName="selected" className="account" to={url}> <NavLink key={i} activeClassName="selected" className="account" to={url}>
{ `Account #${(account.index + 1)}` } { `Account #${(account.index + 1)}` }
<span>{ account.loaded ? balance : "Loading..." }</span> <span>{ account.loaded ? balance : 'Loading...' }</span>
</NavLink> </NavLink>
) );
}); });
if (selectedAccounts.length < 1) { if (selectedAccounts.length < 1) {
if (selected.connected) { if (selected.connected) {
const url: string = location.pathname.replace(/account+\/([0-9]*)/, `account/0`); const url: string = location.pathname.replace(/account+\/([0-9]*)/, 'account/0');
selectedAccounts = ( selectedAccounts = (
<NavLink activeClassName="selected" className="account" to={url}> <NavLink activeClassName="selected" className="account" to={url}>
Account #1 Account #1
<span>Loading...</span> <span>Loading...</span>
</NavLink> </NavLink>
) );
} }
} }
@ -89,19 +88,19 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
<div className="aside-tooltip-wrapper"> <div className="aside-tooltip-wrapper">
To add a new account, last account must have some transactions. To add a new account, last account must have some transactions.
</div> </div>
) );
discoveryStatus = ( discoveryStatus = (
<Tooltip <Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>} arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={tooltip} overlay={tooltip}
placement="top"> placement="top"
>
<div className="add-account disabled"> <div className="add-account disabled">
Add account Add account
</div> </div>
</Tooltip> </Tooltip>
); );
} }
} else if (!selected.connected || !selected.available) { } else if (!selected.connected || !selected.available) {
discoveryStatus = ( discoveryStatus = (
<div className="discovery-status"> <div className="discovery-status">
@ -137,6 +136,6 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
{ discoveryStatus } { discoveryStatus }
</section> </section>
); );
} };
export default AccountSelection; export default AccountSelection;

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
//import React, { Node } from 'react'; //import React, { Node } from 'react';
import * as React from 'react'; import * as React from 'react';
@ -20,44 +20,42 @@ type TransitionMenuProps = {
children?: React.Node; children?: React.Node;
} }
const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGroup> => { const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGroup> => (
return (
<TransitionGroup component="div" className="transition-container"> <TransitionGroup component="div" className="transition-container">
<CSSTransition <CSSTransition
key={props.animationType} key={props.animationType}
onExit= { () => { window.dispatchEvent( new Event('resize') ) } } onExit={() => { window.dispatchEvent(new Event('resize')); }}
onExited={() => window.dispatchEvent(new Event('resize'))} onExited={() => window.dispatchEvent(new Event('resize'))}
in={ true } in
out={ true } out
classNames={props.animationType} classNames={props.animationType}
appear={false} appear={false}
timeout={ 300 }> timeout={300}
>
{ props.children } { props.children }
</CSSTransition> </CSSTransition>
</TransitionGroup> </TransitionGroup>
) );
}
const Aside = (props: Props): React$Element<typeof StickyContainer | string> => { const Aside = (props: Props): React$Element<typeof StickyContainer | string> => {
const selected: ?TrezorDevice = props.wallet.selectedDevice; const selected: ?TrezorDevice = props.wallet.selectedDevice;
const { location } = props.router; const { location } = props.router;
if (location.pathname === '/' || !selected) return (<aside></aside>); if (location.pathname === '/' || !selected) return (<aside />);
let menu = <section></section>; let menu = <section />;
if (props.deviceDropdownOpened) { if (props.deviceDropdownOpened) {
menu = <DeviceDropdown {...props} />; menu = <DeviceDropdown {...props} />;
} else if (location.state.network) { } else if (location.state.network) {
menu = ( menu = (
<TransitionMenu animationType={ "slide-left" }> <TransitionMenu animationType="slide-left">
<AccountSelection {...props} /> <AccountSelection {...props} />
</TransitionMenu> </TransitionMenu>
); );
} else if (selected.features && !selected.features.bootloader_mode && selected.features.initialized) { } else if (selected.features && !selected.features.bootloader_mode && selected.features.initialized) {
menu = ( menu = (
<TransitionMenu animationType={ "slide-right" }> <TransitionMenu animationType="slide-right">
<CoinSelection {...props} /> <CoinSelection {...props} />
</TransitionMenu> </TransitionMenu>
); );
@ -73,7 +71,7 @@ const Aside = (props: Props): React$Element<typeof StickyContainer | string> =>
</div> </div>
</div> </div>
</StickyContainer> </StickyContainer>
) );
} };
export default Aside; export default Aside;

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { Link, NavLink } from 'react-router-dom'; import { Link, NavLink } from 'react-router-dom';
@ -20,15 +20,15 @@ const CoinSelection = (props: Props): React$Element<string> => {
} }
} }
const walletCoins = config.coins.map(item => { const walletCoins = config.coins.map((item) => {
const url = `${baseUrl}/network/${item.network}/account/0`; const url = `${baseUrl}/network/${item.network}/account/0`;
const className = `coin ${ item.network }` const className = `coin ${item.network}`;
return ( return (
<NavLink key={item.network} to={url} className={className}> <NavLink key={item.network} to={url} className={className}>
{ item.name } { item.name }
</NavLink> </NavLink>
) );
}) });
return ( return (
<section> <section>
@ -56,6 +56,6 @@ const CoinSelection = (props: Props): React$Element<string> => {
</a> </a>
</section> </section>
); );
} };
export default CoinSelection; export default CoinSelection;

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Select from 'react-select'; import Select from 'react-select';
@ -9,49 +9,48 @@ import type { Props } from './index';
import type { TrezorDevice } from '~/flowtype'; import type { TrezorDevice } from '~/flowtype';
export const DeviceSelect = (props: Props) => { export const DeviceSelect = (props: Props) => {
const { devices } = props; const { devices } = props;
const { transport } = props.connect; const { transport } = props.connect;
const selected: ?TrezorDevice = props.wallet.selectedDevice; const selected: ?TrezorDevice = props.wallet.selectedDevice;
if (!selected) return null; if (!selected) return null;
let deviceStatus: string = "Connected"; let deviceStatus: string = 'Connected';
let css: string = "device-select device"; let css: string = 'device-select device';
if (props.deviceDropdownOpened) css += " opened"; if (props.deviceDropdownOpened) css += ' opened';
if (!selected.connected) { if (!selected.connected) {
css += " disconnected"; css += ' disconnected';
deviceStatus = "Disconnected"; deviceStatus = 'Disconnected';
} else if (!selected.available) { } else if (!selected.available) {
css += " unavailable"; css += ' unavailable';
deviceStatus = "Unavailable"; deviceStatus = 'Unavailable';
} else { } else {
if (selected.unacquired) { if (selected.unacquired) {
css += " unacquired"; css += ' unacquired';
deviceStatus = "Used in other window"; deviceStatus = 'Used in other window';
} }
if (selected.isUsedElsewhere) { if (selected.isUsedElsewhere) {
css += " used-elsewhere"; css += ' used-elsewhere';
deviceStatus = "Used in other window"; deviceStatus = 'Used in other window';
} else if (selected.featuresNeedsReload) { } else if (selected.featuresNeedsReload) {
css += " reload-features"; css += ' reload-features';
} }
} }
if (selected.features && selected.features.major_version > 1) { if (selected.features && selected.features.major_version > 1) {
css += " trezor-t"; css += ' trezor-t';
} }
const handleOpen = () => { const handleOpen = () => {
props.toggleDeviceDropdown(props.deviceDropdownOpened ? false : true); props.toggleDeviceDropdown(!props.deviceDropdownOpened);
} };
const deviceCount = devices.length; const deviceCount = devices.length;
const webusb: boolean = (transport && transport.version.indexOf('webusb') >= 0) ? true : false; const webusb: boolean = !!((transport && transport.version.indexOf('webusb') >= 0));
const disabled: boolean = (devices.length < 1 && !webusb) || (devices.length === 1 && !selected.features); const disabled: boolean = (devices.length < 1 && !webusb) || (devices.length === 1 && !selected.features);
if (disabled) { if (disabled) {
css += " disabled"; css += ' disabled';
} }
return ( return (
@ -61,10 +60,10 @@ export const DeviceSelect = (props: Props) => {
<span className="status">{ deviceStatus }</span> <span className="status">{ deviceStatus }</span>
</div> </div>
{ deviceCount > 1 ? <div className="counter">{ deviceCount }</div> : null } { deviceCount > 1 ? <div className="counter">{ deviceCount }</div> : null }
<div className="arrow"></div> <div className="arrow" />
</div> </div>
); );
} };
type DeviceMenuItem = { type DeviceMenuItem = {
type: string; type: string;
@ -72,8 +71,8 @@ type DeviceMenuItem = {
} }
export class DeviceDropdown extends Component<Props> { export class DeviceDropdown extends Component<Props> {
mouseDownHandler: (event: MouseEvent) => void; mouseDownHandler: (event: MouseEvent) => void;
blurHandler: (event: FocusEvent) => void; blurHandler: (event: FocusEvent) => void;
constructor(props: Props) { constructor(props: Props) {
@ -84,8 +83,7 @@ export class DeviceDropdown extends Component<Props> {
componentDidUpdate() { componentDidUpdate() {
const { transport } = this.props.connect; const { transport } = this.props.connect;
if (transport && transport.version.indexOf('webusb') >= 0) if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
TrezorConnect.renderWebUSBButton();
} }
mouseDownHandler(event: MouseEvent): void { mouseDownHandler(event: MouseEvent): void {
@ -113,8 +111,7 @@ export class DeviceDropdown extends Component<Props> {
window.addEventListener('mousedown', this.mouseDownHandler, false); window.addEventListener('mousedown', this.mouseDownHandler, false);
// window.addEventListener('blur', this.blurHandler, false); // window.addEventListener('blur', this.blurHandler, false);
const { transport } = this.props.connect; const { transport } = this.props.connect;
if (transport && transport.version.indexOf('webusb') >= 0) if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
TrezorConnect.renderWebUSBButton();
} }
componentWillUnmount(): void { componentWillUnmount(): void {
@ -136,7 +133,6 @@ export class DeviceDropdown extends Component<Props> {
} }
render() { render() {
const { devices } = this.props; const { devices } = this.props;
const { transport } = this.props.connect; const { transport } = this.props.connect;
const selected: ?TrezorDevice = this.props.wallet.selectedDevice; const selected: ?TrezorDevice = this.props.wallet.selectedDevice;
@ -152,25 +148,23 @@ export class DeviceDropdown extends Component<Props> {
const deviceMenuItems: Array<DeviceMenuItem> = []; const deviceMenuItems: Array<DeviceMenuItem> = [];
if (selected.isUsedElsewhere) { if (selected.isUsedElsewhere) {
deviceMenuItems.push({ type: "reload", label: "Renew session" }); deviceMenuItems.push({ type: 'reload', label: 'Renew session' });
} else if (selected.featuresNeedsReload) { } else if (selected.featuresNeedsReload) {
deviceMenuItems.push({ type: "reload", label: "Renew session" }); deviceMenuItems.push({ type: 'reload', label: 'Renew session' });
} }
deviceMenuItems.push({ type: "settings", label: "Device settings" }); deviceMenuItems.push({ type: 'settings', label: 'Device settings' });
if (selected.features && selected.features.passphrase_protection && selected.connected && selected.available) { if (selected.features && selected.features.passphrase_protection && selected.connected && selected.available) {
deviceMenuItems.push({ type: "clone", label: "Clone device" }); deviceMenuItems.push({ type: 'clone', label: 'Clone device' });
} }
//if (selected.remember) { //if (selected.remember) {
deviceMenuItems.push({ type: "forget", label: "Forget device" }); deviceMenuItems.push({ type: 'forget', label: 'Forget device' });
//} //}
const deviceMenuButtons = deviceMenuItems.map((item, index) => { const deviceMenuButtons = deviceMenuItems.map((item, index) => (
return ( <div key={item.type} className={item.type} onClick={event => this.onDeviceMenuClick(item, selected)}>{ item.label}</div>
<div key={ item.type } className={ item.type } onClick={ (event) => this.onDeviceMenuClick(item, selected) }>{ item.label}</div> ));
)
});
currentDeviceMenu = deviceMenuButtons.length < 1 ? null : ( currentDeviceMenu = deviceMenuButtons.length < 1 ? null : (
<div className="device-menu"> <div className="device-menu">
{ deviceMenuButtons } { deviceMenuButtons }
@ -181,21 +175,21 @@ export class DeviceDropdown extends Component<Props> {
const deviceList = devices.map((dev, index) => { const deviceList = devices.map((dev, index) => {
if (dev === selected) return null; if (dev === selected) return null;
let deviceStatus: string = "Connected"; let deviceStatus: string = 'Connected';
let css: string = "device item" let css: string = 'device item';
if (dev.unacquired || dev.isUsedElsewhere) { if (dev.unacquired || dev.isUsedElsewhere) {
deviceStatus = "Used in other window"; deviceStatus = 'Used in other window';
css += " unacquired"; css += ' unacquired';
} else if (!dev.connected) { } else if (!dev.connected) {
deviceStatus = "Disconnected"; deviceStatus = 'Disconnected';
css += " disconnected"; css += ' disconnected';
} else if (!dev.available) { } else if (!dev.available) {
deviceStatus = "Unavailable"; deviceStatus = 'Unavailable';
css += " unavailable"; css += ' unavailable';
} }
if (dev.features && dev.features.major_version > 1) { if (dev.features && dev.features.major_version > 1) {
css += " trezor-t"; css += ' trezor-t';
} }
return ( return (
@ -204,11 +198,14 @@ export class DeviceDropdown extends Component<Props> {
<span className="label">{ dev.instanceLabel }</span> <span className="label">{ dev.instanceLabel }</span>
<span className="status">{ deviceStatus }</span> <span className="status">{ deviceStatus }</span>
</div> </div>
<div className="forget-button" onClick={ (event) => { <div
className="forget-button"
onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
this.onDeviceMenuClick({ type: 'forget', label: '' }, dev); this.onDeviceMenuClick({ type: 'forget', label: '' }, dev);
} }> </div> }}
/>
</div> </div>
); );
}); });

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
// https://github.com/KyleAMathews/react-headroom/blob/master/src/shouldUpdate.js // https://github.com/KyleAMathews/react-headroom/blob/master/src/shouldUpdate.js
@ -14,18 +14,25 @@ type Props = {
} }
export default class StickyContainer extends React.PureComponent<Props> { export default class StickyContainer extends React.PureComponent<Props> {
// Class variables. // Class variables.
currentScrollY: number = 0; currentScrollY: number = 0;
lastKnownScrollY: number = 0; lastKnownScrollY: number = 0;
topOffset: number = 0; topOffset: number = 0;
firstRender: boolean = false; firstRender: boolean = false;
framePending: boolean = false; framePending: boolean = false;
stickToBottom: boolean = false; stickToBottom: boolean = false;
top: number = 0; top: number = 0;
aside: ?HTMLElement; aside: ?HTMLElement;
wrapper: ?HTMLElement; wrapper: ?HTMLElement;
subscribers = []; subscribers = [];
handleResize = (event: Event) => { handleResize = (event: Event) => {
@ -68,24 +75,21 @@ export default class StickyContainer extends React.PureComponent<Props> {
if (this.topOffset > 0) this.topOffset = 0; if (this.topOffset > 0) this.topOffset = 0;
if (maxTop < 0 && this.topOffset < maxTop) this.topOffset = maxTop; if (maxTop < 0 && this.topOffset < maxTop) this.topOffset = maxTop;
wrapper.style.top = `${this.topOffset}px`; wrapper.style.top = `${this.topOffset}px`;
} else { } else {
wrapper.classList.remove('fixed'); wrapper.classList.remove('fixed');
wrapper.style.top = `0px`; wrapper.style.top = '0px';
this.topOffset = 0; this.topOffset = 0;
} }
if (wrapperBounds.height > viewportHeight) { if (wrapperBounds.height > viewportHeight) {
wrapper.classList.remove('fixed-bottom'); wrapper.classList.remove('fixed-bottom');
} else { } else if (wrapper.classList.contains('fixed-bottom')) {
if (wrapper.classList.contains('fixed-bottom')) {
if (bottomBounds.top < wrapperBounds.bottom - bottomBounds.height) { if (bottomBounds.top < wrapperBounds.bottom - bottomBounds.height) {
wrapper.classList.remove('fixed-bottom'); wrapper.classList.remove('fixed-bottom');
} }
} else if (bottomBounds.bottom < viewportHeight) { } else if (bottomBounds.bottom < viewportHeight) {
wrapper.classList.add('fixed-bottom'); wrapper.classList.add('fixed-bottom');
} }
}
aside.style.minHeight = `${wrapperBounds.height}px`; aside.style.minHeight = `${wrapperBounds.height}px`;
} }
@ -135,7 +139,8 @@ export default class StickyContainer extends React.PureComponent<Props> {
> >
<div <div
className="sticky-container" className="sticky-container"
ref={ node => this.wrapper = node }> ref={node => this.wrapper = node}
>
{ this.props.children } { this.props.children }
</div> </div>
</aside> </aside>

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -43,8 +43,7 @@ type DispatchProps = {
export type Props = StateProps & DispatchProps; export type Props = StateProps & DispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => { const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
return {
connect: state.connect, connect: state.connect,
accounts: state.accounts, accounts: state.accounts,
router: state.router, router: state.router,
@ -55,11 +54,9 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
wallet: state.wallet, wallet: state.wallet,
devices: state.devices, devices: state.devices,
pending: state.pending, pending: state.pending,
}; });
}
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => { const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
return {
//onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch), //onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch),
toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch), toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch),
addAccount: bindActionCreators(TrezorConnectActions.addAccount, dispatch), addAccount: bindActionCreators(TrezorConnectActions.addAccount, dispatch),
@ -68,10 +65,9 @@ const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps>
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch), duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch), gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch),
onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch), onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch),
}; });
}
// export default connect(mapStateToProps, mapDispatchToProps)(Aside); // export default connect(mapStateToProps, mapDispatchToProps)(Aside);
export default withRouter( export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Aside) connect(mapStateToProps, mapDispatchToProps)(Aside),
); );

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import * as React from 'react'; import * as React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -28,8 +28,7 @@ type ContentProps = {
} }
const Content = (props: ContentProps) => { const Content = (props: ContentProps) => (
return (
<article> <article>
<nav> <nav>
<Route path="/device/:device/network/:network/account/:account" component={AccountTabs} /> <Route path="/device/:device/network/:network/account/:account" component={AccountTabs} />
@ -41,10 +40,8 @@ const Content = (props: ContentProps) => {
<Footer /> <Footer />
</article> </article>
); );
}
const Wallet = (props: WalletContainerProps) => { const Wallet = (props: WalletContainerProps) => (
return (
<div className="app"> <div className="app">
<Header /> <Header />
{/* <div>{ props.wallet.online ? "ONLINE" : "OFFLINE" }</div> */} {/* <div>{ props.wallet.online ? "ONLINE" : "OFFLINE" }</div> */}
@ -57,14 +54,11 @@ const Wallet = (props: WalletContainerProps) => {
<ModalContainer /> <ModalContainer />
</div> </div>
); );
}
const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State, own: {}): WalletContainerProps => { const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State, own: {}): WalletContainerProps => ({
return { wallet: state.wallet,
wallet: state.wallet });
};
}
export default withRouter( export default withRouter(
connect(mapStateToProps, null)(Wallet) connect(mapStateToProps, null)(Wallet),
); );

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
@ -8,20 +8,20 @@ import { Notification } from '~/js/components/common/Notification';
import * as TrezorConnectActions from '~/js/actions/TrezorConnectActions'; import * as TrezorConnectActions from '~/js/actions/TrezorConnectActions';
import type { State, Dispatch } from '~/flowtype'; import type { State, Dispatch } from '~/flowtype';
type Props = { type Props = {
acquiring: boolean; acquiring: boolean;
acquireDevice: typeof TrezorConnectActions.acquire acquireDevice: typeof TrezorConnectActions.acquire
} }
const Acquire = (props: Props) => { const Acquire = (props: Props) => {
const actions = props.acquiring ? [] : [ const actions = props.acquiring ? [] : [
{ {
label: 'Acquire device', label: 'Acquire device',
callback: () => { callback: () => {
props.acquireDevice() props.acquireDevice();
} },
} },
]; ];
return ( return (
@ -35,17 +35,13 @@ const Acquire = (props: Props) => {
/> />
</section> </section>
); );
} };
export default connect( export default connect(
(state: State) => { (state: State) => ({
return { acquiring: state.connect.acquiring,
acquiring: state.connect.acquiring }),
}; (dispatch: Dispatch) => ({
},
(dispatch: Dispatch) => {
return {
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
}; }),
}
)(Acquire); )(Acquire);

@ -1,12 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
const Bootloader = () => { const Bootloader = () => (
return (
<section className="device-settings"> <section className="device-settings">
<div className="row"> <div className="row">
<h2>Your device is in firmware update mode</h2> <h2>Your device is in firmware update mode</h2>
@ -14,6 +13,5 @@ const Bootloader = () => {
</div> </div>
</section> </section>
); );
}
export default connect(null, null)(Bootloader); export default connect(null, null)(Bootloader);

@ -1,13 +1,12 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import DashboardImg from '~/images/dashboard.png'; import DashboardImg from '~/images/dashboard.png';
const Dashboard = () => { const Dashboard = () => (
return (
<section className="dashboard"> <section className="dashboard">
<h2>Dashboard</h2> <h2>Dashboard</h2>
<div className="row"> <div className="row">
@ -17,6 +16,5 @@ const Dashboard = () => {
</div> </div>
</section> </section>
); );
}
export default connect(null, null)(Dashboard); export default connect(null, null)(Dashboard);

@ -1,12 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
export const DeviceSettings = () => { export const DeviceSettings = () => (
return (
<section className="device-settings"> <section className="device-settings">
<div className="row"> <div className="row">
<h2>Device settings is under construction</h2> <h2>Device settings is under construction</h2>
@ -15,6 +14,5 @@ export const DeviceSettings = () => {
</div> </div>
</section> </section>
); );
}
export default connect(null, null)(DeviceSettings); export default connect(null, null)(DeviceSettings);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
@ -16,7 +16,6 @@ type State = {
} }
const AccountTabs = (props: any): any => { const AccountTabs = (props: any): any => {
const urlParams = props.match.params; const urlParams = props.match.params;
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`; const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
@ -25,6 +24,6 @@ const AccountTabs = (props: any): any => {
<a>Device settings</a> <a>Device settings</a>
</div> </div>
); );
} };
export default AccountTabs; export default AccountTabs;

@ -1,12 +1,11 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
const Initialize = () => { const Initialize = () => (
return (
<section className="device-settings"> <section className="device-settings">
<div className="row"> <div className="row">
<h2>Your device is in not initialized</h2> <h2>Your device is in not initialized</h2>
@ -15,6 +14,5 @@ const Initialize = () => {
</div> </div>
</section> </section>
); );
}
export default connect(null, null)(Initialize); export default connect(null, null)(Initialize);

@ -1,16 +1,14 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
export const WalletSettings = () => { export const WalletSettings = () => (
return (
<section className="settings"> <section className="settings">
Wallet settings Wallet settings
</section> </section>
); );
}
export default connect(null, null)(WalletSettings); export default connect(null, null)(WalletSettings);

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
@ -15,7 +15,7 @@ if (root) {
window.onbeforeunload = () => { window.onbeforeunload = () => {
store.dispatch(onBeforeUnload()); store.dispatch(onBeforeUnload());
} };
if (typeof module !== undefined && module.hasOwnProperty('hot')) { if (typeof module !== undefined && module.hasOwnProperty('hot')) {
// $FlowIssue // $FlowIssue

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import * as CONNECT from '../actions/constants/TrezorConnect'; import * as CONNECT from '../actions/constants/TrezorConnect';
import * as WALLET from '../actions/constants/wallet'; import * as WALLET from '../actions/constants/wallet';
@ -9,7 +9,7 @@ import type { Action, TrezorDevice } from '~/flowtype';
import type { import type {
AccountCreateAction, AccountCreateAction,
AccountSetBalanceAction, AccountSetBalanceAction,
AccountSetNonceAction AccountSetNonceAction,
} from '../actions/AccountsActions'; } from '../actions/AccountsActions';
export type Account = { export type Account = {
@ -28,20 +28,16 @@ export type State = Array<Account>;
const initialState: State = []; const initialState: State = [];
export const findAccount = (state: State, index: number, deviceState: string, network: string): ?Account => { export const findAccount = (state: State, index: number, deviceState: string, network: string): ?Account => state.find(a => a.deviceState === deviceState && a.index === index && a.network === network);
return state.find(a => a.deviceState === deviceState && a.index === index && a.network === network);
}
export const findDeviceAccounts = (state: State, device: TrezorDevice, network: string): Array<Account> => { export const findDeviceAccounts = (state: State, device: TrezorDevice, network: string): Array<Account> => {
if (network) { if (network) {
return state.filter((addr) => addr.deviceState === device.state && addr.network === network); return state.filter(addr => addr.deviceState === device.state && addr.network === network);
} else {
return state.filter((addr) => addr.deviceState === device.state);
}
} }
return state.filter(addr => addr.deviceState === device.state);
};
const createAccount = (state: State, action: AccountCreateAction): State => { const createAccount = (state: State, action: AccountCreateAction): State => {
// TODO check with device_id // TODO check with device_id
// check if account was created before // check if account was created before
// const exist: ?Account = state.find(account => account.address === action.address && account.network === action.network && action.device.features && account.deviceID === action.device.features.device_id); // const exist: ?Account = state.find(account => account.address === action.address && account.network === action.network && action.device.features && account.deviceID === action.device.features.device_id);
@ -60,25 +56,23 @@ const createAccount = (state: State, action: AccountCreateAction): State => {
address: action.address, address: action.address,
balance: '0', balance: '0',
nonce: 0, nonce: 0,
} };
const newState: State = [...state]; const newState: State = [...state];
newState.push(account); newState.push(account);
return newState; return newState;
} };
const removeAccounts = (state: State, device: TrezorDevice): State => state.filter(account => account.deviceState !== device.state);
const removeAccounts = (state: State, device: TrezorDevice): State => {
//return state.filter(account => device.features && account.deviceID !== device.features.device_id);
return state.filter(account => account.deviceState !== device.state);
}
const clear = (state: State, devices: Array<TrezorDevice>): State => { const clear = (state: State, devices: Array<TrezorDevice>): State => {
let newState: State = [...state]; let newState: State = [...state];
devices.forEach(d => { devices.forEach((d) => {
newState = removeAccounts(newState, d); newState = removeAccounts(newState, d);
}); });
return newState; return newState;
} };
const setBalance = (state: State, action: AccountSetBalanceAction): State => { const setBalance = (state: State, action: AccountSetBalanceAction): State => {
const index: number = state.findIndex(account => account.address === action.address && account.network === action.network && account.deviceState === action.deviceState); const index: number = state.findIndex(account => account.address === action.address && account.network === action.network && account.deviceState === action.deviceState);
@ -86,7 +80,7 @@ const setBalance = (state: State, action: AccountSetBalanceAction): State => {
newState[index].loaded = true; newState[index].loaded = true;
newState[index].balance = action.balance; newState[index].balance = action.balance;
return newState; return newState;
} };
const setNonce = (state: State, action: AccountSetNonceAction): State => { const setNonce = (state: State, action: AccountSetNonceAction): State => {
const index: number = state.findIndex(account => account.address === action.address && account.network === action.network && account.deviceState === action.deviceState); const index: number = state.findIndex(account => account.address === action.address && account.network === action.network && account.deviceState === action.deviceState);
@ -94,12 +88,10 @@ const setNonce = (state: State, action: AccountSetNonceAction): State => {
newState[index].loaded = true; newState[index].loaded = true;
newState[index].nonce = action.nonce; newState[index].nonce = action.nonce;
return newState; return newState;
} };
export default (state: State = initialState, action: Action): State => { export default (state: State = initialState, action: Action): State => {
switch (action.type) { switch (action.type) {
case ACCOUNT.CREATE: case ACCOUNT.CREATE:
return createAccount(state, action); return createAccount(state, action);
@ -124,5 +116,4 @@ export default (state: State = initialState, action: Action): State => {
default: default:
return state; return state;
} }
};
}

@ -1,19 +1,18 @@
/* @flow */ /* @flow */
'use strict';
import { TRANSPORT, DEVICE } from 'trezor-connect'; import { TRANSPORT, DEVICE } from 'trezor-connect';
import type { Device } from 'trezor-connect';
import * as CONNECT from '../actions/constants/TrezorConnect'; import * as CONNECT from '../actions/constants/TrezorConnect';
import * as WALLET from '../actions/constants/wallet'; import * as WALLET from '../actions/constants/wallet';
import type { Action, TrezorDevice } from '~/flowtype'; import type { Action, TrezorDevice } from '~/flowtype';
import type { Device } from 'trezor-connect';
export type State = Array<TrezorDevice>; export type State = Array<TrezorDevice>;
const initialState: State = []; const initialState: State = [];
const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): TrezorDevice => { const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): TrezorDevice => {
// do not merge if passphrase protection was changed // do not merge if passphrase protection was changed
// if (upcoming.features && current.features) { // if (upcoming.features && current.features) {
// if (upcoming.features.passphrase_protection !== current.features.passphrase_protection) { // if (upcoming.features.passphrase_protection !== current.features.passphrase_protection) {
@ -24,7 +23,7 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T
let instanceLabel = current.instanceLabel; let instanceLabel = current.instanceLabel;
if (upcoming.label !== current.label) { if (upcoming.label !== current.label) {
instanceLabel = upcoming.label instanceLabel = upcoming.label;
if (typeof current.instance === 'number') { if (typeof current.instance === 'number') {
instanceLabel += ` (${current.instanceName || current.instance})`; instanceLabel += ` (${current.instanceName || current.instance})`;
} }
@ -42,7 +41,7 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T
instanceName: typeof upcoming.instanceName === 'string' ? upcoming.instanceName : current.instanceName, instanceName: typeof upcoming.instanceName === 'string' ? upcoming.instanceName : current.instanceName,
state: current.state, state: current.state,
ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts, ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts,
} };
// corner-case: trying to merge unacquired device with acquired // corner-case: trying to merge unacquired device with acquired
// make sure that sensitive fields will not be changed and device will remain acquired // make sure that sensitive fields will not be changed and device will remain acquired
if (upcoming.unacquired && current.state) { if (upcoming.unacquired && current.state) {
@ -52,10 +51,9 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T
} }
return dev; return dev;
} };
const addDevice = (state: State, device: Device): State => { const addDevice = (state: State, device: Device): State => {
let affectedDevices: Array<TrezorDevice> = []; let affectedDevices: Array<TrezorDevice> = [];
let otherDevices: Array<TrezorDevice> = []; let otherDevices: Array<TrezorDevice> = [];
if (!device.features) { if (!device.features) {
@ -84,7 +82,7 @@ const addDevice = (state: State, device: Device): State => {
instanceLabel: device.label, instanceLabel: device.label,
instanceName: null, instanceName: null,
ts: new Date().getTime(), ts: new Date().getTime(),
} };
if (affectedDevices.length > 0) { if (affectedDevices.length > 0) {
// check if freshly added device has different "passphrase_protection" settings // check if freshly added device has different "passphrase_protection" settings
@ -114,23 +112,18 @@ const addDevice = (state: State, device: Device): State => {
// changedDevices.push(newDevice); // changedDevices.push(newDevice);
// } // }
const changedDevices: Array<TrezorDevice> = affectedDevices.filter(d => d.features && d.features.passphrase_protection === device.features.passphrase_protection).map(d => { const changedDevices: Array<TrezorDevice> = affectedDevices.filter(d => d.features && d.features.passphrase_protection === device.features.passphrase_protection).map(d => mergeDevices(d, { ...device, connected: true, available: true }));
return mergeDevices(d, { ...device, connected: true, available: true } );
});
if (changedDevices.length !== affectedDevices.length) { if (changedDevices.length !== affectedDevices.length) {
changedDevices.push(newDevice); changedDevices.push(newDevice);
} }
return otherDevices.concat(changedDevices); return otherDevices.concat(changedDevices);
} else {
return otherDevices.concat([newDevice]);
}
} }
return otherDevices.concat([newDevice]);
};
const duplicate = (state: State, device: TrezorDevice): State => { const duplicate = (state: State, device: TrezorDevice): State => {
if (!device.features) return state; if (!device.features) return state;
const newState: State = [...state]; const newState: State = [...state];
@ -145,23 +138,20 @@ const duplicate = (state: State, device: TrezorDevice): State => {
// instance, (instance is already part of device - added in modal) // instance, (instance is already part of device - added in modal)
instanceLabel: `${device.label} (${device.instanceName || instance})`, instanceLabel: `${device.label} (${device.instanceName || instance})`,
ts: new Date().getTime(), ts: new Date().getTime(),
} };
newState.push(newDevice); newState.push(newDevice);
return newState; return newState;
} };
const changeDevice = (state: State, device: Device): State => { const changeDevice = (state: State, device: Device): State => {
// change only acquired devices // change only acquired devices
if (!device.features) return state; if (!device.features) return state;
// find devices with the same device_id and passphrase_protection settings // find devices with the same device_id and passphrase_protection settings
// or devices with the same path (TODO: should be that way?) // or devices with the same path (TODO: should be that way?)
const affectedDevices: Array<TrezorDevice> = state.filter(d => const affectedDevices: Array<TrezorDevice> = state.filter(d => (d.features && d.features.device_id === device.features.device_id && d.features.passphrase_protection === device.features.passphrase_protection)
(d.features && d.features.device_id === device.features.device_id && d.features.passphrase_protection === device.features.passphrase_protection) || || (d.features && d.path.length > 0 && d.path === device.path));
(d.features && d.path.length > 0 && d.path === device.path)
);
const otherDevices: Array<TrezorDevice> = state.filter(d => affectedDevices.indexOf(d) === -1); const otherDevices: Array<TrezorDevice> = state.filter(d => affectedDevices.indexOf(d) === -1);
@ -172,7 +162,7 @@ const changeDevice = (state: State, device: Device): State => {
} }
return state; return state;
} };
const authDevice = (state: State, device: TrezorDevice, deviceState: string): State => { const authDevice = (state: State, device: TrezorDevice, deviceState: string): State => {
const affectedDevice: ?TrezorDevice = state.find(d => d.path === device.path && d.instance === device.instance); const affectedDevice: ?TrezorDevice = state.find(d => d.path === device.path && d.instance === device.instance);
@ -183,30 +173,23 @@ const authDevice = (state: State, device: TrezorDevice, deviceState: string): St
return otherDevices.concat([affectedDevice]); return otherDevices.concat([affectedDevice]);
} }
return state; return state;
} };
// Transform JSON form local storage into State // Transform JSON form local storage into State
const devicesFromStorage = (devices: Array<TrezorDevice>): State => { const devicesFromStorage = (devices: Array<TrezorDevice>): State => devices.map((d: TrezorDevice) => ({
return devices.map( (d: TrezorDevice) => {
return {
...d, ...d,
connected: false, connected: false,
available: false, available: false,
path: '', path: '',
acquiring: false, acquiring: false,
featuresNeedsReload: false, featuresNeedsReload: false,
isUsedElsewhere: false isUsedElsewhere: false,
} }));
});
}
// Remove all device reference from State // Remove all device reference from State
const forgetDevice = (state: State, device: TrezorDevice): State => { const forgetDevice = (state: State, device: TrezorDevice): State => state.filter(d => d.remember || (d.features && device.features && d.features.device_id !== device.features.device_id) || (!d.features && d.path !== device.path));
// remove all instances after disconnect (remember request declined)
// TODO: filter clones
return state.filter(d => d.remember || (d.features && device.features && d.features.device_id !== device.features.device_id) || (!d.features && d.path !== device.path));
}
// Remove single device reference from State // Remove single device reference from State
const forgetSingleDevice = (state: State, device: TrezorDevice): State => { const forgetSingleDevice = (state: State, device: TrezorDevice): State => {
@ -214,16 +197,15 @@ const forgetSingleDevice = (state: State, device: TrezorDevice): State => {
const newState: State = [...state]; const newState: State = [...state];
newState.splice(newState.indexOf(device), 1); newState.splice(newState.indexOf(device), 1);
return newState; return newState;
} };
const disconnectDevice = (state: State, device: Device): State => { const disconnectDevice = (state: State, device: Device): State => {
const affectedDevices: State = state.filter(d => d.path === device.path || (d.features && device.features && d.features.device_id === device.features.device_id)); const affectedDevices: State = state.filter(d => d.path === device.path || (d.features && device.features && d.features.device_id === device.features.device_id));
const otherDevices: State = state.filter(d => affectedDevices.indexOf(d) === -1); const otherDevices: State = state.filter(d => affectedDevices.indexOf(d) === -1);
if (affectedDevices.length > 0) { if (affectedDevices.length > 0) {
const acquiredDevices = affectedDevices.filter(d => !d.unacquired && d.state); const acquiredDevices = affectedDevices.filter(d => !d.unacquired && d.state);
return otherDevices.concat( acquiredDevices.map(d => { return otherDevices.concat(acquiredDevices.map((d) => {
d.connected = false; d.connected = false;
d.available = false; d.available = false;
d.isUsedElsewhere = false; d.isUsedElsewhere = false;
@ -234,7 +216,7 @@ const disconnectDevice = (state: State, device: Device): State => {
} }
return state; return state;
} };
const onSelectedDevice = (state: State, device: ?TrezorDevice): State => { const onSelectedDevice = (state: State, device: ?TrezorDevice): State => {
if (device) { if (device) {
@ -242,12 +224,10 @@ const onSelectedDevice = (state: State, device: ?TrezorDevice): State => {
return otherDevices.concat([{ ...device, ts: new Date().getTime() }]); return otherDevices.concat([{ ...device, ts: new Date().getTime() }]);
} }
return state; return state;
} };
export default function devices(state: State = initialState, action: Action): State { export default function devices(state: State = initialState, action: Action): State {
switch (action.type) { switch (action.type) {
case CONNECT.DEVICE_FROM_STORAGE: case CONNECT.DEVICE_FROM_STORAGE:
return devicesFromStorage(action.payload); return devicesFromStorage(action.payload);
@ -283,25 +263,20 @@ export default function devices(state: State = initialState, action: Action): St
default: default:
return state; return state;
} }
} }
// UTILS // UTILS
export const getNewInstance = (devices: State, device: Device | TrezorDevice): number => { export const getNewInstance = (devices: State, device: Device | TrezorDevice): number => {
const affectedDevices: State = devices.filter(d => d.features && device.features && d.features.device_id === device.features.device_id) const affectedDevices: State = devices.filter(d => d.features && device.features && d.features.device_id === device.features.device_id)
.sort((a, b) => { .sort((a, b) => {
if (!a.instance) { if (!a.instance) {
return -1; return -1;
} else {
return !b.instance || a.instance > b.instance ? 1 : -1;
} }
return !b.instance || a.instance > b.instance ? 1 : -1;
}); });
const instance: number = affectedDevices.reduce((inst, dev) => { const instance: number = affectedDevices.reduce((inst, dev) => (dev.instance ? dev.instance + 1 : inst + 1), 0);
return dev.instance ? dev.instance + 1 : inst + 1;
}, 0);
return instance; return instance;
} };

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import HDKey from 'hdkey'; import HDKey from 'hdkey';
@ -13,12 +13,12 @@ import type {
DiscoveryStartAction, DiscoveryStartAction,
DiscoveryWaitingAction, DiscoveryWaitingAction,
DiscoveryStopAction, DiscoveryStopAction,
DiscoveryCompleteAction DiscoveryCompleteAction,
} from '../actions/DiscoveryActions'; } from '../actions/DiscoveryActions';
import type { import type {
AccountCreateAction AccountCreateAction,
} from '../actions/AccountsActions' } from '../actions/AccountsActions';
export type Discovery = { export type Discovery = {
network: string; network: string;
@ -37,9 +37,7 @@ export type Discovery = {
export type State = Array<Discovery>; export type State = Array<Discovery>;
const initialState: State = []; const initialState: State = [];
const findIndex = (state: State, network: string, deviceState: string): number => { const findIndex = (state: State, network: string, deviceState: string): number => state.findIndex(d => d.network === network && d.deviceState === deviceState);
return state.findIndex(d => d.network === network && d.deviceState === deviceState);
}
const start = (state: State, action: DiscoveryStartAction): State => { const start = (state: State, action: DiscoveryStartAction): State => {
const deviceState: string = action.device.state || '0'; const deviceState: string = action.device.state || '0';
@ -58,7 +56,7 @@ const start = (state: State, action: DiscoveryStartAction): State => {
completed: false, completed: false,
waitingForDevice: false, waitingForDevice: false,
waitingForBackend: false, waitingForBackend: false,
} };
const newState: State = [...state]; const newState: State = [...state];
const index: number = findIndex(state, action.network, deviceState); const index: number = findIndex(state, action.network, deviceState);
@ -68,33 +66,31 @@ const start = (state: State, action: DiscoveryStartAction): State => {
newState.push(instance); newState.push(instance);
} }
return newState; return newState;
} };
const complete = (state: State, action: DiscoveryCompleteAction): State => { const complete = (state: State, action: DiscoveryCompleteAction): State => {
const index: number = findIndex(state, action.network, action.device.state || '0'); const index: number = findIndex(state, action.network, action.device.state || '0');
const newState: State = [...state]; const newState: State = [...state];
newState[index].completed = true; newState[index].completed = true;
return newState; return newState;
} };
const accountCreate = (state: State, action: AccountCreateAction): State => { const accountCreate = (state: State, action: AccountCreateAction): State => {
const index: number = findIndex(state, action.network, action.device.state || '0'); const index: number = findIndex(state, action.network, action.device.state || '0');
const newState: State = [...state]; const newState: State = [...state];
newState[index].accountIndex++; newState[index].accountIndex++;
return newState; return newState;
} };
const forgetDiscovery = (state: State, device: TrezorDevice): State => { const forgetDiscovery = (state: State, device: TrezorDevice): State => state.filter(d => d.deviceState !== device.state);
return state.filter(d => d.deviceState !== device.state);
}
const clear = (state: State, devices: Array<TrezorDevice>): State => { const clear = (state: State, devices: Array<TrezorDevice>): State => {
let newState: State = [...state]; let newState: State = [...state];
devices.forEach(d => { devices.forEach((d) => {
newState = forgetDiscovery(newState, d); newState = forgetDiscovery(newState, d);
}); });
return newState; return newState;
} };
const stop = (state: State, action: DiscoveryStopAction): State => { const stop = (state: State, action: DiscoveryStopAction): State => {
const newState: State = [...state]; const newState: State = [...state];
@ -105,10 +101,9 @@ const stop = (state: State, action: DiscoveryStopAction): State => {
} }
return d; return d;
}); });
} };
const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State => { const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State => {
const deviceState: string = action.device.state || '0'; const deviceState: string = action.device.state || '0';
const instance: Discovery = { const instance: Discovery = {
network: action.network, network: action.network,
@ -122,7 +117,7 @@ const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State =
completed: false, completed: false,
waitingForDevice: true, waitingForDevice: true,
waitingForBackend: false, waitingForBackend: false,
} };
const index: number = findIndex(state, action.network, deviceState); const index: number = findIndex(state, action.network, deviceState);
const newState: State = [...state]; const newState: State = [...state];
@ -133,7 +128,7 @@ const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State =
} }
return newState; return newState;
} };
const waitingForBackend = (state: State, action: DiscoveryWaitingAction): State => { const waitingForBackend = (state: State, action: DiscoveryWaitingAction): State => {
const deviceState: string = action.device.state || '0'; const deviceState: string = action.device.state || '0';
@ -148,8 +143,8 @@ const waitingForBackend = (state: State, action: DiscoveryWaitingAction): State
interrupted: false, interrupted: false,
completed: false, completed: false,
waitingForDevice: false, waitingForDevice: false,
waitingForBackend: true waitingForBackend: true,
} };
const index: number = findIndex(state, action.network, deviceState); const index: number = findIndex(state, action.network, deviceState);
const newState: State = [...state]; const newState: State = [...state];
@ -160,10 +155,9 @@ const waitingForBackend = (state: State, action: DiscoveryWaitingAction): State
} }
return newState; return newState;
} };
export default function discovery(state: State = initialState, action: Action): State { export default function discovery(state: State = initialState, action: Action): State {
switch (action.type) { switch (action.type) {
case DISCOVERY.START: case DISCOVERY.START:
return start(state, action); return start(state, action);
@ -178,7 +172,7 @@ export default function discovery(state: State = initialState, action: Action):
case DISCOVERY.WAITING_FOR_BACKEND: case DISCOVERY.WAITING_FOR_BACKEND:
return waitingForBackend(state, action); return waitingForBackend(state, action);
case DISCOVERY.FROM_STORAGE: case DISCOVERY.FROM_STORAGE:
return action.payload.map(d => { return action.payload.map((d) => {
const hdKey: HDKey = new HDKey(); const hdKey: HDKey = new HDKey();
hdKey.publicKey = new Buffer(d.publicKey, 'hex'); hdKey.publicKey = new Buffer(d.publicKey, 'hex');
hdKey.chainCode = new Buffer(d.chainCode, 'hex'); hdKey.chainCode = new Buffer(d.chainCode, 'hex');
@ -188,8 +182,8 @@ export default function discovery(state: State = initialState, action: Action):
interrupted: false, interrupted: false,
waitingForDevice: false, waitingForDevice: false,
waitingForBackend: false, waitingForBackend: false,
} };
}) });
case CONNECT.FORGET: case CONNECT.FORGET:
case CONNECT.FORGET_SINGLE: case CONNECT.FORGET_SINGLE:
return forgetDiscovery(state, action.device); return forgetDiscovery(state, action.device);
@ -199,5 +193,4 @@ export default function discovery(state: State = initialState, action: Action):
default: default:
return state; return state;
} }
} }

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import { RATE_UPDATE } from '../services/CoinmarketcapService'; import { RATE_UPDATE } from '../services/CoinmarketcapService';
@ -21,22 +21,19 @@ const update = (state: Array<Fiat>, action: FiatRateAction): Array<Fiat> => {
} else { } else {
newState.push({ newState.push({
network: action.network, network: action.network,
value: action.rate.price_usd value: action.rate.price_usd,
}) });
} }
return newState; return newState;
} };
export default (state: Array<Fiat> = initialState, action: Action): Array<Fiat> => { export default (state: Array<Fiat> = initialState, action: Action): Array<Fiat> => {
switch (action.type) { switch (action.type) {
case RATE_UPDATE: case RATE_UPDATE:
return update(state, action); return update(state, action);
default: default:
return state; return state;
} }
};
}

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import * as STORAGE from '../actions/constants/localStorage'; import * as STORAGE from '../actions/constants/localStorage';
@ -80,23 +80,21 @@ const initialState: State = {
error: null, error: null,
config: { config: {
coins: [], coins: [],
fiatValueTickers: [] fiatValueTickers: [],
}, },
ERC20Abi: [], ERC20Abi: [],
tokens: {}, tokens: {},
customBackend: [ customBackend: [
{ {
name: "Custom1", name: 'Custom1',
slug: "custom1", slug: 'custom1',
url: "http://google.com" url: 'http://google.com',
} },
] ],
}; };
export default function localStorage(state: State = initialState, action: Action): State { export default function localStorage(state: State = initialState, action: Action): State {
switch (action.type) { switch (action.type) {
case STORAGE.READY: case STORAGE.READY:
return { return {
...state, ...state,
@ -104,18 +102,17 @@ export default function localStorage(state: State = initialState, action: Action
config: action.config, config: action.config,
ERC20Abi: action.ERC20Abi, ERC20Abi: action.ERC20Abi,
tokens: action.tokens, tokens: action.tokens,
error: null error: null,
} };
case STORAGE.ERROR: case STORAGE.ERROR:
return { return {
...state, ...state,
error: action.error error: action.error,
} };
default: default:
return state; return state;
} }
} }

@ -1,5 +1,5 @@
/* @flow */ /* @flow */
'use strict';
import * as LOG from '../actions/constants/log'; import * as LOG from '../actions/constants/log';
import type { Action } from '~/flowtype'; import type { Action } from '~/flowtype';
@ -23,29 +23,26 @@ export const initialState: State = {
export default (state: State = initialState, action: Action): State => { export default (state: State = initialState, action: Action): State => {
switch (action.type) { switch (action.type) {
case LOG.OPEN: case LOG.OPEN:
return { return {
...state, ...state,
opened: true opened: true,
} };
case LOG.CLOSE: case LOG.CLOSE:
return { return {
...state, ...state,
opened: false opened: false,
} };
case LOG.ADD: case LOG.ADD:
return { return {
...state, ...state,
entries: state.entries.concat([ action.payload ]) entries: state.entries.concat([action.payload]),
} };
default: default:
return state; return state;
} }
};
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save