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",
"flow": "flow check src/js",
"test": "",
"lint": "npx eslint ./"
"lint": "npx eslint ./ --fix"
},
"dependencies": {
"color-hash": "^1.0.3",

@ -8,27 +8,27 @@ const replacePrefix = (path, opts = [], sourceFile) => {
const options = [].concat(opts);
if (typeof path === 'string') {
if (path.indexOf('~/') === 0) {
return path.replace('~/', `${ cwd }/src/`);
return path.replace('~/', `${cwd}/src/`);
}
}
return path;
}
};
export default ({ 'types': t }) => {
export default ({ types: t }) => {
const visitor = {
CallExpression(path, state) {
if (path.node.callee.name !== 'require') {
return;
}
const args = path.node.arguments;
if (!args.length) {
return;
}
const firstArg = traverseExpression(t, args[0]);
if (firstArg) {
firstArg.value = replacePrefix(firstArg.value, state.opts, state.file.opts.filename);
}
@ -45,14 +45,14 @@ export default ({ 'types': t }) => {
if (path.node.source) {
path.node.source.value = replacePrefix(path.node.source.value, state.opts, state.file.opts.filename);
}
}
},
};
return {
'visitor': {
visitor: {
Program(path, state) {
path.traverse(visitor, state);
}
}
},
},
};
}
};

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import type {
Store as ReduxStore,
@ -9,7 +9,7 @@ import type {
ThunkAction as ReduxThunkAction,
AsyncAction as ReduxAsyncAction,
ThunkDispatch as ReduxThunkDispatch,
PlainDispatch as ReduxPlainDispatch
PlainDispatch as ReduxPlainDispatch,
} from 'redux';
import type { Reducers, ReducersState } from '~/js/reducers';
@ -43,7 +43,7 @@ import type {
import type { RouterAction, LocationState } from 'react-router-redux';
export type TrezorDevice = {
remember: boolean; // device should be remembered
remember: boolean; // device should be remembered
connected: boolean; // device is connected
available: boolean; // device cannot be used because of features.passphrase_protection is different then expected
path: string;
@ -87,19 +87,19 @@ type IFrameHandshake = {
payload: any
}
export type Action =
export type Action =
RouterAction
| IFrameHandshake
| TransportEventAction
| DeviceEventAction
| UiEventAction
| SelectedAccountAction
| AccountAction
| DiscoveryAction
| StorageAction
| LogAction
| ModalAction
| StorageAction
| LogAction
| ModalAction
| NotificationAction
| PendingTxAction
| ReceiveAction

@ -7,22 +7,22 @@ declare module 'bignumber.js' {
declare type ROUND_HALF_EVEN = 2
declare type ROUND_UP = 3
declare type RM = ROUND_DOWN | ROUND_HALF_UP | ROUND_HALF_EVEN | ROUND_UP
declare class T_BigNumber {
// Properties
static DP: number;
static RM: RM;
static E_NEG: number;
static E_POS: number;
c: Array<DIGIT>;
e: number;
s: -1 | 1;
// Constructors
static (value: $npm$big$number$object): T_BigNumber;
constructor(value: $npm$big$number$object): T_BigNumber;
// Methods
abs(): BigNumber;
cmp(n: $npm$big$number$object): $npm$cmp$result;
@ -51,7 +51,7 @@ declare module 'bignumber.js' {
valueOf(): string;
toJSON(): string;
}
//declare module.exports: typeof T_BigNumber
declare export default typeof T_BigNumber;
}

@ -16,16 +16,16 @@ declare module 'ethereum-types' {
| 'mether'
| 'gether'
| 'tether'
declare export type EthereumAddressT = string
declare export type EthereumBlockNumberT = number
declare export type EthereumBlockHashT = string
declare export type EthereumTransactionHashT = string
// end data types
// start contract types
declare export type EthereumWatchErrorT = ?Object
declare export type EthereumEventT<A> = {
address: EthereumAddressT,
args: A,
@ -38,35 +38,32 @@ declare module 'ethereum-types' {
transactionLogIndex: string,
type: 'mined' // TODO: what other types are there?
}
// this represents the setup object returned from truffle-contract
// we use it to get a known contact `at(address)` (ie. for POATokenContract addresses)
declare export type EthereumContractSetupT<A> = {
at: EthereumAddressT => Promise<A>
}
declare export type EthereumSendTransactionOptionsT = {
from: EthereumAddressT,
gas: number,
value?: number
}
declare export type EthereumSendTransactionT = EthereumSendTransactionOptionsT => Promise<
EthereumTransactionHashT
>
declare export type EthereumSendTransactionT = EthereumSendTransactionOptionsT => Promise<EthereumTransactionHashT>
// TODO(mattgstevens): it would be nice to have an Generic type for a Contract instance
// similar to the EthererumWatchEventT
//
// declare export type SendTransactionContractT = interface .sendTransaction(EthereumAddressT)
// declare export type WatchableContractT = <A>(error: Object, response: A)
// declare export type EthereumContractWatcherT = (options: {
// fromBlock?: EthereumBlockNumberT,
// toBlock?: EthereumBlockNumberT,
// address?: EthereumAddressT
// }) => *
// end contract data
}

@ -1,9 +1,9 @@
// flow-typed signature: 59b0c4be0e1408f21e2446be96c79804
// 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
@ -32,9 +32,7 @@ declare module "react-redux" {
declare type Context = { store: Store<*, *> };
declare type ComponentWithDefaultProps<DP: {}, P: {}, CP: P> = Class<
React$Component<CP>
> & { defaultProps: DP };
declare type ComponentWithDefaultProps<DP: {}, P: {}, CP: P> = Class<React$Component<CP>> & { defaultProps: DP };
declare class ConnectedComponentWithDefaultProps<
OP,
@ -55,13 +53,9 @@ declare module "react-redux" {
state: void
}
declare type ConnectedComponentWithDefaultPropsClass<OP, DP, CP> = Class<
ConnectedComponentWithDefaultProps<OP, DP, CP>
>;
declare type ConnectedComponentWithDefaultPropsClass<OP, DP, CP> = Class<ConnectedComponentWithDefaultProps<OP, DP, CP>>;
declare type ConnectedComponentClass<OP, P> = Class<
ConnectedComponent<OP, P>
>;
declare type ConnectedComponentClass<OP, P> = Class<ConnectedComponent<OP, P>>;
declare type Connector<OP, P> = (<DP: {}, 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<{
basename?: string,
forceRefresh?: boolean,
@ -6,21 +6,21 @@ declare module "react-router-dom" {
keyLength?: number,
children?: React$Node
}> {}
declare export class HashRouter extends React$Component<{
basename?: string,
getUserConfirmation?: GetUserConfirmation,
hashType?: "slash" | "noslash" | "hashbang",
children?: React$Node
}> {}
declare export class Link extends React$Component<{
className?: string,
to: string | LocationShape,
replace?: boolean,
children?: React$Node
}> {}
declare export class NavLink extends React$Component<{
to: string | LocationShape,
activeClassName?: string,
@ -32,7 +32,7 @@ declare module "react-router-dom" {
exact?: boolean,
strict?: boolean
}> {}
// NOTE: Below are duplicated from react-router. If updating these, please
// update the react-router and react-router-native types as well.
declare export type Location = {
@ -42,16 +42,16 @@ declare module "react-router-dom" {
state?: any,
key?: string
};
declare export type LocationShape = {
pathname?: string,
search?: string,
hash?: string,
state?: any
};
declare export type HistoryAction = "PUSH" | "REPLACE" | "POP";
declare export type RouterHistory = {
length: number,
location: Location,
@ -72,37 +72,37 @@ declare module "react-router-dom" {
index?: number,
entries?: Array<Location>
};
declare export type Match = {
params: { [key: string]: ?string },
isExact: boolean,
path: string,
url: string
};
declare export type ContextRouter = {|
history: RouterHistory,
location: Location,
match: Match,
staticContext?: StaticRouterContext,
|};
declare export type GetUserConfirmation = (
message: string,
callback: (confirmed: boolean) => void
) => void;
declare type StaticRouterContext = {
url?: string
};
declare export class StaticRouter extends React$Component<{
basename?: string,
location?: string | Location,
context: StaticRouterContext,
children?: React$Node
}> {}
declare export class MemoryRouter extends React$Component<{
initialEntries?: Array<LocationShape | string>,
initialIndex?: number,
@ -110,22 +110,22 @@ declare module "react-router-dom" {
keyLength?: number,
children?: React$Node
}> {}
declare export class Router extends React$Component<{
history: RouterHistory,
children?: React$Node
}> {}
declare export class Prompt extends React$Component<{
message: string | ((location: Location) => string | boolean),
when?: boolean
}> {}
declare export class Redirect extends React$Component<{
to: string | LocationShape,
push?: boolean
}> {}
declare export class Route extends React$Component<{
component?: React$ComponentType<*>,
render?: (router: ContextRouter) => React$Node,
@ -134,22 +134,22 @@ declare module "react-router-dom" {
exact?: boolean,
strict?: boolean
}> {}
declare export class Switch extends React$Component<{
children?: React$Node
}> {}
declare export function withRouter<P>(
Component: React$ComponentType<{| ...ContextRouter, ...P |}>
): React$ComponentType<P>;
declare type MatchPathOptions = {
path?: string,
exact?: boolean,
sensitive?: boolean,
strict?: boolean
};
declare export function matchPath(
pathname: string,
options?: MatchPathOptions | string

@ -1,12 +1,11 @@
import type {
import type {
RouterHistory,
Location as RouterLocation
Location as RouterLocation,
} from 'react-router';
declare module "react-router-redux" {
declare module 'react-router-redux' {
// custom state for location
declare export type LocationState = {[key: string] : string};
declare export type LocationState = {[key: string]: string};
declare export type Location = {
pathname: string,
@ -16,7 +15,7 @@ declare module "react-router-redux" {
state: LocationState
}
declare export var LOCATION_CHANGE: "@@router/LOCATION_CHANGE";
declare export var LOCATION_CHANGE: "@@router/LOCATION_CHANGE";
declare export type RouterAction = {
type: typeof LOCATION_CHANGE,
@ -32,7 +31,7 @@ declare module "react-router-redux" {
declare export function go(a: string): RouterAction;
declare export function goBack(): RouterAction;
declare export function goForward(): RouterAction;
//declare export function routerReducer<S, A>(state?: S, action: A): S;
declare export function routerReducer(state?: State, action: any): State;
declare export function routerMiddleware(history: any): any;

@ -1,4 +1,4 @@
declare module "react-router" {
declare module 'react-router' {
// 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
// as well.
@ -9,16 +9,16 @@ declare module "react-router" {
state?: any,
key?: string
};
declare export type LocationShape = {
pathname?: string,
search?: string,
hash?: string,
state?: any
};
declare export type HistoryAction = "PUSH" | "REPLACE" | "POP";
declare export type RouterHistory = {
length: number,
location: Location,
@ -39,37 +39,37 @@ declare module "react-router" {
index?: number,
entries?: Array<Location>
};
declare export type Match = {
params: { [key: string]: ?string },
isExact: boolean,
path: string,
url: string
};
declare export type ContextRouter = {|
history: RouterHistory,
location: Location,
match: Match,
staticContext?: StaticRouterContext
|};
declare export type GetUserConfirmation = (
message: string,
callback: (confirmed: boolean) => void
) => void;
declare type StaticRouterContext = {
url?: string
};
declare export class StaticRouter extends React$Component<{
basename?: string,
location?: string | Location,
context: StaticRouterContext,
children?: React$Node
}> {}
declare export class MemoryRouter extends React$Component<{
initialEntries?: Array<LocationShape | string>,
initialIndex?: number,
@ -77,22 +77,22 @@ declare module "react-router" {
keyLength?: number,
children?: React$Node
}> {}
declare export class Router extends React$Component<{
history: RouterHistory,
children?: React$Node
}> {}
declare export class Prompt extends React$Component<{
message: string | ((location: Location) => string | true),
when?: boolean
}> {}
declare export class Redirect extends React$Component<{
to: string | LocationShape,
push?: boolean
}> {}
declare export class Route extends React$Component<{
component?: React$ComponentType<*>,
render?: (router: ContextRouter) => React$Node,
@ -101,15 +101,15 @@ declare module "react-router" {
exact?: boolean,
strict?: boolean
}> {}
declare export class Switch extends React$Component<{
children?: React$Node
}> {}
declare export function withRouter<P>(
Component: React$ComponentType<{| ...ContextRouter, ...P |}>
): React$ComponentType<P>;
declare type MatchPathOptions = {
path?: string,
exact?: boolean,

@ -1,5 +1,4 @@
declare module 'redux' {
/*
S = State
@ -19,7 +18,7 @@ declare module 'redux' {
declare export type AsyncDispatch<S, A> = (action: AsyncAction<S, A>) => Promise<void>;
declare export type PlainDispatch<A: {type: $Subtype<string>}> = DispatchAPI<A>;
/* NEW: Dispatch is now a combination of these different dispatch types */
declare export type ReduxDispatch<S, A> = PlainDispatch<A> & ThunkDispatch<S, A> & AsyncDispatch<S ,A>;
declare export type ReduxDispatch<S, A> = PlainDispatch<A> & ThunkDispatch<S, A> & AsyncDispatch<S, A>;
declare export type MiddlewareAPI<S, A> = {
// dispatch: Dispatch<S, A>;

@ -33,7 +33,7 @@ declare module 'web3' {
network: string;
// and many more
}
}
declare export type EstimateGasOptions = {
@ -124,10 +124,8 @@ declare module 'web3' {
}
//
//
//
//
/*declare module 'web3' {

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

@ -1,19 +1,21 @@
/* @flow */
'use strict';
import TrezorConnect from 'trezor-connect';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import * as DISCOVERY from './constants/discovery';
import * as ACCOUNT from './constants/account';
import * as TOKEN from './constants/token';
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 { 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';
export type DiscoveryAction = {
@ -50,320 +52,297 @@ export type DiscoveryCompleteAction = {
network: string
}
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (!selected) {
// TODO: throw error
console.error("Start discovery: no selected device", device)
return;
} else if (selected.path !== device.path) {
console.error("Start discovery: requested device is not selected", device, selected)
return;
} else if (!selected.state) {
console.warn("Start discovery: Selected device wasn't authenticated yet...")
return;
} else if (selected.connected && !selected.available) {
console.warn("Start discovery: Selected device is unavailable...")
return;
}
const web3 = getState().web3.find(w3 => w3.network === network);
if (!web3) {
console.error("Start discovery: Web3 does not exist", network)
return;
}
if (!web3.web3.currentProvider.isConnected()) {
console.error("Start discovery: Web3 is not connected", network)
dispatch({
type: DISCOVERY.WAITING_FOR_BACKEND,
device,
network
});
return;
}
const discovery: State = getState().discovery;
let discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (!selected) {
// TODO: throw error
console.error('Start discovery: no selected device', device);
return;
} if (selected.path !== device.path) {
console.error('Start discovery: requested device is not selected', device, selected);
return;
} if (!selected.state) {
console.warn("Start discovery: Selected device wasn't authenticated yet...");
return;
} if (selected.connected && !selected.available) {
console.warn('Start discovery: Selected device is unavailable...');
return;
}
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network
});
return;
}
const web3 = getState().web3.find(w3 => w3.network === network);
if (!web3) {
console.error('Start discovery: Web3 does not exist', network);
return;
}
if (!discoveryProcess) {
dispatch( begin(device, network) );
return;
} else {
if (discoveryProcess.completed && !ignoreCompleted) {
dispatch({
type: DISCOVERY.COMPLETE,
device,
network
});
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
// discovery cycle was interrupted
// start from beginning
dispatch( begin(device, network) );
} else {
dispatch( discoverAccount(device, discoveryProcess) );
}
}
if (!web3.web3.currentProvider.isConnected()) {
console.error('Start discovery: Web3 is not connected', network);
dispatch({
type: DISCOVERY.WAITING_FOR_BACKEND,
device,
network,
});
return;
}
}
const begin = (device: TrezorDevice, network: string): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const discovery: State = getState().discovery;
const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.network === network);
if (!coinToDiscover) return;
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network
network,
});
return;
}
// get xpub from TREZOR
const response = await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.state
},
path: coinToDiscover.bip44,
keepSession: true, // acquire and hold session
useEmptyPassphrase: !device.instance,
if (!discoveryProcess) {
dispatch(begin(device, network));
} else if (discoveryProcess.completed && !ignoreCompleted) {
dispatch({
type: DISCOVERY.COMPLETE,
device,
network,
});
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
// discovery cycle was interrupted
// start from beginning
dispatch(begin(device, network));
} else {
dispatch(discoverAccount(device, discoveryProcess));
}
};
const begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.network === network);
if (!coinToDiscover) return;
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network,
});
// get xpub from TREZOR
const response = await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
path: coinToDiscover.bip44,
keepSession: true, // acquire and hold session
useEmptyPassphrase: !device.instance,
});
// handle TREZOR response error
if (!response.success) {
// TODO: check message
console.warn("DISCOVERY ERROR", response)
if (!response.success) {
// TODO: check message
console.warn('DISCOVERY ERROR', response);
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Discovery error',
message: response.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, network));
},
},
],
},
});
return;
}
// check for interruption
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
if (discoveryProcess && discoveryProcess.interrupted) return;
const basePath: Array<number> = response.payload.path;
// send data to reducer
dispatch({
type: DISCOVERY.START,
network: coinToDiscover.network,
device,
publicKey: response.payload.publicKey,
chainCode: response.payload.chainCode,
basePath,
});
dispatch(start(device, network));
};
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const completed: boolean = discoveryProcess.completed;
discoveryProcess.completed = false;
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
const network = discoveryProcess.network;
// TODO: check if address was created before
// verify address with TREZOR
const verifyAddress = await TrezorConnect.ethereumGetAddress({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
path,
showOnTrezor: false,
keepSession: true,
useEmptyPassphrase: !device.instance,
});
if (discoveryProcess.interrupted) return;
// TODO: with block-book (Martin)
// const discoveryA = await TrezorConnect.accountDiscovery({
// device: {
// path: device.path,
// instance: device.instance,
// state: device.state
// },
// });
// if (discoveryProcess.interrupted) return;
if (verifyAddress && verifyAddress.success) {
//const trezorAddress: string = '0x' + verifyAddress.payload.address;
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address);
if (trezorAddress !== ethAddress) {
// throw inconsistent state error
console.warn('Inconsistent state', trezorAddress, ethAddress);
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Discovery error',
message: response.payload.error,
title: 'Address validation error',
message: `Addresses are different. TREZOR: ${trezorAddress} HDKey: ${ethAddress}`,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, network))
}
}
]
}
})
dispatch(start(device, discoveryProcess.network));
},
},
],
},
});
return;
}
// check for interruption
let discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
if (discoveryProcess && discoveryProcess.interrupted) return;
const basePath: Array<number> = response.payload.path;
// send data to reducer
} else {
// handle TREZOR communication error
dispatch({
type: DISCOVERY.START,
network: coinToDiscover.network,
device,
publicKey: response.payload.publicKey,
chainCode: response.payload.chainCode,
basePath,
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Address validation error',
message: verifyAddress.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, discoveryProcess.network));
},
},
],
},
});
dispatch( start(device, network) );
return;
}
}
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.find(w3 => w3.network === network);
if (!web3instance) return;
const completed: boolean = discoveryProcess.completed;
discoveryProcess.completed = false;
const balance = await getBalanceAsync(web3instance.web3, ethAddress);
if (discoveryProcess.interrupted) return;
const nonce: number = await getNonceAsync(web3instance.web3, ethAddress);
if (discoveryProcess.interrupted) return;
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
const network = discoveryProcess.network;
const addressIsEmpty = nonce < 1 && !balance.greaterThan(0);
if (!addressIsEmpty || (addressIsEmpty && completed) || (addressIsEmpty && discoveryProcess.accountIndex === 0)) {
dispatch({
type: ACCOUNT.CREATE,
device,
network,
index: discoveryProcess.accountIndex,
path,
address: ethAddress,
});
dispatch(
AccountsActions.setBalance(ethAddress, network, device.state || 'undefined', web3instance.web3.fromWei(balance.toString(), 'ether')),
);
dispatch(AccountsActions.setNonce(ethAddress, network, device.state || 'undefined', nonce));
// TODO: check if address was created before
if (!completed) { dispatch(discoverAccount(device, discoveryProcess)); }
}
// verify address with TREZOR
const verifyAddress = await TrezorConnect.ethereumGetAddress({
if (addressIsEmpty) {
// release acquired sesssion
await TrezorConnect.getFeatures({
device: {
path: device.path,
instance: device.instance,
state: device.state
state: device.state,
},
path,
showOnTrezor: false,
keepSession: true,
keepSession: false,
useEmptyPassphrase: !device.instance,
});
if (discoveryProcess.interrupted) return;
// TODO: with block-book (Martin)
// const discoveryA = await TrezorConnect.accountDiscovery({
// device: {
// path: device.path,
// instance: device.instance,
// state: device.state
// },
// });
// if (discoveryProcess.interrupted) return;
if (verifyAddress && verifyAddress.success) {
//const trezorAddress: string = '0x' + verifyAddress.payload.address;
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address);
if (trezorAddress !== ethAddress) {
// throw inconsistent state error
console.warn("Inconsistent state", trezorAddress, ethAddress);
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Address validation error',
message: `Addresses are different. TREZOR: ${ trezorAddress } HDKey: ${ ethAddress }`,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, discoveryProcess.network))
}
}
]
}
});
return;
}
} else {
// handle TREZOR communication error
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Address validation error',
message: verifyAddress.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, discoveryProcess.network))
}
}
]
}
});
return;
}
const web3instance = getState().web3.find(w3 => w3.network === network);
if (!web3instance) return;
const balance = await getBalanceAsync(web3instance.web3, ethAddress);
if (discoveryProcess.interrupted) return;
const nonce: number = await getNonceAsync(web3instance.web3, ethAddress);
if (discoveryProcess.interrupted) return;
const addressIsEmpty = nonce < 1 && !balance.greaterThan(0);
if (!addressIsEmpty || (addressIsEmpty && completed) || (addressIsEmpty && discoveryProcess.accountIndex === 0)) {
dispatch({
type: ACCOUNT.CREATE,
device,
network,
index: discoveryProcess.accountIndex,
path,
address: ethAddress
});
dispatch(
AccountsActions.setBalance(ethAddress, network, device.state || 'undefined', web3instance.web3.fromWei(balance.toString(), 'ether'))
);
dispatch(AccountsActions.setNonce(ethAddress, network, device.state || 'undefined', nonce));
if (!completed)
dispatch( discoverAccount(device, discoveryProcess) );
}
if (addressIsEmpty) {
// release acquired sesssion
await TrezorConnect.getFeatures({
device: {
path: device.path,
instance: device.instance,
state: device.state
},
keepSession: false,
useEmptyPassphrase: !device.instance,
});
if (discoveryProcess.interrupted) return;
dispatch({
type: DISCOVERY.COMPLETE,
device,
network
});
}
dispatch({
type: DISCOVERY.COMPLETE,
device,
network,
});
}
}
};
export const restore = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
export const restore = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (selected && selected.connected && selected.features) {
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === selected.state && d.waitingForDevice);
if (discoveryProcess) {
dispatch( start(selected, discoveryProcess.network) );
}
if (selected && selected.connected && selected.features) {
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === selected.state && d.waitingForDevice);
if (discoveryProcess) {
dispatch(start(selected, discoveryProcess.network));
}
}
}
};
// TODO: rename method to something intuitive
// 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
// try to start discovery after CONNECT.AUTH_DEVICE action
export const check = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (!selected) return;
const urlParams = getState().router.location.state;
if (urlParams.network) {
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === selected.state && d.network === urlParams.network);
if (!discoveryProcess) {
dispatch( start(selected, urlParams.network) );
}
export const check = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (!selected) return;
const urlParams = getState().router.location.state;
if (urlParams.network) {
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === selected.state && d.network === urlParams.network);
if (!discoveryProcess) {
dispatch(start(selected, urlParams.network));
}
}
}
};
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,
device
}
}
export const stop = (device: TrezorDevice): Action => ({
type: DISCOVERY.STOP,
device,
});

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

@ -1,15 +1,17 @@
/* @flow */
'use strict';
import * as CONNECT from './constants/TrezorConnect';
import * as ACCOUNT from './constants/account';
import * as TOKEN from './constants/token';
import * as DISCOVERY from './constants/discovery';
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 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 AppConfigJSON from '~/data/appConfig.json';
@ -28,24 +30,21 @@ export type StorageAction = {
error: string,
};
export const loadData = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
// check if local storage is available
// let available: boolean = true;
// if (typeof window.localStorage === 'undefined') {
// available = false;
// } else {
// try {
// window.localStorage.setItem('ethereum_wallet', true);
// } catch (error) {
// available = false;
// }
// }
dispatch( loadTokensFromJSON() );
}
}
export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
// check if local storage is available
// let available: boolean = true;
// if (typeof window.localStorage === 'undefined') {
// available = false;
// } else {
// try {
// window.localStorage.setItem('ethereum_wallet', true);
// } catch (error) {
// available = false;
// }
// }
dispatch(loadTokensFromJSON());
};
// const parseConfig = (json: JSON): Config => {
@ -68,13 +67,13 @@ export const loadData = (): ThunkAction => {
// if (json.config && json.hasOwnProperty('coins') && Array.isArray(json.coins)) {
// json.coins.map(c => {
// return {
// }
// })
// } else {
// throw new Error(`Property "coins" is missing in appConfig.json`);
// }
// return {
// coins: [],
@ -84,22 +83,21 @@ export const loadData = (): ThunkAction => {
export function loadTokensFromJSON(): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
if (typeof window.localStorage === 'undefined') return;
try {
const config: Config = await httpRequest(AppConfigJSON, 'json');
const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json');
window.addEventListener('storage', event => {
dispatch( update(event) );
})
window.addEventListener('storage', (event) => {
dispatch(update(event));
});
// load tokens
const tokens = await config.coins.reduce(async (promise: Promise<TokensCollection>, coin: Coin): Promise<TokensCollection> => {
const collection: TokensCollection = await promise;
const json = await httpRequest(coin.tokens, 'json');
collection[ coin.network ] = json;
collection[coin.network] = json;
return collection;
}, Promise.resolve({}));
@ -107,62 +105,60 @@ export function loadTokensFromJSON(): AsyncAction {
if (devices) {
dispatch({
type: CONNECT.DEVICE_FROM_STORAGE,
payload: JSON.parse(devices)
})
payload: JSON.parse(devices),
});
}
const accounts: ?string = get('accounts');
if (accounts) {
dispatch({
type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(accounts)
})
payload: JSON.parse(accounts),
});
}
const userTokens: ?string = get('tokens');
if (userTokens) {
dispatch({
type: TOKEN.FROM_STORAGE,
payload: JSON.parse(userTokens)
})
payload: JSON.parse(userTokens),
});
}
const pending: ?string = get('pending');
if (pending) {
dispatch({
type: PENDING.FROM_STORAGE,
payload: JSON.parse(pending)
})
payload: JSON.parse(pending),
});
}
const discovery: ?string = get('discovery');
if (discovery) {
dispatch({
type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(discovery)
})
payload: JSON.parse(discovery),
});
}
dispatch({
type: STORAGE.READY,
config,
tokens,
ERC20Abi
})
} catch(error) {
ERC20Abi,
});
} catch (error) {
dispatch({
type: STORAGE.ERROR,
error
})
error,
});
}
}
};
}
export function update(event: StorageEvent): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
if (!event.newValue) return;
if (event.key === 'devices') {
@ -183,46 +179,44 @@ export function update(event: StorageEvent): AsyncAction {
if (event.key === 'accounts') {
dispatch({
type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(event.newValue)
payload: JSON.parse(event.newValue),
});
}
if (event.key === 'tokens') {
dispatch({
type: TOKEN.FROM_STORAGE,
payload: JSON.parse(event.newValue)
payload: JSON.parse(event.newValue),
});
}
if (event.key === 'pending') {
dispatch({
type: PENDING.FROM_STORAGE,
payload: JSON.parse(event.newValue)
payload: JSON.parse(event.newValue),
});
}
if (event.key === 'discovery') {
dispatch({
type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(event.newValue)
payload: JSON.parse(event.newValue),
});
}
}
};
}
export const save = (key: string, value: string): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
if (typeof window.localStorage !== 'undefined') {
try {
window.localStorage.setItem(key, value);
} catch (error) {
// available = false;
console.error("Local Storage ERROR: " + error)
}
export const save = (key: string, value: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
if (typeof window.localStorage !== 'undefined') {
try {
window.localStorage.setItem(key, value);
} catch (error) {
// available = false;
console.error(`Local Storage ERROR: ${error}`);
}
}
}
};
export const get = (key: string): ?string => {
try {
@ -231,4 +225,4 @@ export const get = (key: string): ?string => {
// available = false;
return null;
}
}
};

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

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

@ -1,9 +1,11 @@
/* @flow */
'use strict';
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';
export type NotificationAction = {
@ -23,31 +25,27 @@ export type NotificationAction = {
}
}
export const close = (payload: any = {}): Action => {
return {
type: NOTIFICATION.CLOSE,
payload
}
}
export const close = (payload: any = {}): Action => ({
type: NOTIFICATION.CLOSE,
payload,
});
// called from RouterService
export const clear = (currentParams: RouterLocationState, requestedParams: RouterLocationState): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// 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)
if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
const entries = getState().notifications.filter(entry => typeof entry.devicePath === 'string');
entries.forEach(entry => {
if (typeof entry.devicePath === 'string') {
dispatch({
type: NOTIFICATION.CLOSE,
payload: {
devicePath: entry.devicePath
}
})
}
});
}
export const clear = (currentParams: RouterLocationState, requestedParams: RouterLocationState): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// 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)
if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
const entries = getState().notifications.filter(entry => typeof entry.devicePath === 'string');
entries.forEach((entry) => {
if (typeof entry.devicePath === 'string') {
dispatch({
type: NOTIFICATION.CLOSE,
payload: {
devicePath: entry.devicePath,
},
});
}
});
}
}
};

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import TrezorConnect from 'trezor-connect';
import * as RECEIVE from './constants/receive';
@ -8,7 +8,9 @@ import * as NOTIFICATION from './constants/notification';
import { initialState } 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 = {
type: typeof RECEIVE.INIT,
@ -26,90 +28,80 @@ export type ReceiveAction = {
type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS
}
export const init = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
const state: State = {
...initialState,
};
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const state: State = {
...initialState,
};
dispatch({
type: RECEIVE.INIT,
state: state
});
}
}
dispatch({
type: RECEIVE.INIT,
state,
});
};
export const dispose = (): Action => {
return {
type: RECEIVE.DISPOSE
}
}
export const dispose = (): Action => ({
type: RECEIVE.DISPOSE,
});
export const showUnverifiedAddress = (): Action => {
return {
type: RECEIVE.SHOW_UNVERIFIED_ADDRESS
}
}
export const showUnverifiedAddress = (): Action => ({
type: RECEIVE.SHOW_UNVERIFIED_ADDRESS,
});
//export const showAddress = (address_n: string): AsyncAction => {
export const showAddress = (address_n: Array<number>): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
export const showAddress = (address_n: Array<number>): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected = getState().wallet.selectedDevice;
if (!selected) return;
if (selected && (!selected.connected || !selected.available)) {
dispatch({
type: RECEIVE.REQUEST_UNVERIFIED,
device: selected,
});
return;
}
const selected = getState().wallet.selectedDevice;
if (!selected) return;
const response = await TrezorConnect.ethereumGetAddress({
device: {
path: selected.path,
instance: selected.instance,
state: selected.state,
},
path: address_n,
useEmptyPassphrase: !selected.instance,
});
if (selected && (!selected.connected || !selected.available)) {
dispatch({
type: RECEIVE.REQUEST_UNVERIFIED,
device: selected
});
return;
}
if (response && response.success) {
dispatch({
type: RECEIVE.SHOW_ADDRESS,
});
} else {
dispatch({
type: RECEIVE.HIDE_ADDRESS,
});
const response = await TrezorConnect.ethereumGetAddress({
device: {
path: selected.path,
instance: selected.instance,
state: selected.state
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Verifying address error',
message: response.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(showAddress(address_n));
},
},
],
},
path: address_n,
useEmptyPassphrase: !selected.instance,
});
if (response && response.success) {
dispatch({
type: RECEIVE.SHOW_ADDRESS
})
} else {
dispatch({
type: RECEIVE.HIDE_ADDRESS
})
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Verifying address error',
message: response.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(showAddress(address_n))
}
}
]
}
})
}
}
}
};
export default {
init,
dispose,
showAddress,
showUnverifiedAddress
}
showUnverifiedAddress,
};

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import { LOCATION_CHANGE } from 'react-router-redux';
import * as ACCOUNT from './constants/account';
@ -18,8 +18,8 @@ import type {
TrezorDevice,
AsyncAction,
ThunkAction,
Action,
GetState,
Action,
GetState,
Dispatch,
State,
} from '~/flowtype';
@ -32,110 +32,101 @@ export type SelectedAccountAction = {
payload: $ElementType<State, 'selectedAccount'>
};
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const locationChange: boolean = action.type === LOCATION_CHANGE;
const state: State = getState();
const location = state.router.location;
const prevLocation = prevState.router.location;
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const locationChange: boolean = action.type === LOCATION_CHANGE;
const state: State = getState();
const location = state.router.location;
const prevLocation = prevState.router.location;
let needUpdate: boolean = false;
const needUpdate: boolean = false;
// reset form to default
if (action.type === SEND.TX_COMPLETE) {
// dispatch( SendFormActions.init() );
// linear action
// SessionStorageActions.clear(location.pathname);
}
// reset form to default
if (action.type === SEND.TX_COMPLETE) {
// dispatch( SendFormActions.init() );
// linear action
// SessionStorageActions.clear(location.pathname);
}
// handle devices state change (from trezor-connect events or location change)
if (locationChange
// handle devices state change (from trezor-connect events or location change)
if (locationChange
|| prevState.accounts !== state.accounts
|| prevState.discovery !== state.discovery
|| prevState.tokens !== state.tokens
|| prevState.pending !== state.pending
|| prevState.web3 !== state.web3) {
if (locationChange) {
// dispose current account view
dispatch(dispose());
}
const account = stateUtils.getSelectedAccount(state);
const network = stateUtils.getSelectedNetwork(state);
const discovery = stateUtils.getDiscoveryProcess(state);
const tokens = stateUtils.getAccountTokens(state, account);
const pending = stateUtils.getAccountPendingTx(state.pending, account);
const web3 = stateUtils.getWeb3(state);
const payload: $ElementType<State, 'selectedAccount'> = {
// location: location.pathname,
account,
network,
discovery,
tokens,
pending,
web3,
};
if (locationChange) {
// dispose current account view
dispatch( dispose() );
}
const account = stateUtils.getSelectedAccount(state);
const network = stateUtils.getSelectedNetwork(state);
const discovery = stateUtils.getDiscoveryProcess(state);
const tokens = stateUtils.getAccountTokens(state, account);
const pending = stateUtils.getAccountPendingTx(state.pending, account);
const web3 = stateUtils.getWeb3(state);
const payload: $ElementType<State, 'selectedAccount'> = {
// location: location.pathname,
account,
network,
discovery,
tokens,
pending,
web3
}
let needUpdate: boolean = false;
Object.keys(payload).forEach((key) => {
if (Array.isArray(payload[key])) {
if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) {
needUpdate = true;
}
} else {
if (payload[key] !== state.selectedAccount[key]) {
needUpdate = true;
}
let needUpdate: boolean = false;
Object.keys(payload).forEach((key) => {
if (Array.isArray(payload[key])) {
if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) {
needUpdate = true;
}
})
} else if (payload[key] !== state.selectedAccount[key]) {
needUpdate = true;
}
});
if (needUpdate) {
dispatch({
type: ACCOUNT.UPDATE_SELECTED_ACCOUNT,
payload,
});
if (needUpdate) {
dispatch({
type: ACCOUNT.UPDATE_SELECTED_ACCOUNT,
payload,
});
// initialize SendFormReducer
if (location.state.send && getState().sendForm.currency === "") {
dispatch( SendFormActions.init() );
}
// initialize SendFormReducer
if (location.state.send && getState().sendForm.currency === '') {
dispatch(SendFormActions.init());
}
if (location.state.send) {
const rejectedTxs = pending.filter(tx => tx.rejected);
rejectedTxs.forEach(tx => {
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: "warning",
title: "Pending transaction rejected",
message: `Transaction with id: ${ tx.id } not found.`,
cancelable: true,
actions: [
{
label: 'OK',
callback: () => {
dispatch({
type: PENDING.TX_RESOLVED,
tx,
});
}
}
]
}
})
if (location.state.send) {
const rejectedTxs = pending.filter(tx => tx.rejected);
rejectedTxs.forEach((tx) => {
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'warning',
title: 'Pending transaction rejected',
message: `Transaction with id: ${tx.id} not found.`,
cancelable: true,
actions: [
{
label: 'OK',
callback: () => {
dispatch({
type: PENDING.TX_RESOLVED,
tx,
});
},
},
],
},
});
}
});
}
}
}
}
};
export const dispose = (): Action => {
return {
type: ACCOUNT.DISPOSE
}
}
export const dispose = (): Action => ({
type: ACCOUNT.DISPOSE,
});

File diff suppressed because it is too large Load Diff

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

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

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

@ -1,7 +1,9 @@
/* @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 CONNECT from './constants/TrezorConnect';
import * as NOTIFICATION from './constants/notification';
@ -20,17 +22,17 @@ import type {
TransportMessage,
DeviceMessageType,
TransportMessageType,
UiMessageType
UiMessageType,
} from 'trezor-connect';
import type {
import type {
Dispatch,
GetState,
Action,
ThunkAction,
AsyncAction,
TrezorDevice,
RouterLocationState
RouterLocationState,
} from '~/flowtype';
@ -81,301 +83,276 @@ export type TrezorConnectAction = {
};
export const init = (): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// set listeners
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
// post event to reducers
const type: DeviceMessageType = event.type; // assert flow type
dispatch({
type,
device: event.payload
});
export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// set listeners
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
// post event to reducers
const type: DeviceMessageType = event.type; // assert flow type
dispatch({
type,
device: event.payload,
});
});
TrezorConnect.on(UI_EVENT, (event: UiMessage): void => {
// post event to reducers
const type: UiMessageType = event.type; // assert flow type
dispatch({
type,
payload: event.payload
});
TrezorConnect.on(UI_EVENT, (event: UiMessage): void => {
// post event to reducers
const type: UiMessageType = event.type; // assert flow type
dispatch({
type,
payload: event.payload,
});
});
TrezorConnect.on(TRANSPORT_EVENT, (event: TransportMessage): void => {
// post event to reducers
const type: TransportMessageType = event.type; // assert flow type
dispatch({
type,
payload: event.payload
});
TrezorConnect.on(TRANSPORT_EVENT, (event: TransportMessage): void => {
// post event to reducers
const type: TransportMessageType = event.type; // assert flow type
dispatch({
type,
payload: event.payload,
});
});
try {
await TrezorConnect.init({
transportReconnect: true,
// connectSrc: 'https://localhost:8088/',
connectSrc: 'https://sisyfos.trezor.io/',
debug: false,
popup: false,
webusb: true,
pendingTransportEvent: (getState().devices.length < 1)
});
} catch (error) {
// dispatch({
// type: CONNECT.INITIALIZATION_ERROR,
// error
// })
}
try {
await TrezorConnect.init({
transportReconnect: true,
// connectSrc: 'https://localhost:8088/',
connectSrc: 'https://sisyfos.trezor.io/',
debug: false,
popup: false,
webusb: true,
pendingTransportEvent: (getState().devices.length < 1),
});
} catch (error) {
// dispatch({
// type: CONNECT.INITIALIZATION_ERROR,
// error
// })
}
}
};
// called after backend was initialized
// set listeners for connect/disconnect
export const postInit = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
export const postInit = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const handleDeviceConnect = (device: Device) => {
dispatch(initConnectedDevice(device));
};
const handleDeviceConnect = (device: Device) => {
dispatch( initConnectedDevice(device) );
}
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
const { devices } = getState();
const { devices } = getState();
const { initialPathname, initialParams } = getState().wallet;
const { initialPathname, initialParams } = getState().wallet;
if (initialPathname) {
dispatch({
type: WALLET.SET_INITIAL_URL,
// pathname: null,
// params: null
});
}
if (initialPathname) {
dispatch({
type: WALLET.SET_INITIAL_URL,
// pathname: null,
// params: null
});
}
if (devices.length > 0) {
const unacquired: ?TrezorDevice = devices.find(d => d.unacquired);
if (unacquired) {
dispatch(onSelectDevice(unacquired));
} else {
const latest: Array<TrezorDevice> = sortDevices(devices);
const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
dispatch(onSelectDevice(firstConnected || latest[0]));
// TODO
if (initialParams) {
if (!initialParams.hasOwnProperty('network') && initialPathname !== getState().router.location.pathname) {
// dispatch( push(initialPathname) );
} else {
if (devices.length > 0) {
const unacquired: ?TrezorDevice = devices.find(d => d.unacquired);
if (unacquired) {
dispatch( onSelectDevice(unacquired) );
} else {
const latest: Array<TrezorDevice> = sortDevices(devices);
const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
dispatch( onSelectDevice(firstConnected || latest[0]) );
// TODO
if (initialParams) {
if (!initialParams.hasOwnProperty("network") && initialPathname !== getState().router.location.pathname) {
// dispatch( push(initialPathname) );
} else {
}
}
}
}
}
}
const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => {
return devices.sort((a, b) => {
if (!a.ts || !b.ts) {
return -1;
} else {
return a.ts > b.ts ? -1 : 1;
}
});
}
};
export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
// if (!selected || (selected && selected.state)) {
dispatch( onSelectDevice(device) );
// }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
// dispatch( onSelectDevice(device) );
// } else if (!selected) {
// dispatch( onSelectDevice(device) );
// }
const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => devices.sort((a, b) => {
if (!a.ts || !b.ts) {
return -1;
}
}
return a.ts > b.ts ? -1 : 1;
});
export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
// if (!selected || (selected && selected.state)) {
dispatch(onSelectDevice(device));
// }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
// dispatch( onSelectDevice(device) );
// } else if (!selected) {
// dispatch( onSelectDevice(device) );
// }
};
// selection from Aside dropdown button
// after device_connect event
// or after acquiring device
// device type could be local TrezorDevice or Device (from trezor-connect device_connect event)
export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
// || device.isUsedElsewhere
// switch to initial url and reset this value
if (!device.features) {
dispatch( push(`/device/${ device.path }/acquire`) );
} else if (device.features.bootloader_mode) {
dispatch( push(`/device/${ device.path }/bootloader`) );
} else if (!device.features.initialized) {
dispatch( push(`/device/${ device.features.device_id }/initialize`) );
} else if (typeof device.instance === 'number') {
dispatch( push(`/device/${ device.features.device_id }:${ device.instance }`) );
} else {
const deviceId: string = device.features.device_id;
const urlParams: RouterLocationState = getState().router.location.state;
// let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`;
let url: string = `/device/${ deviceId }`;
let instance: ?number;
// check if device is not TrezorDevice type
if (!device.hasOwnProperty('ts')) {
// its device from trezor-connect (called in initConnectedDevice triggered by device_connect event)
// need to lookup if there are unavailable instances
const available: Array<TrezorDevice> = getState().devices.filter(d => d.path === device.path);
const latest: Array<TrezorDevice> = sortDevices(available);
if (latest.length > 0 && latest[0].instance) {
url += `:${ latest[0].instance }`;
instance = latest[0].instance;
}
export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
// || device.isUsedElsewhere
// switch to initial url and reset this value
if (!device.features) {
dispatch(push(`/device/${device.path}/acquire`));
} else if (device.features.bootloader_mode) {
dispatch(push(`/device/${device.path}/bootloader`));
} else if (!device.features.initialized) {
dispatch(push(`/device/${device.features.device_id}/initialize`));
} else if (typeof device.instance === 'number') {
dispatch(push(`/device/${device.features.device_id}:${device.instance}`));
} else {
const deviceId: string = device.features.device_id;
const urlParams: RouterLocationState = getState().router.location.state;
// let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`;
let url: string = `/device/${deviceId}`;
let instance: ?number;
// check if device is not TrezorDevice type
if (!device.hasOwnProperty('ts')) {
// its device from trezor-connect (called in initConnectedDevice triggered by device_connect event)
// need to lookup if there are unavailable instances
const available: Array<TrezorDevice> = getState().devices.filter(d => d.path === device.path);
const latest: Array<TrezorDevice> = sortDevices(available);
if (latest.length > 0 && latest[0].instance) {
url += `:${latest[0].instance}`;
instance = latest[0].instance;
}
// check if current location is not set to this device
//dispatch( push(`/device/${ device.features.device_id }/network/etc/account/0`) );
}
// check if current location is not set to this device
//dispatch( push(`/device/${ device.features.device_id }/network/etc/account/0`) );
if (urlParams.deviceInstance !== instance || urlParams.device !== deviceId) {
dispatch( push(url) );
}
if (urlParams.deviceInstance !== instance || urlParams.device !== deviceId) {
dispatch(push(url));
}
}
}
export const switchToFirstAvailableDevice = (): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
};
const { devices } = getState();
if (devices.length > 0) {
// TODO: Priority:
// 1. First Unacquired
// 2. First connected
// 3. Saved with latest timestamp
const unacquired = devices.find(d => d.unacquired);
if (unacquired) {
dispatch( initConnectedDevice(unacquired) );
} else {
const latest: Array<TrezorDevice> = sortDevices(devices);
const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
dispatch( onSelectDevice(firstConnected || latest[0]) );
}
export const switchToFirstAvailableDevice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { devices } = getState();
if (devices.length > 0) {
// TODO: Priority:
// 1. First Unacquired
// 2. First connected
// 3. Saved with latest timestamp
const unacquired = devices.find(d => d.unacquired);
if (unacquired) {
dispatch(initConnectedDevice(unacquired));
} else {
dispatch( push('/') );
const latest: Array<TrezorDevice> = sortDevices(devices);
const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
dispatch(onSelectDevice(firstConnected || latest[0]));
}
} else {
dispatch(push('/'));
}
}
};
export const getSelectedDeviceState = (): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected = getState().wallet.selectedDevice;
if (selected
export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected = getState().wallet.selectedDevice;
if (selected
&& selected.connected
&& (selected.features && !selected.features.bootloader_mode && selected.features.initialized)
&& !selected.state) {
const response = await TrezorConnect.getDeviceState({
device: {
path: selected.path,
instance: selected.instance,
state: selected.state,
},
useEmptyPassphrase: !selected.instance,
});
const response = await TrezorConnect.getDeviceState({
device: {
path: selected.path,
instance: selected.instance,
state: selected.state
if (response && response.success) {
dispatch({
type: CONNECT.AUTH_DEVICE,
device: selected,
state: response.payload.state,
});
} else {
dispatch({
type: NOTIFICATION.ADD,
payload: {
devicePath: selected.path,
type: 'error',
title: 'Authentication error',
message: response.payload.error,
cancelable: false,
actions: [
{
label: 'Try again',
callback: () => {
dispatch({
type: NOTIFICATION.CLOSE,
payload: { devicePath: selected.path },
});
dispatch(getSelectedDeviceState());
},
},
],
},
useEmptyPassphrase: !selected.instance,
});
if (response && response.success) {
dispatch({
type: CONNECT.AUTH_DEVICE,
device: selected,
state: response.payload.state
});
} else {
dispatch({
type: NOTIFICATION.ADD,
payload: {
devicePath: selected.path,
type: 'error',
title: 'Authentication error',
message: response.payload.error,
cancelable: false,
actions: [
{
label: 'Try again',
callback: () => {
dispatch( {
type: NOTIFICATION.CLOSE,
payload: { devicePath: selected.path }
});
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 (selected && selected.features && selected.features.device_id === device.features.device_id) {
dispatch( DiscoveryActions.stop(selected) );
}
if (device && device.features) {
if (selected && selected.features && selected.features.device_id === device.features.device_id) {
dispatch(DiscoveryActions.stop(selected));
}
const instances = getState().devices.filter(d => d.features && d.state && !d.remember && d.features.device_id === device.features.device_id);
if (instances.length > 0) {
dispatch({
type: CONNECT.REMEMBER_REQUEST,
device: instances[0],
instances,
});
}
const instances = getState().devices.filter(d => d.features && d.state && !d.remember && d.features.device_id === device.features.device_id);
if (instances.length > 0) {
dispatch({
type: CONNECT.REMEMBER_REQUEST,
device: instances[0],
instances,
});
}
}
}
};
export const coinChanged = (network: ?string): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return;
export const coinChanged = (network: ?string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return;
dispatch( DiscoveryActions.stop(selected) );
dispatch(DiscoveryActions.stop(selected));
if (network) {
dispatch( DiscoveryActions.start(selected, network) );
}
if (network) {
dispatch(DiscoveryActions.start(selected, network));
}
}
};
export function reload(): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
}
};
}
export function acquire(): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
if (!selected) return;
dispatch({
type: CONNECT.START_ACQUIRING,
})
});
const response = await TrezorConnect.getFeatures({
const response = await TrezorConnect.getFeatures({
device: {
path: selected.path,
},
@ -398,47 +375,41 @@ export function acquire(): AsyncAction {
// }
// }
// ]
}
})
},
});
}
dispatch({
type: CONNECT.STOP_ACQUIRING,
})
}
});
};
}
export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
if (device.features) {
const devUrl: string = `${device.features.device_id}${ device.instance ? `:${ device.instance}` : '' }`;
dispatch( push( `/device/${ devUrl }/settings` ) );
}
export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
if (device.features) {
const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`;
dispatch(push(`/device/${devUrl}/settings`));
}
}
};
// called from Aside - device menu (forget single instance)
export const forget = (device: TrezorDevice): Action => {
return {
type: CONNECT.FORGET_REQUEST,
device
};
}
export const duplicateDevice = (device: TrezorDevice): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
dispatch({
type: CONNECT.TRY_TO_DUPLICATE,
device
})
}
}
export const forget = (device: TrezorDevice): Action => ({
type: CONNECT.FORGET_REQUEST,
device,
});
export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
dispatch({
type: CONNECT.TRY_TO_DUPLICATE,
device,
});
};
export function addAccount(): ThunkAction {
return (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
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 */
'use strict';
import { LOCATION_CHANGE } from 'react-router-redux';
import * as WALLET from './constants/wallet';
@ -8,20 +8,20 @@ import * as stateUtils from '../reducers/utils';
import type { Device } from 'trezor-connect';
import type
{
{
Account,
Coin,
Discovery,
Token,
Web3Instance,
TrezorDevice,
RouterLocationState,
TrezorDevice,
RouterLocationState,
ThunkAction,
AsyncAction,
Action,
Dispatch,
Dispatch,
GetState,
State
State,
} from '~/flowtype';
export type WalletAction = {
@ -47,82 +47,66 @@ export type WalletAction = {
devices: Array<TrezorDevice>
}
export const init = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
const updateOnlineStatus = (event) => {
dispatch({
type: WALLET.ONLINE_STATUS,
online: navigator.onLine
})
}
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
}
}
export const onBeforeUnload = (): WalletAction => {
return {
type: WALLET.ON_BEFORE_UNLOAD
}
}
export const toggleDeviceDropdown = (opened: boolean): WalletAction => {
return {
type: WALLET.TOGGLE_DEVICE_DROPDOWN,
opened
}
}
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const updateOnlineStatus = (event) => {
dispatch({
type: WALLET.ONLINE_STATUS,
online: navigator.onLine,
});
};
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
};
export const onBeforeUnload = (): WalletAction => ({
type: WALLET.ON_BEFORE_UNLOAD,
});
export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({
type: WALLET.TOGGLE_DEVICE_DROPDOWN,
opened,
});
// This method will be called after each DEVICE.CONNECT action
// if connected device has different "passphrase_protection" settings than saved instances
// all saved instances will be removed immediately inside DevicesReducer
// This method will clear leftovers associated with removed instances from reducers.
// (DiscoveryReducer, AccountReducer, TokensReducer)
export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => {
if (!device.features) return;
export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
if (!device.features) return;
const affectedDevices = prevState.devices.filter(d =>
d.features
const affectedDevices = prevState.devices.filter(d => d.features
&& 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) {
dispatch({
type: WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
devices: affectedDevices,
})
}
if (affectedDevices.length > 0) {
dispatch({
type: WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
devices: affectedDevices,
});
}
}
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const locationChange: boolean = action.type === LOCATION_CHANGE;
const state: State = getState();
// handle devices state change (from trezor-connect events or location change)
if (locationChange || prevState.devices !== state.devices) {
const device = stateUtils.getSelectedDevice(state);
if (state.wallet.selectedDevice !== device) {
if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
dispatch({
type: WALLET.UPDATE_SELECTED_DEVICE,
device
});
} else {
dispatch({
type: WALLET.SET_SELECTED_DEVICE,
device
});
}
};
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const locationChange: boolean = action.type === LOCATION_CHANGE;
const state: State = getState();
// handle devices state change (from trezor-connect events or location change)
if (locationChange || prevState.devices !== state.devices) {
const device = stateUtils.getSelectedDevice(state);
if (state.wallet.selectedDevice !== device) {
if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
dispatch({
type: WALLET.UPDATE_SELECTED_DEVICE,
device,
});
} else {
dispatch({
type: WALLET.SET_SELECTED_DEVICE,
device,
});
}
}
}
}
};

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import Web3 from 'web3';
import HDKey from 'hdkey';
@ -7,27 +7,27 @@ import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx';
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 * as WEB3 from './constants/web3';
import * as PENDING from './constants/pendingTx';
import * as AccountsActions from '../actions/AccountsActions';
import * as TokenActions from '../actions/TokenActions';
import * as AccountsActions from './AccountsActions';
import * as TokenActions from './TokenActions';
import type {
import type {
Dispatch,
GetState,
Action,
AsyncAction,
} from '~/flowtype';
import type { ContractFactory, EstimateGasOptions } from 'web3';
import type BigNumber from 'bignumber.js';
import type { Account } from '../reducers/AccountsReducer';
import type { PendingTx } from '../reducers/PendingTxReducer';
import type { Web3Instance } from '../reducers/Web3Reducer';
import type { Token } from '../reducers/TokensReducer';
import type { NetworkToken } from '../reducers/LocalStorageReducer';
import type { TransactionStatus, TransactionReceipt } from 'web3';
export type Web3Action = {
type: typeof WEB3.READY,
@ -53,10 +53,9 @@ export type Web3UpdateGasPriceAction = {
export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { config, ERC20Abi } = getState().localStorage;
const coin = config.coins[ coinIndex ];
const coin = config.coins[coinIndex];
if (!coin) {
// all instances done
dispatch({
@ -72,12 +71,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
if (instance) {
const currentHost = instance.currentProvider.host;
let currentHostIndex: number = urls.indexOf(currentHost);
const currentHostIndex: number = urls.indexOf(currentHost);
if (currentHostIndex + 1 < urls.length) {
web3host = urls[currentHostIndex + 1];
} else {
console.error("TODO: Backend " + network + " not working", instance.currentProvider );
console.error(`TODO: Backend ${network} not working`, instance.currentProvider);
dispatch({
type: WEB3.CREATE,
@ -88,17 +87,17 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
erc20: instance.eth.contract(ERC20Abi),
latestBlock: '0',
gasPrice: '0',
}
},
});
// try next coin
dispatch( init(null, coinIndex + 1) );
dispatch(init(null, coinIndex + 1));
return;
}
}
//const instance = new Web3(window.web3.currentProvider);
const web3 = new Web3( new Web3.providers.HttpProvider(web3host) );
const web3 = new Web3(new Web3.providers.HttpProvider(web3host));
// instance = new Web3( new Web3.providers.HttpProvider('https://pyrus2.ubiqscan.io') ); // UBQ
//instance = new Web3( new Web3.providers.HttpProvider('https://node.expanse.tech/') ); // EXP
@ -112,7 +111,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
// initial check if backend is running
if (!web3.currentProvider.isConnected()) {
// try different url
dispatch( init(web3, coinIndex) );
dispatch(init(web3, coinIndex));
return;
}
@ -122,12 +121,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
type: WEB3.CREATE,
instance: {
network,
web3: web3,
web3,
chainId: coin.chainId,
erc20,
latestBlock: '0',
gasPrice: '0',
}
},
});
// dispatch({
@ -136,15 +135,13 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
// gasPrice
// });
// console.log("GET CHAIN", instance.version.network)
// instance.version.getWhisper((err, shh) => {
// console.log("-----whisperrr", error, shh)
// })
// const sshFilter = instance.ssh.filter('latest');
// sshFilter.watch((error, blockHash) => {
@ -157,10 +154,9 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
const onBlockMined = async (error: ?Error, blockHash: ?string) => {
if (error) {
window.setTimeout(() => {
// try again
onBlockMined(new Error("manually_triggered_error"), undefined);
onBlockMined(new Error('manually_triggered_error'), undefined);
}, 30000);
}
@ -168,7 +164,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
dispatch({
type: WEB3.BLOCK_UPDATED,
network,
blockHash
blockHash,
});
}
@ -181,70 +177,66 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
// dispatch( getBalance(account) );
// TODO: check if nonce was updated,
// update tokens balance,
// update tokens balance,
// update account balance,
// update pending transactions
}
dispatch( getBalance(account) );
dispatch(getBalance(account));
// dispatch( getNonce(account) );
}
const tokens = getState().tokens.filter(t => t.network === network);
for (const token of tokens) {
dispatch( getTokenBalance(token) );
dispatch(getTokenBalance(token));
}
dispatch( getGasPrice(network) );
dispatch(getGasPrice(network));
const pending = getState().pending.filter(p => p.network === network);
for (const tx of pending) {
dispatch( getTransactionReceipt(tx) );
dispatch(getTransactionReceipt(tx));
}
}
};
// latestBlockFilter.watch(onBlockMined);
onBlockMined(new Error("manually_triggered_error"), undefined);
onBlockMined(new Error('manually_triggered_error'), undefined);
// init next coin
dispatch( init(web3, coinIndex + 1) );
dispatch(init(web3, coinIndex + 1));
// let instance2 = new Web3( new Web3.providers.HttpProvider('https://pyrus2.ubiqscan.io') );
// console.log("INIT WEB3", instance, instance2);
// instance2.eth.getGasPrice((error, gasPrice) => {
// console.log("---gasss price from UBQ", gasPrice)
// });
}
};
}
export function getGasPrice(network: string): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const index: number = getState().web3.findIndex(w3 => w3.network === network);
const web3instance = getState().web3[ index ];
const web3instance = getState().web3[index];
const web3 = web3instance.web3;
web3.eth.getGasPrice((error, gasPrice) => {
if (!error) {
if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) {
dispatch({
type: WEB3.GAS_PRICE_UPDATED,
network: network,
gasPrice
network,
gasPrice,
});
}
}
});
}
};
}
export function getBalance(account: Account): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
const web3: Web3 = web3instance.web3;
@ -256,42 +248,39 @@ export function getBalance(account: Account): AsyncAction {
account.address,
account.network,
account.deviceState,
newBalance
newBalance,
));
// dispatch( loadHistory(addr) );
}
}
});
}
};
}
export function getTokenBalance(token: Token): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === token.network)[0];
const web3 = web3instance.web3;
const contract = web3instance.erc20.at(token.address);
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
if (balance) {
const newBalance: string = balance.dividedBy( Math.pow(10, token.decimals) ).toString(10);
const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10);
if (newBalance !== token.balance) {
dispatch(TokenActions.setBalance(
token.address,
token.ethAddress,
newBalance
newBalance,
));
}
}
});
}
};
}
export function getNonce(account: Account): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
const web3 = web3instance.web3;
@ -302,158 +291,138 @@ 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 web3 = web3instance.web3;
const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0];
const web3 = web3instance.web3;
web3.eth.getTransaction(tx.id, (error: Error, status: TransactionStatus) => {
if (!error && !status) {
dispatch({
type: PENDING.TX_NOT_FOUND,
tx,
})
} else if (status && status.blockNumber) {
web3.eth.getTransactionReceipt(tx.id, (error: Error, receipt: TransactionReceipt) => {
if (receipt) {
if (status.gas !== receipt.gasUsed) {
dispatch({
type: PENDING.TX_TOKEN_ERROR,
tx
})
}
web3.eth.getTransaction(tx.id, (error: Error, status: TransactionStatus) => {
if (!error && !status) {
dispatch({
type: PENDING.TX_NOT_FOUND,
tx,
});
} else if (status && status.blockNumber) {
web3.eth.getTransactionReceipt(tx.id, (error: Error, receipt: TransactionReceipt) => {
if (receipt) {
if (status.gas !== receipt.gasUsed) {
dispatch({
type: PENDING.TX_RESOLVED,
type: PENDING.TX_TOKEN_ERROR,
tx,
receipt
})
});
}
});
}
});
}
}
export const getTransaction = (web3: Web3, txid: string): Promise<any> => {
return new Promise((resolve, reject) => {
web3.eth.getTransaction(txid, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
dispatch({
type: PENDING.TX_RESOLVED,
tx,
receipt,
});
}
});
}
});
}
};
export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber> => {
return new Promise((resolve, reject) => {
web3.eth.getBalance(address, (error: Error, result: BigNumber) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
export const getTransaction = (web3: Web3, txid: string): Promise<any> => new Promise((resolve, reject) => {
web3.eth.getTransaction(txid, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
}
export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Promise<string> => {
return new Promise((resolve, reject) => {
const contract = erc20.at(token.address);
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
if (error) {
reject(error);
} else {
const newBalance: string = balance.dividedBy( Math.pow(10, token.decimals) ).toString(10);
resolve(newBalance);
}
});
});
export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber> => new Promise((resolve, reject) => {
web3.eth.getBalance(address, (error: Error, result: BigNumber) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
}
});
export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Promise<string> => new Promise((resolve, reject) => {
const contract = erc20.at(token.address);
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
if (error) {
reject(error);
} else {
const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10);
resolve(newBalance);
}
});
});
export const getNonceAsync = (web3: Web3, address: string): Promise<number> => new Promise((resolve, reject) => {
web3.eth.getTransactionCount(address, (error: Error, result: number) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
export const getNonceAsync = (web3: Web3, address: string): Promise<number> => {
return new Promise((resolve, reject) => {
web3.eth.getTransactionCount(address, (error: Error, result: number) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => new Promise((resolve, reject) => {
const contract = erc20.at(address, (error, res) => {
// console.warn("callback", error, res)
});
}
const info: NetworkToken = {
address,
name: '',
symbol: '',
decimals: 0,
};
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => {
return new Promise((resolve, reject) => {
contract.name.call((error: Error, name: string) => {
if (error) {
resolve(null);
return;
}
info.name = name;
const contract = erc20.at(address, (error, res) => {
// console.warn("callback", error, res)
});
const info: NetworkToken = {
address,
name: '',
symbol: '',
decimals: 0
};
contract.name.call((error: Error, name: string) => {
contract.symbol.call((error: Error, symbol: string) => {
if (error) {
resolve(null);
return;
} else {
info.name = name;
}
contract.symbol.call((error: Error, symbol: string) => {
if (error) {
resolve(null);
return;
info.symbol = symbol;
contract.decimals.call((error: Error, decimals: BigNumber) => {
if (decimals) {
info.decimals = decimals.toNumber();
resolve(info);
} else {
info.symbol = symbol;
resolve(null);
}
contract.decimals.call((error: Error, decimals: BigNumber) => {
if (decimals) {
info.decimals = decimals.toNumber();
resolve(info);
} else {
resolve(null);
}
});
})
});
});
});
}
export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => {
return new Promise((resolve, reject) => {
web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => {
if (error) {
reject(error);
} else if (typeof gas === 'number'){
resolve(gas);
}
});
})
}
export const pushTx = (web3: Web3, tx: any): Promise<string> => {
return new Promise((resolve, reject) => {
web3.eth.sendRawTransaction(tx, (error: Error, result: string) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
})
}
});
export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => new Promise((resolve, reject) => {
web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => {
if (error) {
reject(error);
} else if (typeof gas === 'number') {
resolve(gas);
}
});
});
export const pushTx = (web3: Web3, tx: any): Promise<string> => new Promise((resolve, reject) => {
web3.eth.sendRawTransaction(tx, (error: Error, result: string) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});

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

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

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

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

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
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';

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

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

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

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

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

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

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

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

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

@ -1,30 +1,28 @@
/* @flow */
'use strict';
import React from 'react';
const Header = (): React$Element<string> => {
return (
<header>
<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">
<polygon points="101.1,12.8 118.2,12.8 118.2,17.3 108.9,29.9 118.2,29.9 118.2,35.2 101.1,35.2 101.1,30.7 110.4,18.1 101.1,18.1"/>
<path d="M158.8,26.9c2.1-0.8,4.3-2.9,4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1,7.5h6.7L158.8,26.9z M154.7,22.5 h-4V18h4c1.5,0,2.5,0.9,2.5,2.2C157.2,21.6,156.2,22.5,154.7,22.5z"/>
<path d="M130.8,12.5c-6.8,0-11.6,4.9-11.6,11.5s4.9,11.5,11.6,11.5s11.7-4.9,11.7-11.5S137.6,12.5,130.8,12.5z M130.8,30.3 c-3.4,0-5.7-2.6-5.7-6.3c0-3.8,2.3-6.3,5.7-6.3c3.4,0,5.8,2.6,5.8,6.3C136.6,27.7,134.2,30.3,130.8,30.3z"/>
<polygon points="82.1,12.8 98.3,12.8 98.3,18 87.9,18 87.9,21.3 98,21.3 98,26.4 87.9,26.4 87.9,30 98.3,30 98.3,35.2 82.1,35.2 "/>
<path d="M24.6,9.7C24.6,4.4,20,0,14.4,0S4.2,4.4,4.2,9.7v3.1H0v22.3h0l14.4,6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4,9.7 c0-2.5,2.2-4.5,5-4.5s5,2,5,4.5v3.1H9.4V9.7z M23,31.5l-8.6,4l-8.6-4V18.1H23V31.5z"/>
<path d="M79.4,20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1,7.5H80l-4.9-8.3C77.2,26.1,79.4,24,79.4,20.3z M71,22.5h-4V18 h4c1.5,0,2.5,0.9,2.5,2.2C73.5,21.6,72.5,22.5,71,22.5z"/>
<polygon points="40.5,12.8 58.6,12.8 58.6,18.1 52.4,18.1 52.4,35.2 46.6,35.2 46.6,18.1 40.5,18.1 "/>
</svg>
<div>
<a href="https://trezor.io/" target="_blank" rel="noreferrer noopener">TREZOR</a>
<a href="https://doc.satoshilabs.com/trezor-user/" target="_blank" rel="noreferrer noopener">Docs</a>
<a href="https://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</a>
<a href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</a>
</div>
const Header = (): React$Element<string> => (
<header>
<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">
<polygon points="101.1,12.8 118.2,12.8 118.2,17.3 108.9,29.9 118.2,29.9 118.2,35.2 101.1,35.2 101.1,30.7 110.4,18.1 101.1,18.1" />
<path d="M158.8,26.9c2.1-0.8,4.3-2.9,4.3-6.6c0-4.5-3.1-7.4-7.7-7.4h-10.5v22.3h5.8v-7.5h2.2l4.1,7.5h6.7L158.8,26.9z M154.7,22.5 h-4V18h4c1.5,0,2.5,0.9,2.5,2.2C157.2,21.6,156.2,22.5,154.7,22.5z" />
<path d="M130.8,12.5c-6.8,0-11.6,4.9-11.6,11.5s4.9,11.5,11.6,11.5s11.7-4.9,11.7-11.5S137.6,12.5,130.8,12.5z M130.8,30.3 c-3.4,0-5.7-2.6-5.7-6.3c0-3.8,2.3-6.3,5.7-6.3c3.4,0,5.8,2.6,5.8,6.3C136.6,27.7,134.2,30.3,130.8,30.3z" />
<polygon points="82.1,12.8 98.3,12.8 98.3,18 87.9,18 87.9,21.3 98,21.3 98,26.4 87.9,26.4 87.9,30 98.3,30 98.3,35.2 82.1,35.2 " />
<path d="M24.6,9.7C24.6,4.4,20,0,14.4,0S4.2,4.4,4.2,9.7v3.1H0v22.3h0l14.4,6.7l14.4-6.7h0V12.9h-4.2V9.7z M9.4,9.7 c0-2.5,2.2-4.5,5-4.5s5,2,5,4.5v3.1H9.4V9.7z M23,31.5l-8.6,4l-8.6-4V18.1H23V31.5z" />
<path d="M79.4,20.3c0-4.5-3.1-7.4-7.7-7.4H61.2v22.3H67v-7.5h2.2l4.1,7.5H80l-4.9-8.3C77.2,26.1,79.4,24,79.4,20.3z M71,22.5h-4V18 h4c1.5,0,2.5,0.9,2.5,2.2C73.5,21.6,72.5,22.5,71,22.5z" />
<polygon points="40.5,12.8 58.6,12.8 58.6,18.1 52.4,18.1 52.4,35.2 46.6,35.2 46.6,18.1 40.5,18.1 " />
</svg>
<div>
<a href="https://trezor.io/" target="_blank" rel="noreferrer noopener">TREZOR</a>
<a href="https://doc.satoshilabs.com/trezor-user/" target="_blank" rel="noreferrer noopener">Docs</a>
<a href="https://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</a>
<a href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</a>
</div>
</header>
);
}
</div>
</header>
);
export default Header;

@ -1,17 +1,16 @@
/* @flow */
'use strict';
import React from 'react';
export default (props: { size: string, label?: string }): React$Element<string> => {
const style = {
width: `${props.size}px`,
height: `${props.size}px`,
}
width: `${props.size}px`,
height: `${props.size}px`,
};
return (
<div className="loader-circle" style={ style }>
<div className="loader-circle" style={style}>
<p>{ props.label }</p>
<svg className="circular" viewBox="25 25 50 50">
<circle className="route" cx="50" cy="50" r="20" fill="none" stroke="" strokeWidth="1" strokeMiterlimit="10" />
@ -19,4 +18,4 @@ export default (props: { size: string, label?: string }): React$Element<string>
</svg>
</div>
);
}
};

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
@ -14,34 +14,29 @@ type Props = {
}
const Log = (props: Props): ?React$Element<string> => {
if (!props.log.opened)
return null;
if (!props.log.opened) return null;
// const entries = props.log.entries.map(entry => {
// return (
// )
// })
return (
<div className="log">
<button className="log-close transparent" onClick={ props.toggle }></button>
<button className="log-close transparent" onClick={props.toggle} />
<h2>Log</h2>
<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>
)
}
);
};
export default connect(
(state: State) => {
return {
log: state.log
};
},
(dispatch: Dispatch) => {
return {
toggle: bindActionCreators(LogActions.toggle, dispatch),
};
}
export default connect(
(state: State) => ({
log: state.log,
}),
(dispatch: Dispatch) => ({
toggle: bindActionCreators(LogActions.toggle, dispatch),
}),
)(Log);

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import TrezorConnect from 'trezor-connect';
@ -25,41 +25,38 @@ const DisconnectDevice = (props: Props) => {
</span>
</p>
</div>
<div className="image">
</div>
<div className="image" />
</main>
)
}
);
};
const ConnectHIDDevice = (props: Props) => {
return (
<main>
<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 allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<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-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect>
<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>
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></rect>
</g>
</svg>
const ConnectHIDDevice = (props: Props) => (
<main>
<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 allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row">
<p className="connect">
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<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 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" />
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
</g>
</svg>
Connect TREZOR to continue
</span>
</p>
</div>
<div className="image">
<p>
<span>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></span>
</p>
</div>
</main>
);
}
</span>
</p>
</div>
<div className="image">
<p>
<span>Don't have TREZOR? <a href="https://trezor.io/" className="green" target="_blank" rel="noreferrer noopener">Get one</a></span>
</p>
</div>
</main>
);
class ConnectWebUsbDevice extends Component<Props> {
componentDidMount(): void {
@ -81,10 +78,10 @@ class ConnectWebUsbDevice extends Component<Props> {
<span>
<svg width="12px" height="35px" viewBox="0 0 20 57">
<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-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect>
<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>
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></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" />
<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" />
</g>
</svg>
Connect TREZOR
@ -107,12 +104,11 @@ class ConnectWebUsbDevice extends Component<Props> {
const ConnectDevice = (props: Props) => {
const { transport, disconnectRequest } = props;
if (disconnectRequest) {
return <DisconnectDevice {...props} />
} else if (transport && transport.version.indexOf('webusb') >= 0) {
return <ConnectWebUsbDevice {...props} />
} else {
return <ConnectHIDDevice {...props} />
return <DisconnectDevice {...props} />;
} if (transport && transport.version.indexOf('webusb') >= 0) {
return <ConnectWebUsbDevice {...props} />;
}
}
return <ConnectHIDDevice {...props} />;
};
export default ConnectDevice;

@ -1,9 +1,9 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import Preloader from './Preloader';
import Select from 'react-select';
import Preloader from './Preloader';
type State = {
version: string;
@ -35,7 +35,6 @@ type Props = {
}
export default class InstallBridge extends Component<Props, State> {
constructor(props: Props) {
super(props);
@ -49,7 +48,7 @@ export default class InstallBridge extends Component<Props, State> {
onChange(value: InstallTarget) {
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) {
const currentTarget: ?InstallTarget = installers.find(i => i.id === this.props.browserState.osname);
this.setState({
target: currentTarget
})
target: currentTarget,
});
}
}
@ -67,8 +66,8 @@ export default class InstallBridge extends Component<Props, State> {
return <Preloader />;
}
const label: string = this.state.target.label;
const url = `${ this.state.url }${ this.state.target.value }`;
const url = `${this.state.url}${this.state.target.value}`;
return (
<main>
<h3 className="claim">TREZOR Bridge. <span>Version 2.0.12</span></h3>
@ -77,12 +76,13 @@ export default class InstallBridge extends Component<Props, State> {
<Select
name="installers"
className="installers"
searchable={ false }
clearable= { false }
value={ this.state.target }
onChange={ this.onChange.bind(this) }
options={ installers } />
<a href={ url } className="button">Download for { label }</a>
searchable={false}
clearable={false}
value={this.state.target}
onChange={this.onChange.bind(this)}
options={installers}
/>
<a href={url} className="button">Download for { label }</a>
</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>
</main>

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React from 'react';
import Preloader from './Preloader';
@ -14,29 +14,25 @@ import Notifications, { Notification } from '../common/Notification';
import type { Props } from './index';
const BrowserNotSupported = (props: {}): React$Element<string> => {
return (
<main>
<h2>Your browser is not supported</h2>
<p>Please choose one of the supported browsers</p>
<div className="row">
<div className="chrome">
<p>Google Chrome</p>
<a className="button" href="https://www.google.com/chrome/" target="_blank" rel="noreferrer noopener">Get Chrome</a>
</div>
<div className="firefox">
<p>Mozilla Firefox</p>
<a className="button" href="https://www.mozilla.org/en-US/firefox/new/" target="_blank" rel="noreferrer noopener">Get Firefox</a>
</div>
const BrowserNotSupported = (props: {}): React$Element<string> => (
<main>
<h2>Your browser is not supported</h2>
<p>Please choose one of the supported browsers</p>
<div className="row">
<div className="chrome">
<p>Google Chrome</p>
<a className="button" href="https://www.google.com/chrome/" target="_blank" rel="noreferrer noopener">Get Chrome</a>
</div>
</main>
)
}
<div className="firefox">
<p>Mozilla Firefox</p>
<a className="button" href="https://www.mozilla.org/en-US/firefox/new/" target="_blank" rel="noreferrer noopener">Get Firefox</a>
</div>
</div>
</main>
);
export default (props: Props) => {
const web3 = props.web3;
const { devices } = props;
const { browserState, transport } = props.connect;
@ -49,26 +45,28 @@ export default (props: Props) => {
const bridgeRoute: boolean = props.router.location.state.hasOwnProperty('bridge');
if (localStorageError) {
notification = (<Notification
title="Initialization error"
message="Config files are missing"
className="error"
/>);
notification = (
<Notification
title="Initialization error"
message="Config files are missing"
className="error"
/>
);
css += ' config-error';
} else if (browserState.supported === false) {
css += ' browser-not-supported'
css += ' browser-not-supported';
body = <BrowserNotSupported />;
} else if (connectError || bridgeRoute) {
css += ' install-bridge';
body = <InstallBridge browserState={ browserState } />;
body = <InstallBridge browserState={browserState} />;
} else if (props.wallet.ready && devices.length < 1) {
css += ' connect-device';
body = <ConnectDevice transport={ transport } disconnectRequest={ props.wallet.disconnectRequest } />;
body = <ConnectDevice transport={transport} disconnectRequest={props.wallet.disconnectRequest} />;
}
if (notification || body) {
return (
<div className={ css }>
<div className={css}>
<Header />
{ notification }
<Notifications />
@ -77,7 +75,6 @@ export default (props: Props) => {
<Footer />
</div>
);
} else {
return (<Preloader />);
}
}
return (<Preloader />);
};

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

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

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

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import { findAccount } from '~/js/reducers/AccountsReducer';
@ -7,10 +7,9 @@ import { findAccount } from '~/js/reducers/AccountsReducer';
import type { Props } from './index';
const ConfirmAddress = (props: Props) => {
const {
account,
network
network,
} = props.selectedAccount;
if (!account || !network) return null;
@ -26,11 +25,10 @@ const ConfirmAddress = (props: Props) => {
</div>
</div>
);
}
};
export default ConfirmAddress;
export class ConfirmUnverifiedAddress extends Component<Props> {
keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void {
@ -52,18 +50,17 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
verifyAddress() {
if (!this.props.modal.opened) return;
const {
account
account,
} = this.props.selectedAccount;
if (!account) return null;
this.props.modalActions.onCancel();
this.props.receiveActions.showAddress(account.addressPath);
}
showUnverifiedAddress() {
if (!this.props.modal.opened) return;
this.props.modalActions.onCancel();
this.props.receiveActions.showUnverifiedAddress();
}
@ -71,33 +68,33 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
render() {
if (!this.props.modal.opened) return null;
const {
device
device,
} = this.props.modal;
const {
onCancel
onCancel,
} = this.props.modalActions;
let deviceStatus: string;
let claim: string;
if (!device.connected) {
deviceStatus = `${ device.label } is not connected`;
claim = 'Please connect your device'
deviceStatus = `${device.label} is not connected`;
claim = 'Please connect your device';
} else {
// 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';
deviceStatus = `${ device.label } is unavailable`;
claim = `Please ${ enable } passphrase settings`;
deviceStatus = `${device.label} is unavailable`;
claim = `Please ${enable} passphrase settings`;
}
return (
<div className="confirm-address-unverified">
<button className="close-modal transparent" onClick={ onCancel }></button>
<button className="close-modal transparent" onClick={onCancel} />
<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>
<button onClick={ event => this.verifyAddress() }>Try again</button>
<button className="white" onClick={ event => this.showUnverifiedAddress() }>Show unverified address</button>
<button onClick={event => this.verifyAddress()}>Try again</button>
<button className="white" onClick={event => this.showUnverifiedAddress()}>Show unverified address</button>
</div>
);
}

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

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

@ -1,12 +1,12 @@
/* @flow */
'use strict';
import React from 'react';
import type { Props } from './index';
const InvalidPin = (props: Props) => {
if (!props.modal.opened) return null;
const { device } = props.modal;
return (
<div className="pin">
@ -14,6 +14,6 @@ const InvalidPin = (props: Props) => {
<p>Retrying...</p>
</div>
);
}
};
export default InvalidPin;

@ -1,10 +1,11 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import raf from 'raf';
import type { Props } from './index';
type State = {
deviceLabel: string;
singleInput: boolean;
@ -18,10 +19,12 @@ type State = {
}
export default class PinModal extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
passphraseInput: ?HTMLInputElement;
passphraseRevisionInput: ?HTMLInputElement;
constructor(props: Props) {
@ -48,13 +51,11 @@ export default class PinModal extends Component<Props, State> {
passphraseRevisionFocused: false,
passphraseRevisionTouched: false,
match: true,
visible: false
}
visible: false,
};
}
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) {
event.preventDefault();
//this.passphraseInput.blur();
@ -65,7 +66,7 @@ export default class PinModal extends Component<Props, State> {
this.submit();
// TODO: set timeout, or wait for blur event
// TODO: set timeout, or wait for blur event
//onPassphraseSubmit(passphrase, passphraseCached);
//raf(() => onPassphraseSubmit(passphrase));
}
@ -73,13 +74,11 @@ export default class PinModal extends Component<Props, State> {
componentDidMount(): void {
// one time autofocus
if (this.passphraseInput)
this.passphraseInput.focus();
if (this.passphraseInput) this.passphraseInput.focus();
this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false);
// document.oncontextmenu = (event) => {
// const el = window.event.srcElement || event.target;
// const type = el.tagName.toLowerCase() || '';
@ -101,12 +100,12 @@ export default class PinModal extends Component<Props, State> {
// we don't want to keep password inside "value" attribute,
// so we need to replace it thru javascript
componentDidUpdate() {
const {
const {
passphrase,
passphraseRevision,
passphraseFocused,
passphraseRevisionFocused,
visible
visible,
} = this.state;
// } = this.props.modal;
@ -121,11 +120,11 @@ export default class PinModal extends Component<Props, State> {
if (this.passphraseInput) {
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) {
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') {
this.setState({
match: this.state.singleInput || this.state.passphraseRevision === value,
passphrase: value
passphrase: value,
});
} else {
this.setState({
match: this.state.passphrase === value,
passphraseRevision: value,
passphraseRevisionTouched: true
passphraseRevisionTouched: true,
});
}
}
@ -150,11 +149,11 @@ export default class PinModal extends Component<Props, State> {
onPassphraseFocus = (input: string): void => {
if (input === 'passphrase') {
this.setState({
passphraseFocused: true
passphraseFocused: true,
});
} else {
this.setState({
passphraseRevisionFocused: true
passphraseRevisionFocused: true,
});
}
}
@ -162,24 +161,24 @@ export default class PinModal extends Component<Props, State> {
onPassphraseBlur = (input: string): void => {
if (input === 'passphrase') {
this.setState({
passphraseFocused: false
passphraseFocused: false,
});
} else {
this.setState({
passphraseRevisionFocused: false
passphraseRevisionFocused: false,
});
}
}
onPassphraseShow = (): void => {
this.setState({
visible: true
visible: true,
});
}
onPassphraseHide = (): void => {
this.setState({
visible: false
visible: false,
});
}
@ -203,21 +202,20 @@ export default class PinModal extends Component<Props, State> {
passphraseRevision: '',
passphraseFocused: false,
passphraseRevisionFocused: false,
visible: false
})
visible: false,
});
raf(() => onPassphraseSubmit(empty ? '' : passphrase));
}
render() {
if (!this.props.modal.opened) return null;
const {
const {
device,
} = this.props.modal;
const {
const {
deviceLabel,
singleInput,
passphrase,
@ -229,9 +227,9 @@ export default class PinModal extends Component<Props, State> {
passphraseRevisionTouched,
} = this.state;
let passphraseInputType: string = visible || (!visible && !passphraseFocused) ? "text" : "password";
let passphraseRevisionInputType: string = visible || (!visible && !passphraseRevisionFocused) ? "text" : "password";
passphraseInputType = passphraseRevisionInputType = "text";
let passphraseInputType: string = visible || (!visible && !passphraseFocused) ? 'text' : 'password';
let passphraseRevisionInputType: string = visible || (!visible && !passphraseRevisionFocused) ? 'text' : 'password';
passphraseInputType = passphraseRevisionInputType = 'text';
//let passphraseInputType: string = visible || passphraseFocused ? "text" : "password";
//let passphraseRevisionInputType: string = visible || passphraseRevisionFocused ? "text" : "password";
@ -246,44 +244,46 @@ export default class PinModal extends Component<Props, State> {
<div className="row">
<label>Passphrase</label>
<input
ref={ (element) => { this.passphraseInput = element; } }
onChange={ event => this.onPassphraseChange('passphrase', event.currentTarget.value) }
type={ passphraseInputType }
ref={(element) => { this.passphraseInput = element; }}
onChange={event => this.onPassphraseChange('passphrase', event.currentTarget.value)}
type={passphraseInputType}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
data-lpignore="true"
onFocus={ event => this.onPassphraseFocus('passphrase') }
onBlur={ event => this.onPassphraseBlur('passphrase') }
tabIndex="1" />
onFocus={event => this.onPassphraseFocus('passphrase')}
onBlur={event => this.onPassphraseBlur('passphrase')}
tabIndex="1"
/>
</div>
{ singleInput ? null : (
<div className="row">
<label>Re-enter passphrase</label>
<input
ref={ (element) => { this.passphraseRevisionInput = element; } }
onChange={ event => this.onPassphraseChange('revision', event.currentTarget.value) }
type={ passphraseRevisionInputType }
ref={(element) => { this.passphraseRevisionInput = element; }}
onChange={event => this.onPassphraseChange('revision', event.currentTarget.value)}
type={passphraseRevisionInputType}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
data-lpignore="true"
onFocus={ event => this.onPassphraseFocus('revision') }
onBlur={ event => this.onPassphraseBlur('revision') }
tabIndex="2" />
onFocus={event => this.onPassphraseFocus('revision')}
onBlur={event => this.onPassphraseBlur('revision')}
tabIndex="2"
/>
{ !match && passphraseRevisionTouched ? <span className="error">Passphrases do not match</span> : null }
</div>
) }
<div className="row">
<label className="custom-checkbox">
<input type="checkbox" tabIndex="3" onChange={ showPassphraseCheckboxFn } checked={ visible } />
<span className="indicator"></span>
<input type="checkbox" tabIndex="3" onChange={showPassphraseCheckboxFn} checked={visible} />
<span className="indicator" />
Show passphrase
</label>
{/* <label className="custom-checkbox">
@ -294,12 +294,12 @@ export default class PinModal extends Component<Props, State> {
</div>
<div>
<button type="button" className="submit" tabIndex="4" disabled={ !match } onClick={ event => this.submit() }>Enter</button>
<button type="button" className="submit" tabIndex="4" disabled={!match} onClick={event => this.submit()}>Enter</button>
</div>
<div>
<p>If you want to access your default account</p>
<p><a className="green" onClick={ event => this.submit(true) }>Leave passphrase blank</a></p>
<p><a className="green" onClick={event => this.submit(true)}>Leave passphrase blank</a></p>
</div>
</div>

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

@ -1,16 +1,17 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import type { Props } from './index';
type State = {
pin: string;
}
export default class Pin extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
constructor(props: Props) {
@ -18,7 +19,7 @@ export default class Pin extends Component<Props, State> {
this.state = {
pin: '',
}
};
}
onPinAdd = (input: number): void => {
@ -26,7 +27,7 @@ export default class Pin extends Component<Props, State> {
if (pin.length < 9) {
pin += input;
this.setState({
pin: pin
pin,
});
}
}
@ -43,56 +44,55 @@ export default class Pin extends Component<Props, State> {
event.preventDefault();
switch (event.keyCode) {
case 13 :
case 13:
// enter,
onPinSubmit(pin);
break;
// backspace
case 8 :
case 8:
this.onPinBackspace();
break;
// numeric and numpad
case 49 :
case 97 :
case 49:
case 97:
this.onPinAdd(1);
break;
case 50 :
case 98 :
case 50:
case 98:
this.onPinAdd(2);
break;
case 51 :
case 99 :
case 51:
case 99:
this.onPinAdd(3);
break;
case 52 :
case 100 :
case 52:
case 100:
this.onPinAdd(4);
break;
case 53 :
case 101 :
case 53:
case 101:
this.onPinAdd(5);
break;
case 54 :
case 102 :
case 54:
case 102:
this.onPinAdd(6);
break;
case 55 :
case 103 :
case 55:
case 103:
this.onPinAdd(7);
break;
case 56 :
case 104 :
case 56:
case 104:
this.onPinAdd(8);
break;
case 57 :
case 105 :
case 57:
case 105:
this.onPinAdd(9);
break;
}
}
componentWillMount(): void {
this.keyboardHandler = this.keyboardHandler.bind(this);
@ -104,13 +104,12 @@ export default class Pin extends Component<Props, State> {
}
render() {
if (!this.props.modal.opened) return null;
const { onPinSubmit } = this.props.modalActions;
const { device } = this.props.modal;
const { pin } = this.state;
return (
<div className="pin">
{/* <button className="close-modal transparent"></button> */}
@ -119,26 +118,26 @@ export default class Pin extends Component<Props, State> {
<div className="pin-input-row">
<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 className="pin-row">
<button type="button" data-value="7" onClick={ event => this.onPinAdd(7) }>&#8226;</button>
<button type="button" data-value="8" onClick={ event => this.onPinAdd(8) }>&#8226;</button>
<button type="button" data-value="9" onClick={ event => this.onPinAdd(9) }>&#8226;</button>
<button type="button" data-value="7" onClick={event => this.onPinAdd(7)}>&#8226;</button>
<button type="button" data-value="8" onClick={event => this.onPinAdd(8)}>&#8226;</button>
<button type="button" data-value="9" onClick={event => this.onPinAdd(9)}>&#8226;</button>
</div>
<div className="pin-row">
<button type="button" data-value="4" onClick={ event => this.onPinAdd(4) }>&#8226;</button>
<button type="button" data-value="5" onClick={ event => this.onPinAdd(5) }>&#8226;</button>
<button type="button" data-value="6" onClick={ event => this.onPinAdd(6) }>&#8226;</button>
<button type="button" data-value="4" onClick={event => this.onPinAdd(4)}>&#8226;</button>
<button type="button" data-value="5" onClick={event => this.onPinAdd(5)}>&#8226;</button>
<button type="button" data-value="6" onClick={event => this.onPinAdd(6)}>&#8226;</button>
</div>
<div className="pin-row">
<button type="button" data-value="1" onClick={ event => this.onPinAdd(1) }>&#8226;</button>
<button type="button" data-value="2" onClick={ event => this.onPinAdd(2) }>&#8226;</button>
<button type="button" data-value="3" onClick={ event => this.onPinAdd(3) }>&#8226;</button>
<button type="button" data-value="1" onClick={event => this.onPinAdd(1)}>&#8226;</button>
<button type="button" data-value="2" onClick={event => this.onPinAdd(2)}>&#8226;</button>
<button type="button" data-value="3" onClick={event => this.onPinAdd(3)}>&#8226;</button>
</div>
<div><button className="submit" type="button" onClick={ event => onPinSubmit(pin) }>Enter PIN</button></div>
<div><button className="submit" type="button" onClick={event => onPinSubmit(pin)}>Enter PIN</button></div>
<p>Not sure how PIN works? <a className="green" href="http://doc.satoshilabs.com/trezor-user/enteringyourpin.html" target="_blank" rel="noreferrer noopener">Learn more</a></p>
</div>
);

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import Loader from '../common/LoaderCircle';
@ -12,8 +12,8 @@ type State = {
}
export default class RememberDevice extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
constructor(props: Props) {
@ -21,7 +21,7 @@ export default class RememberDevice extends Component<Props, State> {
this.state = {
countdown: 10,
}
};
}
keyboardHandler(event: KeyboardEvent): void {
@ -32,10 +32,9 @@ export default class RememberDevice extends Component<Props, State> {
}
componentDidMount(): void {
const ticker = () => {
if (this.state.countdown - 1 <= 0) {
// TODO: possible race condition,
// TODO: possible race condition,
// device could be already connected but it didn't emit Device.CONNECT event yet
window.clearInterval(this.state.ticker);
if (this.props.modal.opened) {
@ -43,14 +42,14 @@ export default class RememberDevice extends Component<Props, State> {
}
} else {
this.setState({
countdown: this.state.countdown - 1
countdown: this.state.countdown - 1,
});
}
}
};
this.setState({
countdown: 10,
ticker: window.setInterval(ticker, 1000)
ticker: window.setInterval(ticker, 1000),
});
this.keyboardHandler = this.keyboardHandler.bind(this);
@ -76,13 +75,13 @@ export default class RememberDevice extends Component<Props, State> {
const { onForgetDevice, onRememberDevice } = this.props.modalActions;
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) {
label = instances.map((instance, index) => {
let comma: string = '';
if (index > 0) comma = ', ';
return (
<span key={ index }>{ comma }{ instance.instanceLabel }</span>
<span key={index}>{ comma }{ instance.instanceLabel }</span>
);
});
}
@ -90,15 +89,14 @@ export default class RememberDevice extends Component<Props, State> {
<div className="remember">
<h3>Forget {label}?</h3>
<p>Would you like TREZOR Wallet to forget your { devicePlural }, so that it is still visible even while disconnected?</p>
<button onClick={ event => this.forget() }><span>Forget <Loader size="28" label={ this.state.countdown.toString() } /></span></button>
<button className="white" onClick={ event => onRememberDevice(device) }>Remember</button>
<button onClick={event => this.forget()}><span>Forget <Loader size="28" label={this.state.countdown.toString()} /></span></button>
<button className="white" onClick={event => onRememberDevice(device)}>Remember</button>
</div>
);
}
}
export class ForgetDevice extends Component<Props> {
keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void {
@ -131,8 +129,8 @@ export class ForgetDevice extends Component<Props> {
<div className="remember">
<h3>Forget { device.instanceLabel } ?</h3>
<p>Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your TREZOR again.</p>
<button onClick={ event => this.forget() }>Forget</button>
<button className="white" onClick={ onCancel }>Don't forget</button>
<button onClick={event => this.forget()}>Forget</button>
<button className="white" onClick={onCancel}>Don't forget</button>
</div>
);
}

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
@ -16,8 +16,8 @@ type State = {
}
class Indicator extends Component<Props, State> {
reposition: () => void;
state: State;
constructor(props: Props) {
@ -26,9 +26,9 @@ class Indicator extends Component<Props, State> {
this.state = {
style: {
width: 0,
left: 0
left: 0,
},
}
};
this.reposition = this.reposition.bind(this);
}
@ -63,21 +63,20 @@ class Indicator extends Component<Props, State> {
this.setState({
style: {
width: bounds.width,
left: left,
}
})
left,
},
});
}
}
render() {
return (
<div className="indicator" style={ this.state.style }>{ this.props.pathname }</div>
<div className="indicator" style={this.state.style}>{ this.props.pathname }</div>
);
}
}
const AccountTabs = (props: any) => {
const urlParams = props.match.params;
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
@ -86,21 +85,21 @@ const AccountTabs = (props: any) => {
{/* <NavLink to={ `${basePath}` }>
History
</NavLink> */}
<NavLink exact to={ `${basePath}` }>
<NavLink exact to={`${basePath}`}>
Summary
</NavLink>
<NavLink to={ `${basePath}/send` }>
<NavLink to={`${basePath}/send`}>
Send
</NavLink>
<NavLink to={ `${basePath}/receive` }>
<NavLink to={`${basePath}/receive`}>
Receive
</NavLink>
{/* <NavLink to={ `${basePath}/signverify` }>
Sign &amp; Verify
</NavLink> */}
<Indicator pathname={props.match.pathname } />
<Indicator pathname={props.match.pathname} />
</div>
);
}
};
export default AccountTabs;

@ -1,10 +1,12 @@
/* @flow */
'use strict';
import * as React from 'react';
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 { Discovery } from '~/js/reducers/DiscoveryReducer';
@ -16,7 +18,7 @@ export type StateProps = {
}
export type DispatchProps = {
}
export type Props = StateProps & DispatchProps;
@ -31,7 +33,7 @@ const SelectedAccount = (props: Props) => {
const {
account,
discovery
discovery,
} = accountState;
// account not found (yet). checking why...
@ -45,73 +47,68 @@ const SelectedAccount = (props: Props) => {
<Notification className="info" title="Loading accounts..." />
</section>
);
} else {
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
return (
<section>
<Notification
className="info"
title={ `Device ${ device.instanceLabel } is unavailable` }
message="Change passphrase settings to use this device"
/>
</section>
);
}
} else {
// case 3: device is disconnected
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
return (
<section>
<Notification
className="info"
title={ `Device ${ device.instanceLabel } is disconnected` }
message="Connect device to load accounts"
/>
<Notification
className="info"
title={`Device ${device.instanceLabel} is unavailable`}
message="Change passphrase settings to use this device"
/>
</section>
);
}
} else if (discovery.waitingForBackend) {
// case 4: backend is not working
// case 3: device is disconnected
return (
<section>
<Notification className="warning" title="Backend not working" />
<Notification
className="info"
title={`Device ${device.instanceLabel} is disconnected`}
message="Connect device to load accounts"
/>
</section>
);
} else if (discovery.completed) {
// case 5: account not found and discovery is completed
} if (discovery.waitingForBackend) {
// case 4: backend is not working
return (
<section>
<Notification className="warning" title="Account does not exist" />
<Notification className="warning" title="Backend not working" />
</section>
);
} else {
// case 6: discovery is not completed yet
} if (discovery.completed) {
// case 5: account not found and discovery is completed
return (
<section>
<Notification className="info" title="Loading accounts..." />
<Notification className="warning" title="Account does not exist" />
</section>
);
}
} else {
let notification: ?React$Element<typeof Notification> = null;
if (!device.connected) {
notification = <Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } />;
} else if (!device.available) {
notification = <Notification className="info" title={ `Device ${ device.instanceLabel } is unavailable` } message="Change passphrase settings to use this device" />;
}
if (discovery && !discovery.completed && !notification) {
notification = <Notification className="info" title="Loading accounts..." />;
}
// case 6: discovery is not completed yet
return (
<section className={ props.className }>
{ notification }
{ props.children }
<section>
<Notification className="info" title="Loading accounts..." />
</section>
)
);
}
}
let notification: ?React$Element<typeof Notification> = null;
if (!device.connected) {
notification = <Notification className="info" title={`Device ${device.instanceLabel} is disconnected`} />;
} else if (!device.available) {
notification = <Notification className="info" title={`Device ${device.instanceLabel} is unavailable`} message="Change passphrase settings to use this device" />;
}
if (discovery && !discovery.completed && !notification) {
notification = <Notification className="info" title="Loading accounts..." />;
}
return (
<section className={props.className}>
{ notification }
{ props.children }
</section>
);
};
export default SelectedAccount;

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
@ -12,9 +12,7 @@ import { Notification } from '~/js/components/common/Notification';
import type { Props } from './index';
const Receive = (props: Props) => {
const device = props.wallet.selectedDevice;
const {
account,
@ -33,7 +31,7 @@ const Receive = (props: Props) => {
let address = `${account.address.substring(0, 20)}...`;
let className = 'address hidden';
let button = (
<button disabled={ device.connected && !discovery.completed } onClick={ event => props.showAddress(account.addressPath) }>
<button disabled={device.connected && !discovery.completed} onClick={event => props.showAddress(account.addressPath)}>
<span>Show full address</span>
</button>
);
@ -46,42 +44,42 @@ const Receive = (props: Props) => {
fgColor="#000000"
level="Q"
style={{ width: 256 }}
value={ account.address }
/>
value={account.address}
/>
);
address = account.address;
className = addressUnverified ? 'address unverified' : 'address';
const tooltip = addressUnverified ?
(<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>);
const tooltip = addressUnverified
? (<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>);
button = (
<Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
overlay={ tooltip }
placement="bottomRight">
<button className="white" onClick={ event => props.showAddress(account.addressPath) }>
<span></span>
arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={tooltip}
placement="bottomRight"
>
<button className="white" onClick={event => props.showAddress(account.addressPath)}>
<span />
</button>
</Tooltip>
);
}
let ver = null;
const ver = null;
if (props.modal.opened && props.modal.windowType === 'ButtonRequest_Address') {
className = 'address verifying';
address = account.address;
// ver = (<div className="confirm">Confirm address on TREZOR</div>)
button = (<div className="confirm">{ account.network } account #{ (account.index + 1) }</div>);
}
return (
<div>
<h2>Receive Ethereum or tokens</h2>
<div className={ className }>
<div className={className}>
{ ver }
<div className="value">
{ address }
@ -91,12 +89,10 @@ const Receive = (props: Props) => {
{ qrCode }
</div>
);
}
};
export default (props: Props) => {
return (
<SelectedAccount { ...props }>
<Receive { ...props} />
</SelectedAccount>
);
}
export default (props: Props) => (
<SelectedAccount {...props}>
<Receive {...props} />
</SelectedAccount>
);

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React from 'react';
import Tooltip from 'rc-tooltip';
@ -13,13 +13,12 @@ type Props = {
};
const AdvancedForm = (props: Props) => {
const {
const {
account,
network
network,
} = props.selectedAccount;
const {
const {
networkSymbol,
currency,
gasPrice,
@ -31,7 +30,7 @@ const AdvancedForm = (props: Props) => {
errors,
warnings,
infos,
advanced
advanced,
} = props.sendForm;
const {
@ -39,19 +38,21 @@ const AdvancedForm = (props: Props) => {
onGasPriceChange,
onGasLimitChange,
onNonceChange,
onDataChange
onDataChange,
} = props.sendFormActions;
if (!advanced) return (
<div className="advanced-container">
<a className="advanced" onClick={ toggleAdvanced }>Advanced settings</a>
{ props.children }
</div>
);
if (!advanced) {
return (
<div className="advanced-container">
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
{ props.children }
</div>
);
}
const nonceTooltip = (
<div className="tooltip-wrapper">
TODO<br/>
TODO<br />
</div>
);
@ -67,18 +68,18 @@ const AdvancedForm = (props: Props) => {
const gasLimitTooltip = (
<div className="tooltip-wrapper">
Gas limit is the amount of gas to send with your transaction.<br/>
<span>TX fee = gas price * gas limit</span> &amp; is paid to miners for including your TX in a block.<br/>
Increasing this number will not get your TX mined faster.<br/>
Gas limit is the amount of gas to send with your transaction.<br />
<span>TX fee = gas price * gas limit</span> &amp; is paid to miners for including your TX in a block.<br />
Increasing this number will not get your TX mined faster.<br />
Default value for sending { gasLimitTooltipCurrency } is <span>{ gasLimitTooltipValue }</span>
</div>
);
const gasPriceTooltip = (
<div className="tooltip-wrapper">
Gas Price is the amount you pay per unit of gas.<br/>
<span>TX fee = gas price * gas limit</span> &amp; is paid to miners for including your TX in a block.<br/>
Higher the gas price = faster transaction, but more expensive. Recommended is <span>{ recommendedGasPrice } GWEI.</span><br/>
Gas Price is the amount you pay per unit of gas.<br />
<span>TX fee = gas price * gas limit</span> &amp; is paid to miners for including your TX in a block.<br />
Higher the gas price = faster transaction, but more expensive. Recommended is <span>{ recommendedGasPrice } GWEI.</span><br />
<a className="green" href="https://myetherwallet.github.io/knowledge-base/gas/what-is-gas-ethereum.html" target="_blank" rel="noreferrer noopener">Read more</a>
</div>
);
@ -90,10 +91,9 @@ const AdvancedForm = (props: Props) => {
);
return (
<div className="advanced-container opened">
<a className="advanced" onClick={ toggleAdvanced }>Advanced settings</a>
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
<div className="row gas-row">
{/* <div className="column nonce">
<label>
@ -105,7 +105,7 @@ const AdvancedForm = (props: Props) => {
<span className="what-is-it"></span>
</Tooltip>
</label>
<input
<input
type="text"
autoComplete="off"
autoCorrect="off"
@ -118,45 +118,49 @@ const AdvancedForm = (props: Props) => {
</div> */}
<div className="column">
<label>
Gas limit
Gas limit
<Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
overlay={ gasLimitTooltip }
placement="top">
<span className="what-is-it"></span>
arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={gasLimitTooltip}
placement="top"
>
<span className="what-is-it" />
</Tooltip>
</label>
<input
<input
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={ gasLimit }
disabled={ networkSymbol === currency && data.length > 0 }
onChange={ event => onGasLimitChange(event.target.value) } />
value={gasLimit}
disabled={networkSymbol === currency && data.length > 0}
onChange={event => onGasLimitChange(event.target.value)}
/>
{ errors.gasLimit ? (<span className="error">{ errors.gasLimit }</span>) : null }
{ warnings.gasLimit ? (<span className="warning">{ warnings.gasLimit }</span>) : null }
{ calculatingGasLimit ? (<span className="info">Calculating...</span>) : null }
</div>
<div className="column">
<label>
Gas price
Gas price
<Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
overlay={ gasPriceTooltip }
placement="top">
<span className="what-is-it"></span>
arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={gasPriceTooltip}
placement="top"
>
<span className="what-is-it" />
</Tooltip>
</label>
<input
<input
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={ gasPrice }
onChange={ event => onGasPriceChange(event.target.value) } />
value={gasPrice}
onChange={event => onGasPriceChange(event.target.value)}
/>
{ errors.gasPrice ? (<span className="error">{ errors.gasPrice }</span>) : null }
</div>
</div>
@ -165,13 +169,14 @@ const AdvancedForm = (props: Props) => {
<label>
Data
<Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
overlay={ dataTooltip }
placement="top">
<span className="what-is-it"></span>
arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={dataTooltip}
placement="top"
>
<span className="what-is-it" />
</Tooltip>
</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 }
</div>
@ -180,7 +185,7 @@ const AdvancedForm = (props: Props) => {
</div>
</div>
)
}
);
};
export default AdvancedForm;

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

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React from 'react';
import ColorHash from 'color-hash';
@ -24,17 +24,15 @@ type Style = {
}
const PendingTransactions = (props: Props) => {
const pending = props.pending.filter(tx => !tx.rejected);
if (pending.length < 1) return null;
const tokens: Array<Token> = props.tokens;
const bgColorFactory = new ColorHash({lightness: 0.7});
const bgColorFactory = new ColorHash({ lightness: 0.7 });
const textColorFactory = new ColorHash();
const pendingTxs: React$Element<string> = pending.map((tx, i) => {
let iconColor: Style;
let symbol: string;
let name: string;
@ -45,17 +43,17 @@ const PendingTransactions = (props: Props) => {
iconColor = {
color: '#ffffff',
background: '#000000',
borderColor: '#000000'
}
symbol = "Unknown";
name = "Unknown";
borderColor: '#000000',
};
symbol = 'Unknown';
name = 'Unknown';
} else {
const bgColor: string = bgColorFactory.hex(token.name);
iconColor = {
color: textColorFactory.hex(token.name),
background: bgColor,
borderColor: bgColor
}
borderColor: bgColor,
};
symbol = token.symbol.toUpperCase();
name = token.name;
}
@ -63,25 +61,25 @@ const PendingTransactions = (props: Props) => {
iconColor = {
color: textColorFactory.hex(tx.network),
background: bgColorFactory.hex(tx.network),
borderColor: bgColorFactory.hex(tx.network)
}
borderColor: bgColorFactory.hex(tx.network),
};
symbol = props.network.symbol;
name = props.network.name;
}
return (
<div key={i} className="tx">
<div className="icon" style={ iconColor }>
<div className="icon" style={iconColor}>
<div className="icon-inner">
<ScaleText widthOnly><p>{ symbol }</p></ScaleText>
</div>
</div>
<div className="name">
<a href={ `${props.network.explorer.tx}${tx.id}`} target="_blank" rel="noreferrer noopener">{ name }</a>
<a href={`${props.network.explorer.tx}${tx.id}`} target="_blank" rel="noreferrer noopener">{ name }</a>
</div>
<div className="amount">{ isSmartContractTx ? tx.amount : tx.total } { symbol }</div>
</div>
)
);
});
@ -90,7 +88,7 @@ const PendingTransactions = (props: Props) => {
<h2>Pending transactions</h2>
{ pendingTxs }
</div>
)
}
);
};
export default PendingTransactions;

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import Select from 'react-select';
@ -17,7 +17,6 @@ import type { Token } from '~/flowtype';
export default class SendContainer extends Component<Props> {
componentWillReceiveProps(newProps: Props) {
calculate(this.props, newProps);
validation(newProps);
@ -27,8 +26,8 @@ export default class SendContainer extends Component<Props> {
render() {
return (
<SelectedAccount { ...this.props }>
<Send { ...this.props} />
<SelectedAccount {...this.props}>
<Send {...this.props} />
</SelectedAccount>
);
}
@ -36,18 +35,17 @@ export default class SendContainer extends Component<Props> {
const Send = (props: Props) => {
const device = props.wallet.selectedDevice;
const {
account,
network,
discovery,
tokens
tokens,
} = props.selectedAccount;
if (!device || !account || !discovery || !network) return null;
const {
const {
address,
amount,
setMax,
@ -77,9 +75,7 @@ const Send = (props: Props) => {
const fiatRate = props.fiat.find(f => f.network === network);
const tokensSelectData = tokens.map(t => {
return { value: t.symbol, label: t.symbol };
});
const tokensSelectData = tokens.map(t => ({ value: t.symbol, label: t.symbol }));
tokensSelectData.unshift({ value: network.symbol, label: network.symbol });
const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max';
@ -87,8 +83,8 @@ const Send = (props: Props) => {
let updateFeeLevelsButton = null;
if (gasPriceNeedsUpdate) {
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;
@ -103,12 +99,12 @@ const Send = (props: Props) => {
let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
let buttonLabel: string = 'Send';
if (networkSymbol !== currency && amount.length > 0 && !errors.amount) {
buttonLabel += ` ${amount} ${ currency.toUpperCase() }`
buttonLabel += ` ${amount} ${currency.toUpperCase()}`;
} else if (networkSymbol === currency && total !== '0') {
buttonLabel += ` ${total} ${ network.symbol }`;
buttonLabel += ` ${total} ${network.symbol}`;
}
if (!device.connected){
if (!device.connected) {
buttonLabel = 'Device is not connected';
buttonDisabled = true;
} else if (!device.available) {
@ -118,22 +114,23 @@ const Send = (props: Props) => {
buttonLabel = 'Loading accounts';
buttonDisabled = true;
}
return (
<section className="send-form">
<h2>Send Ethereum or tokens</h2>
<div className="row address-input">
<label>Address</label>
<input
<input
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={ address }
className={ addressClassName }
onChange={ event => onAddressChange(event.target.value) } />
<span className="input-icon"></span>
value={address}
className={addressClassName}
onChange={event => onAddressChange(event.target.value)}
/>
<span className="input-icon" />
{ errors.address ? (<span className="error">{ errors.address }</span>) : null }
{ warnings.address ? (<span className="warning">{ warnings.address }</span>) : null }
{ infos.address ? (<span className="info">{ infos.address }</span>) : null }
@ -142,28 +139,30 @@ const Send = (props: Props) => {
<div className="row">
<label>Amount</label>
<div className="amount-input">
<input
<input
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={ amount }
className={ errors.amount ? 'not-valid' : null }
onChange={ event => onAmountChange(event.target.value) } />
value={amount}
className={errors.amount ? 'not-valid' : null}
onChange={event => onAmountChange(event.target.value)}
/>
<a className={ setMaxClassName } onClick={ onSetMax }>Set max</a>
<a className={setMaxClassName} onClick={onSetMax}>Set max</a>
<Select
name="currency"
className="currency"
searchable={ false }
clearable= { false }
multi={ false }
value={ currency }
disabled={ tokensSelectData.length < 2 }
onChange={ onCurrencyChange }
options={ tokensSelectData } />
searchable={false}
clearable={false}
multi={false}
value={currency}
disabled={tokensSelectData.length < 2}
onChange={onCurrencyChange}
options={tokensSelectData}
/>
</div>
{ errors.amount ? (<span className="error">{ errors.amount }</span>) : null }
{ warnings.amount ? (<span className="warning">{ warnings.amount }</span>) : null }
@ -171,32 +170,35 @@ const Send = (props: Props) => {
<div className="row">
<label>Fee{ updateFeeLevelsButton }</label>
<Select
<Select
name="fee"
className="fee"
searchable={ false }
clearable= { false }
value={ selectedFeeLevel }
onChange={ onFeeLevelChange }
valueComponent={ FeeSelectValue }
optionComponent={ FeeSelectOption }
disabled={ networkSymbol === currency && data.length > 0 }
searchable={false}
clearable={false}
value={selectedFeeLevel}
onChange={onFeeLevelChange}
valueComponent={FeeSelectValue}
optionComponent={FeeSelectOption}
disabled={networkSymbol === currency && data.length > 0}
optionClassName="fee-option"
options={ feeLevels } />
options={feeLevels}
/>
</div>
<AdvancedForm
selectedAccount={ props.selectedAccount }
sendForm={ props.sendForm }
sendFormActions={ props.sendFormActions }>
<button disabled={ buttonDisabled } onClick={ event => onSend() }>{ buttonLabel }</button>
<AdvancedForm
selectedAccount={props.selectedAccount}
sendForm={props.sendForm}
sendFormActions={props.sendFormActions}
>
<button disabled={buttonDisabled} onClick={event => onSend()}>{ buttonLabel }</button>
</AdvancedForm>
<PendingTransactions
pending={ props.selectedAccount.pending }
tokens={ props.selectedAccount.tokens }
network={ network } />
<PendingTransactions
pending={props.selectedAccount.pending}
tokens={props.selectedAccount.tokens}
network={network}
/>
</section>
);
}
};

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

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component } from 'react';
import BigNumber from 'bignumber.js';
@ -22,7 +22,7 @@ const Summary = (props: Props) => {
account,
network,
tokens,
pending
pending,
} = props.selectedAccount;
// flow
@ -40,79 +40,77 @@ const Summary = (props: Props) => {
return (
<div>
<h2 className={ `summary-header ${account.network}` }>
<h2 className={`summary-header ${account.network}`}>
Account #{ parseInt(account.index) + 1 }
<a href={ explorerLink } className="gray" target="_blank" rel="noreferrer noopener">See full transaction history</a>
<a href={explorerLink} className="gray" target="_blank" rel="noreferrer noopener">See full transaction history</a>
</h2>
<SummaryDetails
coin={ network }
summary={ props.summary }
balance={ balance }
network={ network.network }
fiat={ props.fiat }
localStorage={ props.localStorage }
onToggle={ props.onDetailsToggle } />
<SummaryDetails
coin={network}
summary={props.summary}
balance={balance}
network={network.network}
fiat={props.fiat}
localStorage={props.localStorage}
onToggle={props.onDetailsToggle}
/>
<h2>
Tokens
<Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
overlay={ tokensTooltip }
placement="top">
<span className="what-is-it"></span>
arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={tokensTooltip}
placement="top"
>
<span className="what-is-it" />
</Tooltip>
</h2>
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 Lahod */}
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 T01 */}
<div className="filter">
<AsyncSelect
<AsyncSelect
className="token-select"
multi={ false }
autoload={ false }
ignoreCase={ true }
backspaceRemoves={ true }
value={ null }
onChange={ token => props.addToken(token, account) }
loadOptions={ input => props.loadTokens(input, account.network) }
filterOptions= {
(options: Array<NetworkToken>, search: string, values: Array<NetworkToken>) => {
return options.map(o => {
const added = tokens.find(t => t.symbol === o.symbol);
if (added) {
return {
...o,
name: `${o.name} (Already added)`,
disabled: true
};
} else {
return o;
}
});
}
multi={false}
autoload={false}
ignoreCase
backspaceRemoves
value={null}
onChange={token => props.addToken(token, account)}
loadOptions={input => props.loadTokens(input, account.network)}
filterOptions={
(options: Array<NetworkToken>, search: string, values: Array<NetworkToken>) => options.map((o) => {
const added = tokens.find(t => t.symbol === o.symbol);
if (added) {
return {
...o,
name: `${o.name} (Already added)`,
disabled: true,
};
}
return o;
})
}
valueKey="symbol"
labelKey="name"
valueKey="symbol"
labelKey="name"
placeholder="Search for token"
searchPromptText="Type token name or address"
noResultsText="Token not found" />
noResultsText="Token not found"
/>
</div>
<SummaryTokens
pending={ pending }
tokens={ tokens }
removeToken={ props.removeToken } />
<SummaryTokens
pending={pending}
tokens={tokens}
removeToken={props.removeToken}
/>
</div>
)
}
export default (props: Props) => {
return (
<SelectedAccount { ...props }>
<Summary { ...props} />
</SelectedAccount>
);
}
};
export default (props: Props) => (
<SelectedAccount {...props}>
<Summary {...props} />
</SelectedAccount>
);

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React from 'react';
import ColorHash from 'color-hash';
@ -16,40 +16,39 @@ type Props = {
}
const SummaryTokens = (props: Props) => {
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 tokens = props.tokens.map((token, index) => {
let iconColor = {
const iconColor = {
color: textColor.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 balance: string = new BigNumber(token.balance).minus(pendingAmount).toString(10);
return (
<div key={ index } className="token">
<div className="icon" style={ iconColor }>
<div key={index} className="token">
<div className="icon" style={iconColor}>
<div className="icon-inner">
<ScaleText widthOnly><p>{ token.symbol }</p></ScaleText>
</div>
</div>
<div className="name">{ token.name }</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>
)
);
});
return (
<div>
{ tokens }
</div>
)
}
);
};
export default SummaryTokens;

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { PureComponent } from 'react';
import { Link, NavLink } from 'react-router-dom';
@ -14,7 +14,6 @@ import type { Props } from './index';
import type { TrezorDevice, Accounts } from '~/flowtype';
const AccountSelection = (props: Props): ?React$Element<string> => {
const selected = props.wallet.selectedDevice;
if (!selected) return null;
@ -30,11 +29,11 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
const deviceAccounts: Accounts = findDeviceAccounts(accounts, selected, location.state.network);
let selectedAccounts = deviceAccounts.map((account, i) => {
// const url: string = `${baseUrl}/network/${location.state.network}/account/${i}`;
const url: string = location.pathname.replace(/account+\/([0-9]*)/, `account/${i}`);
let balance: string = 'Loading...';
if (account.balance !== '') {
const pending = stateUtils.getAccountPendingTx(props.pending, account);
@ -44,35 +43,35 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
if (fiatRate) {
const accountBalance = new BigNumber(availableBalance);
const fiat = accountBalance.times(fiatRate.value).toFixed(2);
balance = `${ availableBalance } ${ selectedCoin.symbol } / $${ fiat }`;
balance = `${availableBalance} ${selectedCoin.symbol} / $${fiat}`;
} else {
balance = `${ availableBalance } ${ selectedCoin.symbol }`;
balance = `${availableBalance} ${selectedCoin.symbol}`;
}
}
return (
<NavLink key={i} activeClassName="selected" className="account" to={ url }>
{ `Account #${(account.index + 1 )}` }
<span>{ account.loaded ? balance : "Loading..." }</span>
<NavLink key={i} activeClassName="selected" className="account" to={url}>
{ `Account #${(account.index + 1)}` }
<span>{ account.loaded ? balance : 'Loading...' }</span>
</NavLink>
)
);
});
if (selectedAccounts.length < 1) {
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 = (
<NavLink activeClassName="selected" className="account" to={ url }>
<NavLink activeClassName="selected" className="account" to={url}>
Account #1
<span>Loading...</span>
</NavLink>
)
);
}
}
let discoveryStatus = null;
const discovery = props.discovery.find(d => d.deviceState === selected.state && d.network === location.state.network);
if (discovery) {
if (discovery.completed) {
// TODO: add only if last one is not empty
@ -80,7 +79,7 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
const lastAccount = deviceAccounts[deviceAccounts.length - 1];
if (lastAccount && (new BigNumber(lastAccount.balance).greaterThan(0) || lastAccount.nonce > 0)) {
discoveryStatus = (
<div className="add-account" onClick={ props.addAccount }>
<div className="add-account" onClick={props.addAccount}>
Add account
</div>
);
@ -89,24 +88,24 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
<div className="aside-tooltip-wrapper">
To add a new account, last account must have some transactions.
</div>
)
);
discoveryStatus = (
<Tooltip
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
overlay={ tooltip }
placement="top">
<div className="add-account disabled">
arrowContent={<div className="rc-tooltip-arrow-inner" />}
overlay={tooltip}
placement="top"
>
<div className="add-account disabled">
Add account
</div>
</div>
</Tooltip>
);
}
} else if (!selected.connected || !selected.available) {
discoveryStatus = (
<div className="discovery-status">
Accounts could not be loaded
<span>{ `Connect ${ selected.instanceLabel } device` }</span>
<span>{ `Connect ${selected.instanceLabel} device` }</span>
</div>
);
} else {
@ -118,12 +117,12 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
}
}
let backButton = null;
if (selectedCoin) {
backButton = (
<NavLink to={ baseUrl } className={ `back ${ selectedCoin.network }` }>
<span className={ selectedCoin.network }>{ selectedCoin.name }</span>
<NavLink to={baseUrl} className={`back ${selectedCoin.network}`}>
<span className={selectedCoin.network}>{ selectedCoin.name }</span>
</NavLink>
);
}
@ -137,6 +136,6 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
{ discoveryStatus }
</section>
);
}
};
export default AccountSelection;

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

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

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

@ -1,5 +1,5 @@
/* @flow */
'use strict';
// 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> {
// Class variables.
currentScrollY: number = 0;
lastKnownScrollY: number = 0;
topOffset: number = 0;
firstRender: boolean = false;
framePending: boolean = false;
stickToBottom: boolean = false;
top: number = 0;
aside: ?HTMLElement;
wrapper: ?HTMLElement;
subscribers = [];
handleResize = (event: Event) => {
@ -45,7 +52,7 @@ export default class StickyContainer extends React.PureComponent<Props> {
if (!wrapper || !aside) return;
const bottom: ?HTMLElement = wrapper.querySelector('.sticky-bottom');
if (!bottom) return;
const viewportHeight: number = getViewportHeight();
const bottomBounds = bottom.getBoundingClientRect();
const asideBounds = aside.getBoundingClientRect();
@ -55,12 +62,12 @@ export default class StickyContainer extends React.PureComponent<Props> {
if (asideBounds.top < 0) {
wrapper.classList.add('fixed');
let maxTop : number= 1;
let maxTop: number = 1;
if (wrapperBounds.height > viewportHeight) {
const bottomOutOfBounds: boolean = (bottomBounds.bottom <= viewportHeight && scrollDirection === 'down');
const topOutOfBounds: boolean = (wrapperBounds.top > 0 && scrollDirection === 'up');
if (!bottomOutOfBounds && !topOutOfBounds) {
this.topOffset += scrollDirection === 'down' ? - distanceScrolled : distanceScrolled;
this.topOffset += scrollDirection === 'down' ? -distanceScrolled : distanceScrolled;
}
maxTop = viewportHeight - wrapperBounds.height;
}
@ -68,26 +75,23 @@ export default class StickyContainer extends React.PureComponent<Props> {
if (this.topOffset > 0) this.topOffset = 0;
if (maxTop < 0 && this.topOffset < maxTop) this.topOffset = maxTop;
wrapper.style.top = `${this.topOffset}px`;
} else {
wrapper.classList.remove('fixed');
wrapper.style.top = `0px`;
wrapper.style.top = '0px';
this.topOffset = 0;
}
if (wrapperBounds.height > viewportHeight) {
wrapper.classList.remove('fixed-bottom');
} else {
if (wrapper.classList.contains('fixed-bottom')) {
if (bottomBounds.top < wrapperBounds.bottom - bottomBounds.height) {
wrapper.classList.remove('fixed-bottom');
}
} else if (bottomBounds.bottom < viewportHeight) {
wrapper.classList.add('fixed-bottom');
} else if (wrapper.classList.contains('fixed-bottom')) {
if (bottomBounds.top < wrapperBounds.bottom - bottomBounds.height) {
wrapper.classList.remove('fixed-bottom');
}
} else if (bottomBounds.bottom < viewportHeight) {
wrapper.classList.add('fixed-bottom');
}
aside.style.minHeight = `${ wrapperBounds.height }px`;
aside.style.minHeight = `${wrapperBounds.height}px`;
}
update = () => {
@ -102,7 +106,7 @@ export default class StickyContainer extends React.PureComponent<Props> {
window.addEventListener('resize', this.handleScroll);
raf(this.update);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
window.removeEventListener('resize', this.handleScroll);
@ -126,19 +130,20 @@ export default class StickyContainer extends React.PureComponent<Props> {
render() {
return (
<aside
ref={ node => this.aside = node }
onScroll={this.handleScroll}
onTouchStart={this.handleScroll}
onTouchMove={this.handleScroll}
onTouchEnd={this.handleScroll}
>
<div
className="sticky-container"
ref={ node => this.wrapper = node }>
{ this.props.children }
</div>
</aside>
<aside
ref={node => this.aside = node}
onScroll={this.handleScroll}
onTouchStart={this.handleScroll}
onTouchMove={this.handleScroll}
onTouchEnd={this.handleScroll}
>
<div
className="sticky-container"
ref={node => this.wrapper = node}
>
{ this.props.children }
</div>
</aside>
);
}
}

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
@ -15,7 +15,7 @@ import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from '~/flowtype';
type OwnProps = {
}
type StateProps = {
@ -43,35 +43,31 @@ type DispatchProps = {
export type Props = StateProps & DispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => {
return {
connect: state.connect,
accounts: state.accounts,
router: state.router,
deviceDropdownOpened: state.wallet.dropdownOpened,
fiat: state.fiat,
localStorage: state.localStorage,
discovery: state.discovery,
wallet: state.wallet,
devices: state.devices,
pending: state.pending,
};
}
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
connect: state.connect,
accounts: state.accounts,
router: state.router,
deviceDropdownOpened: state.wallet.dropdownOpened,
fiat: state.fiat,
localStorage: state.localStorage,
discovery: state.discovery,
wallet: state.wallet,
devices: state.devices,
pending: state.pending,
});
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => {
return {
//onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch),
toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch),
addAccount: bindActionCreators(TrezorConnectActions.addAccount, dispatch),
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
forgetDevice: bindActionCreators(TrezorConnectActions.forget, dispatch),
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch),
onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch),
};
}
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
//onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch),
toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch),
addAccount: bindActionCreators(TrezorConnectActions.addAccount, dispatch),
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
forgetDevice: bindActionCreators(TrezorConnectActions.forget, dispatch),
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch),
onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch),
});
// export default connect(mapStateToProps, mapDispatchToProps)(Aside);
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Aside)
connect(mapStateToProps, mapDispatchToProps)(Aside),
);

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import * as React from 'react';
import { bindActionCreators } from 'redux';
@ -28,43 +28,37 @@ type ContentProps = {
}
const Content = (props: ContentProps) => {
return (
<article>
<nav>
<Route path="/device/:device/network/:network/account/:account" component={ AccountTabs } />
<Route path="/device/:device/device-settings" component={ DeviceSettingsTabs } />
</nav>
<Notifications />
<Log />
{ props.children }
<Footer />
</article>
);
}
const Content = (props: ContentProps) => (
<article>
<nav>
<Route path="/device/:device/network/:network/account/:account" component={AccountTabs} />
<Route path="/device/:device/device-settings" component={DeviceSettingsTabs} />
</nav>
<Notifications />
<Log />
{ props.children }
<Footer />
</article>
);
const Wallet = (props: WalletContainerProps) => {
return (
<div className="app">
<Header />
{/* <div>{ props.wallet.online ? "ONLINE" : "OFFLINE" }</div> */}
<main>
<AsideContainer />
<Content>
{ props.children }
</Content>
</main>
<ModalContainer />
</div>
);
}
const Wallet = (props: WalletContainerProps) => (
<div className="app">
<Header />
{/* <div>{ props.wallet.online ? "ONLINE" : "OFFLINE" }</div> */}
<main>
<AsideContainer />
<Content>
{ props.children }
</Content>
</main>
<ModalContainer />
</div>
);
const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State, own: {}): WalletContainerProps => {
return {
wallet: state.wallet
};
}
const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State, own: {}): WalletContainerProps => ({
wallet: state.wallet,
});
export default withRouter(
connect(mapStateToProps, null)(Wallet)
connect(mapStateToProps, null)(Wallet),
);

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
@ -8,44 +8,40 @@ import { Notification } from '~/js/components/common/Notification';
import * as TrezorConnectActions from '~/js/actions/TrezorConnectActions';
import type { State, Dispatch } from '~/flowtype';
type Props = {
acquiring: boolean;
acquireDevice: typeof TrezorConnectActions.acquire
}
const Acquire = (props: Props) => {
const actions = props.acquiring ? [] : [
{
label: 'Acquire device',
callback: () => {
props.acquireDevice()
}
}
props.acquireDevice();
},
},
];
return (
<section className="acquire">
<Notification
<Notification
title="Device is used in other window"
message="Do you want to use your device in this window?"
className="info"
cancelable={ false }
actions={ actions }
cancelable={false}
actions={actions}
/>
</section>
);
}
};
export default connect(
(state: State) => {
return {
acquiring: state.connect.acquiring
};
},
(dispatch: Dispatch) => {
return {
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
};
}
export default connect(
(state: State) => ({
acquiring: state.connect.acquiring,
}),
(dispatch: Dispatch) => ({
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
}),
)(Acquire);

@ -1,19 +1,17 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
const Bootloader = () => {
return (
<section className="device-settings">
<div className="row">
<h2>Your device is in firmware update mode</h2>
<p>Please re-connect it</p>
</div>
</section>
);
}
const Bootloader = () => (
<section className="device-settings">
<div className="row">
<h2>Your device is in firmware update mode</h2>
<p>Please re-connect it</p>
</div>
</section>
);
export default connect(null, null)(Bootloader);

@ -1,22 +1,20 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import DashboardImg from '~/images/dashboard.png';
const Dashboard = () => {
return (
<section className="dashboard">
<h2>Dashboard</h2>
<div className="row">
<h2>Please select your coin</h2>
<p>You will gain access to recieving &amp; sending selected coin</p>
<img src={ DashboardImg } height="34" width="auto" alt="Dashboard" />
</div>
</section>
);
}
const Dashboard = () => (
<section className="dashboard">
<h2>Dashboard</h2>
<div className="row">
<h2>Please select your coin</h2>
<p>You will gain access to recieving &amp; sending selected coin</p>
<img src={DashboardImg} height="34" width="auto" alt="Dashboard" />
</div>
</section>
);
export default connect(null, null)(Dashboard);

@ -1,20 +1,18 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
export const DeviceSettings = () => {
return (
<section className="device-settings">
<div className="row">
<h2>Device settings is under construction</h2>
<p>Please use Bitcoin wallet interface to change your device settings</p>
<a className="button" href="https://wallet.trezor.io/">Take me to the Bitcoin wallet</a>
</div>
</section>
);
}
export const DeviceSettings = () => (
<section className="device-settings">
<div className="row">
<h2>Device settings is under construction</h2>
<p>Please use Bitcoin wallet interface to change your device settings</p>
<a className="button" href="https://wallet.trezor.io/">Take me to the Bitcoin wallet</a>
</div>
</section>
);
export default connect(null, null)(DeviceSettings);

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

@ -1,20 +1,18 @@
/* @flow */
'use strict';
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
const Initialize = () => {
return (
<section className="device-settings">
<div className="row">
<h2>Your device is in not initialized</h2>
<p>Please use Bitcoin wallet interface to start initialization process</p>
<a className="button" href="https://wallet.trezor.io/">Take me to the Bitcoin wallet</a>
</div>
</section>
);
}
const Initialize = () => (
<section className="device-settings">
<div className="row">
<h2>Your device is in not initialized</h2>
<p>Please use Bitcoin wallet interface to start initialization process</p>
<a className="button" href="https://wallet.trezor.io/">Take me to the Bitcoin wallet</a>
</div>
</section>
);
export default connect(null, null)(Initialize);

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

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

@ -1,15 +1,15 @@
/* @flow */
'use strict';
import * as CONNECT from '../actions/constants/TrezorConnect';
import * as WALLET from '../actions/constants/wallet';
import * as ACCOUNT from '../actions/constants/account';
import type { Action, TrezorDevice } from '~/flowtype';
import type {
import type {
AccountCreateAction,
AccountSetBalanceAction,
AccountSetNonceAction
AccountSetNonceAction,
} from '../actions/AccountsActions';
export type Account = {
@ -28,20 +28,16 @@ export type State = Array<Account>;
const initialState: State = [];
export const findAccount = (state: State, index: number, deviceState: string, network: string): ?Account => {
return state.find(a => a.deviceState === deviceState && a.index === index && a.network === network);
}
export const findAccount = (state: State, index: number, deviceState: string, network: string): ?Account => state.find(a => a.deviceState === deviceState && a.index === index && a.network === network);
export const findDeviceAccounts = (state: State, device: TrezorDevice, network: string): Array<Account> => {
if (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 && addr.network === network);
}
}
return state.filter(addr => addr.deviceState === device.state);
};
const createAccount = (state: State, action: AccountCreateAction): State => {
// TODO check with device_id
// 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);
@ -60,69 +56,64 @@ const createAccount = (state: State, action: AccountCreateAction): State => {
address: action.address,
balance: '0',
nonce: 0,
}
};
const newState: State = [ ...state ];
const newState: State = [...state];
newState.push(account);
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 => {
let newState: State = [ ...state ];
devices.forEach(d => {
let newState: State = [...state];
devices.forEach((d) => {
newState = removeAccounts(newState, d);
});
return newState;
}
};
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 newState: State = [ ...state ];
const newState: State = [...state];
newState[index].loaded = true;
newState[index].balance = action.balance;
return newState;
}
};
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 newState: State = [ ...state ];
const newState: State = [...state];
newState[index].loaded = true;
newState[index].nonce = action.nonce;
return newState;
}
};
export default (state: State = initialState, action: Action): State => {
switch (action.type) {
case ACCOUNT.CREATE :
case ACCOUNT.CREATE:
return createAccount(state, action);
case CONNECT.FORGET :
case CONNECT.FORGET_SINGLE :
case CONNECT.FORGET:
case CONNECT.FORGET_SINGLE:
return removeAccounts(state, action.device);
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA :
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA:
return clear(state, action.devices);
//case CONNECT.FORGET_SINGLE :
// return forgetAccounts(state, action);
case ACCOUNT.SET_BALANCE :
case ACCOUNT.SET_BALANCE:
return setBalance(state, action);
case ACCOUNT.SET_NONCE :
case ACCOUNT.SET_NONCE:
return setNonce(state, action);
case ACCOUNT.FROM_STORAGE :
case ACCOUNT.FROM_STORAGE:
return action.payload;
default:
return state;
}
}
};

@ -1,19 +1,18 @@
/* @flow */
'use strict';
import { TRANSPORT, DEVICE } from 'trezor-connect';
import type { Device } from 'trezor-connect';
import * as CONNECT from '../actions/constants/TrezorConnect';
import * as WALLET from '../actions/constants/wallet';
import type { Action, TrezorDevice } from '~/flowtype';
import type { Device } from 'trezor-connect';
export type State = Array<TrezorDevice>;
const initialState: State = [];
const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): TrezorDevice => {
// do not merge if passphrase protection was changed
// if (upcoming.features && current.features) {
// 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;
if (upcoming.label !== current.label) {
instanceLabel = upcoming.label
instanceLabel = upcoming.label;
if (typeof current.instance === 'number') {
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,
state: current.state,
ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts,
}
};
// corner-case: trying to merge unacquired device with acquired
// make sure that sensitive fields will not be changed and device will remain acquired
if (upcoming.unacquired && current.state) {
@ -52,10 +51,9 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T
}
return dev;
}
};
const addDevice = (state: State, device: Device): State => {
let affectedDevices: Array<TrezorDevice> = [];
let otherDevices: Array<TrezorDevice> = [];
if (!device.features) {
@ -84,9 +82,9 @@ const addDevice = (state: State, device: Device): State => {
instanceLabel: device.label,
instanceName: null,
ts: new Date().getTime(),
}
};
if (affectedDevices.length > 0 ) {
if (affectedDevices.length > 0) {
// check if freshly added device has different "passphrase_protection" settings
// let hasDifferentPassphraseSettings: boolean = false;
@ -114,26 +112,21 @@ const addDevice = (state: State, device: Device): State => {
// changedDevices.push(newDevice);
// }
const changedDevices: Array<TrezorDevice> = affectedDevices.filter(d => d.features && d.features.passphrase_protection === device.features.passphrase_protection).map(d => {
return mergeDevices(d, { ...device, connected: true, available: true } );
});
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 }));
if (changedDevices.length !== affectedDevices.length) {
changedDevices.push(newDevice);
}
return otherDevices.concat(changedDevices);
} else {
return otherDevices.concat([newDevice]);
}
}
return otherDevices.concat([newDevice]);
};
const duplicate = (state: State, device: TrezorDevice): State => {
if (!device.features) return state;
const newState: State = [ ...state ];
const newState: State = [...state];
const instance: number = getNewInstance(state, device);
@ -143,25 +136,22 @@ const duplicate = (state: State, device: TrezorDevice): State => {
remember: false,
state: null,
// 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(),
}
};
newState.push(newDevice);
return newState;
}
};
const changeDevice = (state: State, device: Device): State => {
// change only acquired devices
if (!device.features) return state;
// find devices with the same device_id and passphrase_protection settings
// or devices with the same path (TODO: should be that way?)
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.path.length > 0 && d.path === device.path)
);
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.path.length > 0 && d.path === device.path));
const otherDevices: Array<TrezorDevice> = state.filter(d => affectedDevices.indexOf(d) === -1);
@ -172,7 +162,7 @@ const changeDevice = (state: State, device: Device): State => {
}
return state;
}
};
const authDevice = (state: State, device: TrezorDevice, deviceState: string): State => {
const affectedDevice: ?TrezorDevice = state.find(d => d.path === device.path && d.instance === device.instance);
@ -183,47 +173,39 @@ const authDevice = (state: State, device: TrezorDevice, deviceState: string): St
return otherDevices.concat([affectedDevice]);
}
return state;
}
};
// Transform JSON form local storage into State
const devicesFromStorage = (devices: Array<TrezorDevice>): State => {
return devices.map( (d: TrezorDevice) => {
return {
...d,
connected: false,
available: false,
path: '',
acquiring: false,
featuresNeedsReload: false,
isUsedElsewhere: false
}
});
}
const devicesFromStorage = (devices: Array<TrezorDevice>): State => devices.map((d: TrezorDevice) => ({
...d,
connected: false,
available: false,
path: '',
acquiring: false,
featuresNeedsReload: false,
isUsedElsewhere: false,
}));
// Remove all device reference from State
const forgetDevice = (state: State, device: TrezorDevice): State => {
// 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));
}
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 single device reference from State
const forgetSingleDevice = (state: State, device: TrezorDevice): State => {
// remove only one instance (called from Aside button)
const newState: State = [ ...state ];
const newState: State = [...state];
newState.splice(newState.indexOf(device), 1);
return newState;
}
};
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 otherDevices: State = state.filter(d => affectedDevices.indexOf(d) === -1);
if (affectedDevices.length > 0) {
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.available = false;
d.isUsedElsewhere = false;
@ -234,74 +216,67 @@ const disconnectDevice = (state: State, device: Device): State => {
}
return state;
}
};
const onSelectedDevice = (state: State, device: ?TrezorDevice): State => {
if (device) {
const otherDevices: Array<TrezorDevice> = state.filter(d => d !== device);
return otherDevices.concat([ { ...device, ts: new Date().getTime() } ]);
return otherDevices.concat([{ ...device, ts: new Date().getTime() }]);
}
return state;
}
};
export default function devices(state: State = initialState, action: Action): State {
switch (action.type) {
case CONNECT.DEVICE_FROM_STORAGE :
case CONNECT.DEVICE_FROM_STORAGE:
return devicesFromStorage(action.payload);
case CONNECT.DUPLICATE :
case CONNECT.DUPLICATE:
return duplicate(state, action.device);
case CONNECT.AUTH_DEVICE :
case CONNECT.AUTH_DEVICE:
return authDevice(state, action.device, action.state);
case CONNECT.REMEMBER :
return changeDevice(state, { ...action.device, path: '', remember: true } );
case CONNECT.FORGET :
case CONNECT.REMEMBER:
return changeDevice(state, { ...action.device, path: '', remember: true });
case CONNECT.FORGET:
return forgetDevice(state, action.device);
case CONNECT.FORGET_SINGLE :
case CONNECT.FORGET_SINGLE:
return forgetSingleDevice(state, action.device);
case DEVICE.CONNECT :
case DEVICE.CONNECT_UNACQUIRED :
case DEVICE.CONNECT:
case DEVICE.CONNECT_UNACQUIRED:
return addDevice(state, action.device);
case DEVICE.CHANGED :
return changeDevice(state, { ...action.device, connected: true, available: true });
case DEVICE.CHANGED:
return changeDevice(state, { ...action.device, connected: true, available: true });
// TODO: check if available will propagate to unavailable
case DEVICE.DISCONNECT :
case DEVICE.DISCONNECT_UNACQUIRED :
case DEVICE.DISCONNECT:
case DEVICE.DISCONNECT_UNACQUIRED:
return disconnectDevice(state, action.device);
case WALLET.SET_SELECTED_DEVICE :
case WALLET.SET_SELECTED_DEVICE:
return onSelectedDevice(state, action.device);
default:
return state;
}
}
// UTILS
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)
.sort((a, b) => {
if (!a.instance) {
return -1;
} else {
.sort((a, b) => {
if (!a.instance) {
return -1;
}
return !b.instance || a.instance > b.instance ? 1 : -1;
}
});
});
const instance: number = affectedDevices.reduce((inst, dev) => {
return dev.instance ? dev.instance + 1 : inst + 1;
}, 0);
const instance: number = affectedDevices.reduce((inst, dev) => (dev.instance ? dev.instance + 1 : inst + 1), 0);
return instance;
}
};

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import HDKey from 'hdkey';
@ -9,16 +9,16 @@ import * as CONNECT from '../actions/constants/TrezorConnect';
import * as WALLET from '../actions/constants/wallet';
import type { Action, TrezorDevice } from '~/flowtype';
import type {
import type {
DiscoveryStartAction,
DiscoveryWaitingAction,
DiscoveryStopAction,
DiscoveryCompleteAction
DiscoveryCompleteAction,
} from '../actions/DiscoveryActions';
import type {
AccountCreateAction
} from '../actions/AccountsActions'
AccountCreateAction,
} from '../actions/AccountsActions';
export type Discovery = {
network: string;
@ -37,13 +37,11 @@ export type Discovery = {
export type State = Array<Discovery>;
const initialState: State = [];
const findIndex = (state: State, network: string, deviceState: string): number => {
return state.findIndex(d => d.network === network && d.deviceState === deviceState);
}
const findIndex = (state: State, network: string, deviceState: string): number => state.findIndex(d => d.network === network && d.deviceState === deviceState);
const start = (state: State, action: DiscoveryStartAction): State => {
const deviceState: string = action.device.state || '0';
const hdKey: HDKey = new HDKey();
const hdKey: HDKey = new HDKey();
hdKey.publicKey = new Buffer(action.publicKey, 'hex');
hdKey.chainCode = new Buffer(action.chainCode, 'hex');
const instance: Discovery = {
@ -58,9 +56,9 @@ const start = (state: State, action: DiscoveryStartAction): State => {
completed: false,
waitingForDevice: false,
waitingForBackend: false,
}
};
const newState: State = [ ...state ];
const newState: State = [...state];
const index: number = findIndex(state, action.network, deviceState);
if (index >= 0) {
newState[index] = instance;
@ -68,47 +66,44 @@ const start = (state: State, action: DiscoveryStartAction): State => {
newState.push(instance);
}
return newState;
}
};
const complete = (state: State, action: DiscoveryCompleteAction): State => {
const index: number = findIndex(state, action.network, action.device.state || '0');
const newState: State = [ ...state ];
const newState: State = [...state];
newState[index].completed = true;
return newState;
}
};
const accountCreate = (state: State, action: AccountCreateAction): State => {
const index: number = findIndex(state, action.network, action.device.state || '0');
const newState: State = [ ...state ];
const newState: State = [...state];
newState[index].accountIndex++;
return newState;
}
};
const forgetDiscovery = (state: State, device: TrezorDevice): State => {
return state.filter(d => d.deviceState !== device.state);
}
const forgetDiscovery = (state: State, device: TrezorDevice): State => state.filter(d => d.deviceState !== device.state);
const clear = (state: State, devices: Array<TrezorDevice>): State => {
let newState: State = [ ...state ];
devices.forEach(d => {
let newState: State = [...state];
devices.forEach((d) => {
newState = forgetDiscovery(newState, d);
});
return newState;
}
};
const stop = (state: State, action: DiscoveryStopAction): State => {
const newState: State = [ ...state ];
return newState.map( (d: Discovery) => {
const newState: State = [...state];
return newState.map((d: Discovery) => {
if (d.deviceState === action.device.state && !d.completed) {
d.interrupted = true;
d.waitingForDevice = false;
}
return d;
});
}
};
const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State => {
const deviceState: string = action.device.state || '0';
const instance: Discovery = {
network: action.network,
@ -122,10 +117,10 @@ const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State =
completed: false,
waitingForDevice: true,
waitingForBackend: false,
}
};
const index: number = findIndex(state, action.network, deviceState);
const newState: State = [ ...state ];
const newState: State = [...state];
if (index >= 0) {
newState[index] = instance;
} else {
@ -133,7 +128,7 @@ const waitingForDevice = (state: State, action: DiscoveryWaitingAction): State =
}
return newState;
}
};
const waitingForBackend = (state: State, action: DiscoveryWaitingAction): State => {
const deviceState: string = action.device.state || '0';
@ -148,11 +143,11 @@ const waitingForBackend = (state: State, action: DiscoveryWaitingAction): State
interrupted: false,
completed: false,
waitingForDevice: false,
waitingForBackend: true
}
waitingForBackend: true,
};
const index: number = findIndex(state, action.network, deviceState);
const newState: State = [ ...state ];
const newState: State = [...state];
if (index >= 0) {
newState[index] = instance;
} else {
@ -160,26 +155,25 @@ const waitingForBackend = (state: State, action: DiscoveryWaitingAction): State
}
return newState;
}
};
export default function discovery(state: State = initialState, action: Action): State {
switch (action.type) {
case DISCOVERY.START :
case DISCOVERY.START:
return start(state, action);
case ACCOUNT.CREATE :
case ACCOUNT.CREATE:
return accountCreate(state, action);
case DISCOVERY.STOP :
case DISCOVERY.STOP:
return stop(state, action);
case DISCOVERY.COMPLETE :
case DISCOVERY.COMPLETE:
return complete(state, action);
case DISCOVERY.WAITING_FOR_DEVICE :
case DISCOVERY.WAITING_FOR_DEVICE:
return waitingForDevice(state, action);
case DISCOVERY.WAITING_FOR_BACKEND :
case DISCOVERY.WAITING_FOR_BACKEND:
return waitingForBackend(state, action);
case DISCOVERY.FROM_STORAGE :
return action.payload.map(d => {
const hdKey: HDKey = new HDKey();
case DISCOVERY.FROM_STORAGE:
return action.payload.map((d) => {
const hdKey: HDKey = new HDKey();
hdKey.publicKey = new Buffer(d.publicKey, 'hex');
hdKey.chainCode = new Buffer(d.chainCode, 'hex');
return {
@ -188,16 +182,15 @@ export default function discovery(state: State = initialState, action: Action):
interrupted: false,
waitingForDevice: false,
waitingForBackend: false,
}
})
case CONNECT.FORGET :
case CONNECT.FORGET_SINGLE :
};
});
case CONNECT.FORGET:
case CONNECT.FORGET_SINGLE:
return forgetDiscovery(state, action.device);
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA :
case WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA:
return clear(state, action.devices);
default:
return state;
}
}

@ -1,5 +1,5 @@
/* @flow */
'use strict';
import { RATE_UPDATE } from '../services/CoinmarketcapService';
@ -14,29 +14,26 @@ export type Fiat = {
export const initialState: Array<Fiat> = [];
const update = (state: Array<Fiat>, action: FiatRateAction): Array<Fiat> => {
const newState: Array<Fiat> = [ ...state ];
const newState: Array<Fiat> = [...state];
const exists: ?Fiat = newState.find(f => f.network === action.network);
if (exists) {
exists.value = action.rate.price_usd;
} else {
newState.push({
network: action.network,
value: action.rate.price_usd
})
value: action.rate.price_usd,
});
}
return newState;
}
};
export default (state: Array<Fiat> = initialState, action: Action): Array<Fiat> => {
switch (action.type) {
case RATE_UPDATE :
case RATE_UPDATE:
return update(state, action);
default:
return state;
}
}
};

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

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

Loading…
Cancel
Save