mirror of
https://github.com/trezor/trezor-wallet
synced 2025-02-10 23:22:40 +00:00
Merge pull request #1 from satoshilabs/add-eslint
Add eslint + first tests
This commit is contained in:
commit
d7f8571974
13
.babelrc
13
.babelrc
@ -14,6 +14,15 @@
|
|||||||
"polyfill": false,
|
"polyfill": false,
|
||||||
"regenerator": true
|
"regenerator": true
|
||||||
}],
|
}],
|
||||||
"./src/babel/babel-plugin-import.js"
|
["babel-plugin-root-import", {
|
||||||
]
|
"rootPathSuffix": "./src",
|
||||||
|
"rootPathPrefix": "~"
|
||||||
|
}],
|
||||||
|
"babel-plugin-styled-components"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"test": {
|
||||||
|
"presets": ["jest"]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
solidity
|
55
.eslintrc
Normal file
55
.eslintrc
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"eslint-config-airbnb",
|
||||||
|
"plugin:flowtype/recommended",
|
||||||
|
"plugin:jest/recommended"
|
||||||
|
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"jest": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"react/jsx-one-expression-per-line": 0,
|
||||||
|
"react/jsx-indent": [2, 4],
|
||||||
|
"react/jsx-indent-props": [2, 4],
|
||||||
|
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"]}],
|
||||||
|
"indent": [2, 4, { "SwitchCase": 1 }],
|
||||||
|
"no-confusing-arrow": [2,{ "allowParens": true }],
|
||||||
|
"no-console": 0,
|
||||||
|
"no-alert": 0,
|
||||||
|
"no-prototype-builtins": 0,
|
||||||
|
"new-cap": 0,
|
||||||
|
"max-len": 0,
|
||||||
|
"eol-last": 0,
|
||||||
|
"spaced-comment": 0
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"react",
|
||||||
|
"import",
|
||||||
|
"jest",
|
||||||
|
"flowtype"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"import/parser": "babel-eslint",
|
||||||
|
"import/resolver": {
|
||||||
|
"node": {
|
||||||
|
"moduleDirectory": [
|
||||||
|
"src",
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"import/ignore": [
|
||||||
|
"\\.(scss|less|css)$"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 7,
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true,
|
||||||
|
"experimentalObjectRestSpread": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -18,3 +18,5 @@ logs
|
|||||||
.yarnclean
|
.yarnclean
|
||||||
|
|
||||||
_old
|
_old
|
||||||
|
|
||||||
|
coverage
|
12
jest.config.js
Normal file
12
jest.config.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module.exports = {
|
||||||
|
rootDir: './src',
|
||||||
|
collectCoverage: true,
|
||||||
|
testURL: 'http://localhost',
|
||||||
|
|
||||||
|
modulePathIgnorePatterns: [
|
||||||
|
'node_modules',
|
||||||
|
],
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'js/utils/**.js',
|
||||||
|
],
|
||||||
|
};
|
36
package.json
36
package.json
@ -18,14 +18,22 @@
|
|||||||
"dev": "webpack-dev-server --config ./webpack/config.dev.babel.js --mode development",
|
"dev": "webpack-dev-server --config ./webpack/config.dev.babel.js --mode development",
|
||||||
"build": "rm -rf build && webpack --config ./webpack/config.prod.babel.js --progress",
|
"build": "rm -rf build && webpack --config ./webpack/config.prod.babel.js --progress",
|
||||||
"flow": "flow check src/js",
|
"flow": "flow check src/js",
|
||||||
"test": ""
|
"lint": "npx eslint ./src ./webpack",
|
||||||
|
"test-unit": "jest",
|
||||||
|
"test-unit:watch": "jest -o --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel": "^6.23.0",
|
||||||
|
"babel-core": "^6.26.3",
|
||||||
"color-hash": "^1.0.3",
|
"color-hash": "^1.0.3",
|
||||||
|
"copy-webpack-plugin": "^4.5.2",
|
||||||
"ethereumjs-tx": "^1.3.3",
|
"ethereumjs-tx": "^1.3.3",
|
||||||
"ethereumjs-units": "^0.2.0",
|
"ethereumjs-units": "^0.2.0",
|
||||||
"ethereumjs-util": "^5.1.4",
|
"ethereumjs-util": "^5.1.4",
|
||||||
"hdkey": "^0.8.0",
|
"hdkey": "^0.8.0",
|
||||||
|
"html-webpack-plugin": "^3.2.0",
|
||||||
|
"mini-css-extract-plugin": "^0.4.1",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
"raf": "^3.4.0",
|
"raf": "^3.4.0",
|
||||||
"raven-js": "^3.22.3",
|
"raven-js": "^3.22.3",
|
||||||
"rc-tooltip": "^3.7.0",
|
"rc-tooltip": "^3.7.0",
|
||||||
@ -44,31 +52,41 @@
|
|||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-raven-middleware": "^1.2.0",
|
"redux-raven-middleware": "^1.2.0",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
|
"styled-components": "^3.3.3",
|
||||||
"trezor-connect": "5.0.13",
|
"trezor-connect": "5.0.13",
|
||||||
"web3": "^0.19.0"
|
"web3": "^0.19.0",
|
||||||
|
"webpack": "^4.16.3",
|
||||||
|
"whatwg-fetch": "^2.0.4",
|
||||||
|
"yarn-run-all": "^3.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "^6.24.1",
|
"babel-cli": "^6.24.1",
|
||||||
"babel-loader": "7.1.4",
|
"babel-eslint": "^8.2.6",
|
||||||
"babel-plugin-root-import": "^5.1.0",
|
"babel-loader": "^7.1.5",
|
||||||
|
"babel-plugin-root-import": "^6.1.0",
|
||||||
|
"babel-plugin-styled-components": "^1.5.1",
|
||||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
||||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
"babel-preset-env": "^1.6.0",
|
"babel-preset-env": "^1.6.0",
|
||||||
|
"babel-preset-jest": "^23.2.0",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"copy-webpack-plugin": "4.5.1",
|
|
||||||
"css-loader": "0.28.11",
|
"css-loader": "0.28.11",
|
||||||
|
"eslint": "^4",
|
||||||
|
"eslint-config-airbnb": "^17.0.0",
|
||||||
|
"eslint-plugin-flowtype": "^2.50.0",
|
||||||
|
"eslint-plugin-import": "^2.13.0",
|
||||||
|
"eslint-plugin-jest": "^21.18.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.1.1",
|
||||||
|
"eslint-plugin-react": "^7.10.0",
|
||||||
"file-loader": "1.1.11",
|
"file-loader": "1.1.11",
|
||||||
"flow-bin": "0.72.0",
|
"flow-bin": "0.72.0",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"jest": "^23.4.2",
|
||||||
"less": "^3.0.1",
|
"less": "^3.0.1",
|
||||||
"less-loader": "4.1.0",
|
"less-loader": "4.1.0",
|
||||||
"mini-css-extract-plugin": "^0.4.0",
|
|
||||||
"webpack": "^4.8.3",
|
|
||||||
"webpack-cli": "^2.1.3",
|
"webpack-cli": "^2.1.3",
|
||||||
"webpack-dev-server": "^3.1.4",
|
"webpack-dev-server": "^3.1.4",
|
||||||
"whatwg-fetch": "^2.0.3",
|
|
||||||
"yargs": "11.0.0"
|
"yargs": "11.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
// inspired by:
|
|
||||||
// https://github.com/entwicklerstube/babel-plugin-root-import/
|
|
||||||
// Simple plugin allows es6 import from '~/' which is src root
|
|
||||||
|
|
||||||
const cwd = process.cwd();
|
|
||||||
|
|
||||||
const replacePrefix = (path, opts = [], sourceFile) => {
|
|
||||||
const options = [].concat(opts);
|
|
||||||
if (typeof path === 'string') {
|
|
||||||
if (path.indexOf('~/') === 0) {
|
|
||||||
return path.replace('~/', `${ cwd }/src/`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ImportDeclaration(path, state) {
|
|
||||||
path.node.source.value = replacePrefix(path.node.source.value, state.opts, state.file.opts.filename);
|
|
||||||
},
|
|
||||||
ExportNamedDeclaration(path, state) {
|
|
||||||
if (path.node.source) {
|
|
||||||
path.node.source.value = replacePrefix(path.node.source.value, state.opts, state.file.opts.filename);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ExportAllDeclaration(path, state) {
|
|
||||||
if (path.node.source) {
|
|
||||||
path.node.source.value = replacePrefix(path.node.source.value, state.opts, state.file.opts.filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
'visitor': {
|
|
||||||
Program(path, state) {
|
|
||||||
path.traverse(visitor, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
declare module CSSModule {
|
declare module CSSModule {
|
||||||
declare var exports: { [key: string]: string };
|
declare var exports: { [key: string]: string };
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Store as ReduxStore,
|
Store as ReduxStore,
|
||||||
@ -9,7 +9,7 @@ import type {
|
|||||||
ThunkAction as ReduxThunkAction,
|
ThunkAction as ReduxThunkAction,
|
||||||
AsyncAction as ReduxAsyncAction,
|
AsyncAction as ReduxAsyncAction,
|
||||||
ThunkDispatch as ReduxThunkDispatch,
|
ThunkDispatch as ReduxThunkDispatch,
|
||||||
PlainDispatch as ReduxPlainDispatch
|
PlainDispatch as ReduxPlainDispatch,
|
||||||
} from 'redux';
|
} from 'redux';
|
||||||
|
|
||||||
import type { Reducers, ReducersState } from '~/js/reducers';
|
import type { Reducers, ReducersState } from '~/js/reducers';
|
||||||
|
@ -51,9 +51,7 @@ declare module 'ethereum-types' {
|
|||||||
value?: number
|
value?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
declare export type EthereumSendTransactionT = EthereumSendTransactionOptionsT => Promise<
|
declare export type EthereumSendTransactionT = EthereumSendTransactionOptionsT => Promise<EthereumTransactionHashT>
|
||||||
EthereumTransactionHashT
|
|
||||||
>
|
|
||||||
|
|
||||||
// TODO(mattgstevens): it would be nice to have an Generic type for a Contract instance
|
// TODO(mattgstevens): it would be nice to have an Generic type for a Contract instance
|
||||||
// similar to the EthererumWatchEventT
|
// similar to the EthererumWatchEventT
|
||||||
@ -69,4 +67,3 @@ declare module 'ethereum-types' {
|
|||||||
|
|
||||||
// end contract data
|
// end contract data
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
// flow-typed signature: 59b0c4be0e1408f21e2446be96c79804
|
// flow-typed signature: 59b0c4be0e1408f21e2446be96c79804
|
||||||
// flow-typed version: 9092387fd2/react-redux_v5.x.x/flow_>=v0.54.x
|
// flow-typed version: 9092387fd2/react-redux_v5.x.x/flow_>=v0.54.x
|
||||||
|
|
||||||
import type { Dispatch, Store } from "redux";
|
import type { Dispatch, Store } from 'redux';
|
||||||
|
|
||||||
declare module "react-redux" {
|
declare module 'react-redux' {
|
||||||
/*
|
/*
|
||||||
|
|
||||||
S = State
|
S = State
|
||||||
@ -32,9 +32,7 @@ declare module "react-redux" {
|
|||||||
|
|
||||||
declare type Context = { store: Store<*, *> };
|
declare type Context = { store: Store<*, *> };
|
||||||
|
|
||||||
declare type ComponentWithDefaultProps<DP: {}, P: {}, CP: P> = Class<
|
declare type ComponentWithDefaultProps<DP: {}, P: {}, CP: P> = Class<React$Component<CP>> & { defaultProps: DP };
|
||||||
React$Component<CP>
|
|
||||||
> & { defaultProps: DP };
|
|
||||||
|
|
||||||
declare class ConnectedComponentWithDefaultProps<
|
declare class ConnectedComponentWithDefaultProps<
|
||||||
OP,
|
OP,
|
||||||
@ -55,13 +53,9 @@ declare module "react-redux" {
|
|||||||
state: void
|
state: void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare type ConnectedComponentWithDefaultPropsClass<OP, DP, CP> = Class<
|
declare type ConnectedComponentWithDefaultPropsClass<OP, DP, CP> = Class<ConnectedComponentWithDefaultProps<OP, DP, CP>>;
|
||||||
ConnectedComponentWithDefaultProps<OP, DP, CP>
|
|
||||||
>;
|
|
||||||
|
|
||||||
declare type ConnectedComponentClass<OP, P> = Class<
|
declare type ConnectedComponentClass<OP, P> = Class<ConnectedComponent<OP, P>>;
|
||||||
ConnectedComponent<OP, P>
|
|
||||||
>;
|
|
||||||
|
|
||||||
declare type Connector<OP, P> = (<DP: {}, CP: {}>(
|
declare type Connector<OP, P> = (<DP: {}, CP: {}>(
|
||||||
component: ComponentWithDefaultProps<DP, P, CP>
|
component: ComponentWithDefaultProps<DP, P, CP>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
declare module "react-router-dom" {
|
declare module 'react-router-dom' {
|
||||||
declare export class BrowserRouter extends React$Component<{
|
declare export class BrowserRouter extends React$Component<{
|
||||||
basename?: string,
|
basename?: string,
|
||||||
forceRefresh?: boolean,
|
forceRefresh?: boolean,
|
||||||
|
5
src/flowtype/npm/react-router-redux.js
vendored
5
src/flowtype/npm/react-router-redux.js
vendored
@ -1,10 +1,9 @@
|
|||||||
import type {
|
import type {
|
||||||
RouterHistory,
|
RouterHistory,
|
||||||
Location as RouterLocation
|
Location as RouterLocation,
|
||||||
} from 'react-router';
|
} from 'react-router';
|
||||||
|
|
||||||
declare module "react-router-redux" {
|
declare module 'react-router-redux' {
|
||||||
|
|
||||||
// custom state for location
|
// custom state for location
|
||||||
declare export type LocationState = {[key: string]: string};
|
declare export type LocationState = {[key: string]: string};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
declare module "react-router" {
|
declare module 'react-router' {
|
||||||
// NOTE: many of these are re-exported by react-router-dom and
|
// NOTE: many of these are re-exported by react-router-dom and
|
||||||
// react-router-native, so when making changes, please be sure to update those
|
// react-router-native, so when making changes, please be sure to update those
|
||||||
// as well.
|
// as well.
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
declare module 'redux' {
|
declare module 'redux' {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
S = State
|
S = State
|
||||||
|
@ -124,12 +124,10 @@ declare module 'web3' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*declare module 'web3' {
|
/*declare module 'web3' {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as ACCOUNT from './constants/account';
|
import * as ACCOUNT from './constants/account';
|
||||||
import type { Action, TrezorDevice } from '~/flowtype';
|
import type { Action, TrezorDevice } from '~/flowtype';
|
||||||
@ -41,22 +40,18 @@ export type AccountSetNonceAction = {
|
|||||||
nonce: number
|
nonce: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => {
|
export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => ({
|
||||||
return {
|
|
||||||
type: ACCOUNT.SET_BALANCE,
|
type: ACCOUNT.SET_BALANCE,
|
||||||
address,
|
address,
|
||||||
network,
|
network,
|
||||||
deviceState,
|
deviceState,
|
||||||
balance
|
balance,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const setNonce = (address: string, network: string, deviceState: string, nonce: number): Action => {
|
export const setNonce = (address: string, network: string, deviceState: string, nonce: number): Action => ({
|
||||||
return {
|
|
||||||
type: ACCOUNT.SET_NONCE,
|
type: ACCOUNT.SET_NONCE,
|
||||||
address,
|
address,
|
||||||
network,
|
network,
|
||||||
deviceState,
|
deviceState,
|
||||||
nonce
|
nonce,
|
||||||
}
|
});
|
||||||
}
|
|
@ -1,19 +1,21 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import TrezorConnect from 'trezor-connect';
|
import TrezorConnect from 'trezor-connect';
|
||||||
|
import HDKey from 'hdkey';
|
||||||
|
import EthereumjsUtil from 'ethereumjs-util';
|
||||||
import * as DISCOVERY from './constants/discovery';
|
import * as DISCOVERY from './constants/discovery';
|
||||||
import * as ACCOUNT from './constants/account';
|
import * as ACCOUNT from './constants/account';
|
||||||
import * as TOKEN from './constants/token';
|
import * as TOKEN from './constants/token';
|
||||||
import * as NOTIFICATION from './constants/notification';
|
import * as NOTIFICATION from './constants/notification';
|
||||||
import * as AccountsActions from '../actions/AccountsActions';
|
import * as AccountsActions from './AccountsActions';
|
||||||
|
|
||||||
import HDKey from 'hdkey';
|
|
||||||
import EthereumjsUtil from 'ethereumjs-util';
|
|
||||||
import { getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions';
|
import { getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions';
|
||||||
import { setBalance as setTokenBalance } from './TokenActions';
|
import { setBalance as setTokenBalance } from './TokenActions';
|
||||||
|
|
||||||
import type { ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice } from '~/flowtype';
|
import type {
|
||||||
|
ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice,
|
||||||
|
} from '~/flowtype';
|
||||||
|
|
||||||
import type { Discovery, State } from '../reducers/DiscoveryReducer';
|
import type { Discovery, State } from '../reducers/DiscoveryReducer';
|
||||||
|
|
||||||
export type DiscoveryAction = {
|
export type DiscoveryAction = {
|
||||||
@ -50,64 +52,59 @@ export type DiscoveryCompleteAction = {
|
|||||||
network: string
|
network: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => {
|
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
const selected = getState().wallet.selectedDevice;
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
// TODO: throw error
|
// TODO: throw error
|
||||||
console.error("Start discovery: no selected device", device)
|
console.error('Start discovery: no selected device', device);
|
||||||
return;
|
return;
|
||||||
} else if (selected.path !== device.path) {
|
} if (selected.path !== device.path) {
|
||||||
console.error("Start discovery: requested device is not selected", device, selected)
|
console.error('Start discovery: requested device is not selected', device, selected);
|
||||||
return;
|
return;
|
||||||
} else if (!selected.state) {
|
} if (!selected.state) {
|
||||||
console.warn("Start discovery: Selected device wasn't authenticated yet...")
|
console.warn("Start discovery: Selected device wasn't authenticated yet...");
|
||||||
return;
|
return;
|
||||||
} else if (selected.connected && !selected.available) {
|
} if (selected.connected && !selected.available) {
|
||||||
console.warn("Start discovery: Selected device is unavailable...")
|
console.warn('Start discovery: Selected device is unavailable...');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const web3 = getState().web3.find(w3 => w3.network === network);
|
const web3 = getState().web3.find(w3 => w3.network === network);
|
||||||
if (!web3) {
|
if (!web3) {
|
||||||
console.error("Start discovery: Web3 does not exist", network)
|
console.error('Start discovery: Web3 does not exist', network);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!web3.web3.currentProvider.isConnected()) {
|
if (!web3.web3.currentProvider.isConnected()) {
|
||||||
console.error("Start discovery: Web3 is not connected", network)
|
console.error('Start discovery: Web3 is not connected', network);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.WAITING_FOR_BACKEND,
|
type: DISCOVERY.WAITING_FOR_BACKEND,
|
||||||
device,
|
device,
|
||||||
network
|
network,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const discovery: State = getState().discovery;
|
const discovery: State = getState().discovery;
|
||||||
let discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
|
const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
|
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.WAITING_FOR_DEVICE,
|
type: DISCOVERY.WAITING_FOR_DEVICE,
|
||||||
device,
|
device,
|
||||||
network
|
network,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!discoveryProcess) {
|
if (!discoveryProcess) {
|
||||||
dispatch(begin(device, network));
|
dispatch(begin(device, network));
|
||||||
return;
|
} else if (discoveryProcess.completed && !ignoreCompleted) {
|
||||||
} else {
|
|
||||||
if (discoveryProcess.completed && !ignoreCompleted) {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.COMPLETE,
|
type: DISCOVERY.COMPLETE,
|
||||||
device,
|
device,
|
||||||
network
|
network,
|
||||||
});
|
});
|
||||||
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
|
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
|
||||||
// discovery cycle was interrupted
|
// discovery cycle was interrupted
|
||||||
@ -116,13 +113,9 @@ export const start = (device: TrezorDevice, network: string, ignoreCompleted?: b
|
|||||||
} else {
|
} else {
|
||||||
dispatch(discoverAccount(device, discoveryProcess));
|
dispatch(discoverAccount(device, discoveryProcess));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const begin = (device: TrezorDevice, network: string): AsyncAction => {
|
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
|
const begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const { config } = getState().localStorage;
|
const { config } = getState().localStorage;
|
||||||
const coinToDiscover = config.coins.find(c => c.network === network);
|
const coinToDiscover = config.coins.find(c => c.network === network);
|
||||||
if (!coinToDiscover) return;
|
if (!coinToDiscover) return;
|
||||||
@ -130,7 +123,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.WAITING_FOR_DEVICE,
|
type: DISCOVERY.WAITING_FOR_DEVICE,
|
||||||
device,
|
device,
|
||||||
network
|
network,
|
||||||
});
|
});
|
||||||
|
|
||||||
// get xpub from TREZOR
|
// get xpub from TREZOR
|
||||||
@ -138,7 +131,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
|
|||||||
device: {
|
device: {
|
||||||
path: device.path,
|
path: device.path,
|
||||||
instance: device.instance,
|
instance: device.instance,
|
||||||
state: device.state
|
state: device.state,
|
||||||
},
|
},
|
||||||
path: coinToDiscover.bip44,
|
path: coinToDiscover.bip44,
|
||||||
keepSession: true, // acquire and hold session
|
keepSession: true, // acquire and hold session
|
||||||
@ -148,7 +141,7 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
|
|||||||
// handle TREZOR response error
|
// handle TREZOR response error
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
// TODO: check message
|
// TODO: check message
|
||||||
console.warn("DISCOVERY ERROR", response)
|
console.warn('DISCOVERY ERROR', response);
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.ADD,
|
type: NOTIFICATION.ADD,
|
||||||
payload: {
|
payload: {
|
||||||
@ -160,17 +153,17 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
|
|||||||
{
|
{
|
||||||
label: 'Try again',
|
label: 'Try again',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
dispatch(start(device, network))
|
dispatch(start(device, network));
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for interruption
|
// check for interruption
|
||||||
let discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
|
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
|
||||||
if (discoveryProcess && discoveryProcess.interrupted) return;
|
if (discoveryProcess && discoveryProcess.interrupted) return;
|
||||||
|
|
||||||
const basePath: Array<number> = response.payload.path;
|
const basePath: Array<number> = response.payload.path;
|
||||||
@ -186,12 +179,9 @@ const begin = (device: TrezorDevice, network: string): AsyncAction => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
dispatch(start(device, network));
|
dispatch(start(device, network));
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => {
|
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
|
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const completed: boolean = discoveryProcess.completed;
|
const completed: boolean = discoveryProcess.completed;
|
||||||
discoveryProcess.completed = false;
|
discoveryProcess.completed = false;
|
||||||
|
|
||||||
@ -202,7 +192,6 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
const network = discoveryProcess.network;
|
const network = discoveryProcess.network;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: check if address was created before
|
// TODO: check if address was created before
|
||||||
|
|
||||||
// verify address with TREZOR
|
// verify address with TREZOR
|
||||||
@ -210,7 +199,7 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
device: {
|
device: {
|
||||||
path: device.path,
|
path: device.path,
|
||||||
instance: device.instance,
|
instance: device.instance,
|
||||||
state: device.state
|
state: device.state,
|
||||||
},
|
},
|
||||||
path,
|
path,
|
||||||
showOnTrezor: false,
|
showOnTrezor: false,
|
||||||
@ -234,7 +223,7 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address);
|
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address);
|
||||||
if (trezorAddress !== ethAddress) {
|
if (trezorAddress !== ethAddress) {
|
||||||
// throw inconsistent state error
|
// throw inconsistent state error
|
||||||
console.warn("Inconsistent state", trezorAddress, ethAddress);
|
console.warn('Inconsistent state', trezorAddress, ethAddress);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.ADD,
|
type: NOTIFICATION.ADD,
|
||||||
@ -247,11 +236,11 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
{
|
{
|
||||||
label: 'Try again',
|
label: 'Try again',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
dispatch(start(device, discoveryProcess.network))
|
dispatch(start(device, discoveryProcess.network));
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -268,11 +257,11 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
{
|
{
|
||||||
label: 'Try again',
|
label: 'Try again',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
dispatch(start(device, discoveryProcess.network))
|
dispatch(start(device, discoveryProcess.network));
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -294,15 +283,14 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
network,
|
network,
|
||||||
index: discoveryProcess.accountIndex,
|
index: discoveryProcess.accountIndex,
|
||||||
path,
|
path,
|
||||||
address: ethAddress
|
address: ethAddress,
|
||||||
});
|
});
|
||||||
dispatch(
|
dispatch(
|
||||||
AccountsActions.setBalance(ethAddress, network, device.state || 'undefined', web3instance.web3.fromWei(balance.toString(), 'ether'))
|
AccountsActions.setBalance(ethAddress, network, device.state || 'undefined', web3instance.web3.fromWei(balance.toString(), 'ether')),
|
||||||
);
|
);
|
||||||
dispatch(AccountsActions.setNonce(ethAddress, network, device.state || 'undefined', nonce));
|
dispatch(AccountsActions.setNonce(ethAddress, network, device.state || 'undefined', nonce));
|
||||||
|
|
||||||
if (!completed)
|
if (!completed) { dispatch(discoverAccount(device, discoveryProcess)); }
|
||||||
dispatch( discoverAccount(device, discoveryProcess) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addressIsEmpty) {
|
if (addressIsEmpty) {
|
||||||
@ -311,7 +299,7 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
device: {
|
device: {
|
||||||
path: device.path,
|
path: device.path,
|
||||||
instance: device.instance,
|
instance: device.instance,
|
||||||
state: device.state
|
state: device.state,
|
||||||
},
|
},
|
||||||
keepSession: false,
|
keepSession: false,
|
||||||
useEmptyPassphrase: !device.instance,
|
useEmptyPassphrase: !device.instance,
|
||||||
@ -321,14 +309,12 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.COMPLETE,
|
type: DISCOVERY.COMPLETE,
|
||||||
device,
|
device,
|
||||||
network
|
network,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const restore = (): ThunkAction => {
|
export const restore = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
const selected = getState().wallet.selectedDevice;
|
||||||
|
|
||||||
if (selected && selected.connected && selected.features) {
|
if (selected && selected.connected && selected.features) {
|
||||||
@ -337,15 +323,13 @@ export const restore = (): ThunkAction => {
|
|||||||
dispatch(start(selected, discoveryProcess.network));
|
dispatch(start(selected, discoveryProcess.network));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: rename method to something intuitive
|
// TODO: rename method to something intuitive
|
||||||
// there is no discovery process but it should be
|
// there is no discovery process but it should be
|
||||||
// this is possible race condition when "network" was changed in url but device was not authenticated yet
|
// this is possible race condition when "network" was changed in url but device was not authenticated yet
|
||||||
// try to start discovery after CONNECT.AUTH_DEVICE action
|
// try to start discovery after CONNECT.AUTH_DEVICE action
|
||||||
export const check = (): ThunkAction => {
|
export const check = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
const selected = getState().wallet.selectedDevice;
|
||||||
if (!selected) return;
|
if (!selected) return;
|
||||||
|
|
||||||
@ -356,14 +340,9 @@ export const check = (): ThunkAction => {
|
|||||||
dispatch(start(selected, urlParams.network));
|
dispatch(start(selected, urlParams.network));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const stop = (device: TrezorDevice): Action => {
|
export const stop = (device: TrezorDevice): Action => ({
|
||||||
// TODO: release devices session
|
|
||||||
// corner case switch /eth to /etc (discovery start stop - should not be async)
|
|
||||||
return {
|
|
||||||
type: DISCOVERY.STOP,
|
type: DISCOVERY.STOP,
|
||||||
device
|
device,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
@ -1,2 +1 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as CONNECT from './constants/TrezorConnect';
|
import * as CONNECT from './constants/TrezorConnect';
|
||||||
import * as ACCOUNT from './constants/account';
|
import * as ACCOUNT from './constants/account';
|
||||||
import * as TOKEN from './constants/token';
|
import * as TOKEN from './constants/token';
|
||||||
import * as DISCOVERY from './constants/discovery';
|
import * as DISCOVERY from './constants/discovery';
|
||||||
import * as STORAGE from './constants/localStorage';
|
import * as STORAGE from './constants/localStorage';
|
||||||
import * as PENDING from '../actions/constants/pendingTx';
|
import * as PENDING from './constants/pendingTx';
|
||||||
import { JSONRequest, httpRequest } from '../utils/networkUtils';
|
import { JSONRequest, httpRequest } from '../utils/networkUtils';
|
||||||
|
|
||||||
import type { ThunkAction, AsyncAction, GetState, Dispatch, TrezorDevice } from '~/flowtype';
|
import type {
|
||||||
|
ThunkAction, AsyncAction, GetState, Dispatch, TrezorDevice,
|
||||||
|
} from '~/flowtype';
|
||||||
import type { Config, Coin, TokensCollection } from '../reducers/LocalStorageReducer';
|
import type { Config, Coin, TokensCollection } from '../reducers/LocalStorageReducer';
|
||||||
|
|
||||||
import AppConfigJSON from '~/data/appConfig.json';
|
import AppConfigJSON from '~/data/appConfig.json';
|
||||||
@ -28,9 +30,7 @@ export type StorageAction = {
|
|||||||
error: string,
|
error: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadData = (): ThunkAction => {
|
export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
// check if local storage is available
|
// check if local storage is available
|
||||||
// let available: boolean = true;
|
// let available: boolean = true;
|
||||||
// if (typeof window.localStorage === 'undefined') {
|
// if (typeof window.localStorage === 'undefined') {
|
||||||
@ -44,8 +44,7 @@ export const loadData = (): ThunkAction => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
dispatch(loadTokensFromJSON());
|
dispatch(loadTokensFromJSON());
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// const parseConfig = (json: JSON): Config => {
|
// const parseConfig = (json: JSON): Config => {
|
||||||
|
|
||||||
@ -84,16 +83,15 @@ export const loadData = (): ThunkAction => {
|
|||||||
|
|
||||||
export function loadTokensFromJSON(): AsyncAction {
|
export function loadTokensFromJSON(): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
if (typeof window.localStorage === 'undefined') return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const config: Config = await httpRequest(AppConfigJSON, 'json');
|
const config: Config = await httpRequest(AppConfigJSON, 'json');
|
||||||
const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json');
|
const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json');
|
||||||
|
|
||||||
window.addEventListener('storage', event => {
|
window.addEventListener('storage', (event) => {
|
||||||
dispatch(update(event));
|
dispatch(update(event));
|
||||||
})
|
});
|
||||||
|
|
||||||
// load tokens
|
// load tokens
|
||||||
const tokens = await config.coins.reduce(async (promise: Promise<TokensCollection>, coin: Coin): Promise<TokensCollection> => {
|
const tokens = await config.coins.reduce(async (promise: Promise<TokensCollection>, coin: Coin): Promise<TokensCollection> => {
|
||||||
@ -107,40 +105,40 @@ export function loadTokensFromJSON(): AsyncAction {
|
|||||||
if (devices) {
|
if (devices) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.DEVICE_FROM_STORAGE,
|
type: CONNECT.DEVICE_FROM_STORAGE,
|
||||||
payload: JSON.parse(devices)
|
payload: JSON.parse(devices),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const accounts: ?string = get('accounts');
|
const accounts: ?string = get('accounts');
|
||||||
if (accounts) {
|
if (accounts) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACCOUNT.FROM_STORAGE,
|
type: ACCOUNT.FROM_STORAGE,
|
||||||
payload: JSON.parse(accounts)
|
payload: JSON.parse(accounts),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const userTokens: ?string = get('tokens');
|
const userTokens: ?string = get('tokens');
|
||||||
if (userTokens) {
|
if (userTokens) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOKEN.FROM_STORAGE,
|
type: TOKEN.FROM_STORAGE,
|
||||||
payload: JSON.parse(userTokens)
|
payload: JSON.parse(userTokens),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const pending: ?string = get('pending');
|
const pending: ?string = get('pending');
|
||||||
if (pending) {
|
if (pending) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: PENDING.FROM_STORAGE,
|
type: PENDING.FROM_STORAGE,
|
||||||
payload: JSON.parse(pending)
|
payload: JSON.parse(pending),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const discovery: ?string = get('discovery');
|
const discovery: ?string = get('discovery');
|
||||||
if (discovery) {
|
if (discovery) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.FROM_STORAGE,
|
type: DISCOVERY.FROM_STORAGE,
|
||||||
payload: JSON.parse(discovery)
|
payload: JSON.parse(discovery),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -148,21 +146,19 @@ export function loadTokensFromJSON(): AsyncAction {
|
|||||||
type: STORAGE.READY,
|
type: STORAGE.READY,
|
||||||
config,
|
config,
|
||||||
tokens,
|
tokens,
|
||||||
ERC20Abi
|
ERC20Abi,
|
||||||
})
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: STORAGE.ERROR,
|
type: STORAGE.ERROR,
|
||||||
error
|
error,
|
||||||
})
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function update(event: StorageEvent): AsyncAction {
|
export function update(event: StorageEvent): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
if (!event.newValue) return;
|
if (!event.newValue) return;
|
||||||
|
|
||||||
if (event.key === 'devices') {
|
if (event.key === 'devices') {
|
||||||
@ -183,46 +179,44 @@ export function update(event: StorageEvent): AsyncAction {
|
|||||||
if (event.key === 'accounts') {
|
if (event.key === 'accounts') {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACCOUNT.FROM_STORAGE,
|
type: ACCOUNT.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue)
|
payload: JSON.parse(event.newValue),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'tokens') {
|
if (event.key === 'tokens') {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOKEN.FROM_STORAGE,
|
type: TOKEN.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue)
|
payload: JSON.parse(event.newValue),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'pending') {
|
if (event.key === 'pending') {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: PENDING.FROM_STORAGE,
|
type: PENDING.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue)
|
payload: JSON.parse(event.newValue),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'discovery') {
|
if (event.key === 'discovery') {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DISCOVERY.FROM_STORAGE,
|
type: DISCOVERY.FROM_STORAGE,
|
||||||
payload: JSON.parse(event.newValue)
|
payload: JSON.parse(event.newValue),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const save = (key: string, value: string): ThunkAction => {
|
export const save = (key: string, value: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
if (typeof window.localStorage !== 'undefined') {
|
if (typeof window.localStorage !== 'undefined') {
|
||||||
try {
|
try {
|
||||||
window.localStorage.setItem(key, value);
|
window.localStorage.setItem(key, value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// available = false;
|
// available = false;
|
||||||
console.error("Local Storage ERROR: " + error)
|
console.error(`Local Storage ERROR: ${error}`);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const get = (key: string): ?string => {
|
export const get = (key: string): ?string => {
|
||||||
try {
|
try {
|
||||||
@ -231,4 +225,4 @@ export const get = (key: string): ?string => {
|
|||||||
// available = false;
|
// available = false;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
@ -1,9 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as LOG from './constants/log';
|
import * as LOG from './constants/log';
|
||||||
|
|
||||||
import type { Action, ThunkAction, GetState, Dispatch } from '~/flowtype';
|
import type {
|
||||||
|
Action, ThunkAction, GetState, Dispatch,
|
||||||
|
} from '~/flowtype';
|
||||||
import type { LogEntry } from '../reducers/LogReducer';
|
import type { LogEntry } from '../reducers/LogReducer';
|
||||||
|
|
||||||
export type LogAction = {
|
export type LogAction = {
|
||||||
@ -15,31 +17,26 @@ export type LogAction = {
|
|||||||
payload: LogEntry
|
payload: LogEntry
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toggle = (): ThunkAction => {
|
export const toggle = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
if (!getState().log.opened) {
|
if (!getState().log.opened) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LOG.OPEN
|
type: LOG.OPEN,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LOG.CLOSE
|
type: LOG.CLOSE,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// export const add = (type: string, message: string): Action => {
|
// export const add = (type: string, message: string): Action => {
|
||||||
export const add = (type: string, message: any): Action => {
|
export const add = (type: string, message: any): Action => ({
|
||||||
return {
|
|
||||||
type: LOG.ADD,
|
type: LOG.ADD,
|
||||||
payload: {
|
payload: {
|
||||||
time: new Date().getTime(),
|
time: new Date().getTime(),
|
||||||
type,
|
type,
|
||||||
message
|
message,
|
||||||
}
|
},
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect';
|
import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect';
|
||||||
|
import type { Device } from 'trezor-connect';
|
||||||
import * as MODAL from './constants/modal';
|
import * as MODAL from './constants/modal';
|
||||||
import * as CONNECT from './constants/TrezorConnect';
|
import * as CONNECT from './constants/TrezorConnect';
|
||||||
|
|
||||||
import type { ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice } from '~/flowtype';
|
import type {
|
||||||
|
ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice,
|
||||||
|
} from '~/flowtype';
|
||||||
import type { State } from '../reducers/ModalReducer';
|
import type { State } from '../reducers/ModalReducer';
|
||||||
import type { Device } from 'trezor-connect';
|
|
||||||
|
|
||||||
export type ModalAction = {
|
export type ModalAction = {
|
||||||
type: typeof MODAL.CLOSE
|
type: typeof MODAL.CLOSE
|
||||||
@ -19,25 +21,23 @@ export type ModalAction = {
|
|||||||
export const onPinSubmit = (value: string): Action => {
|
export const onPinSubmit = (value: string): Action => {
|
||||||
TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value });
|
TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value });
|
||||||
return {
|
return {
|
||||||
type: MODAL.CLOSE
|
type: MODAL.CLOSE,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const onPassphraseSubmit = (passphrase: string): AsyncAction => {
|
export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
const resp = await TrezorConnect.uiResponse({
|
const resp = await TrezorConnect.uiResponse({
|
||||||
type: UI.RECEIVE_PASSPHRASE,
|
type: UI.RECEIVE_PASSPHRASE,
|
||||||
payload: {
|
payload: {
|
||||||
value: passphrase,
|
value: passphrase,
|
||||||
save: true
|
save: true,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: MODAL.CLOSE
|
type: MODAL.CLOSE,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// export const askForRemember = (device: TrezorDevice): Action => {
|
// export const askForRemember = (device: TrezorDevice): Action => {
|
||||||
// return {
|
// return {
|
||||||
@ -46,47 +46,35 @@ export const onPassphraseSubmit = (passphrase: string): AsyncAction => {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
export const onRememberDevice = (device: TrezorDevice): Action => {
|
export const onRememberDevice = (device: TrezorDevice): Action => ({
|
||||||
return {
|
|
||||||
type: CONNECT.REMEMBER,
|
type: CONNECT.REMEMBER,
|
||||||
device
|
device,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const onForgetDevice = (device: TrezorDevice): Action => {
|
export const onForgetDevice = (device: TrezorDevice): Action => ({
|
||||||
return {
|
|
||||||
type: CONNECT.FORGET,
|
type: CONNECT.FORGET,
|
||||||
device,
|
device,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const onForgetSingleDevice = (device: TrezorDevice): Action => {
|
export const onForgetSingleDevice = (device: TrezorDevice): Action => ({
|
||||||
return {
|
|
||||||
type: CONNECT.FORGET_SINGLE,
|
type: CONNECT.FORGET_SINGLE,
|
||||||
device,
|
device,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const onCancel = (): Action => {
|
export const onCancel = (): Action => ({
|
||||||
return {
|
type: MODAL.CLOSE,
|
||||||
type: MODAL.CLOSE
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => {
|
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
|
export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
dispatch(onCancel());
|
dispatch(onCancel());
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.DUPLICATE,
|
type: CONNECT.DUPLICATE,
|
||||||
device
|
device,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onRememberRequest = (prevState: State): ThunkAction => {
|
export const onRememberRequest = (prevState: State): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const state: State = getState().modal;
|
const state: State = getState().modal;
|
||||||
// handle case where forget modal is already opened
|
// handle case where forget modal is already opened
|
||||||
// TODO: 2 modals at once (two devices disconnected in the same time)
|
// TODO: 2 modals at once (two devices disconnected in the same time)
|
||||||
@ -95,21 +83,19 @@ export const onRememberRequest = (prevState: State): ThunkAction => {
|
|||||||
if (state.opened) {
|
if (state.opened) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.FORGET,
|
type: CONNECT.FORGET,
|
||||||
device: state.device
|
device: state.device,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// forget previous (old)
|
// forget previous (old)
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.FORGET,
|
type: CONNECT.FORGET,
|
||||||
device: prevState.device
|
device: prevState.device,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onDeviceConnect = (device: Device): ThunkAction => {
|
export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
// interrupt process of remembering device (force forget)
|
// interrupt process of remembering device (force forget)
|
||||||
// TODO: the same for disconnect more than 1 device at once
|
// TODO: the same for disconnect more than 1 device at once
|
||||||
const { modal } = getState();
|
const { modal } = getState();
|
||||||
@ -121,13 +107,11 @@ export const onDeviceConnect = (device: Device): ThunkAction => {
|
|||||||
} else {
|
} else {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.FORGET,
|
type: CONNECT.FORGET,
|
||||||
device: modal.device
|
device: modal.device,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onPinSubmit,
|
onPinSubmit,
|
||||||
@ -137,5 +121,5 @@ export default {
|
|||||||
onForgetDevice,
|
onForgetDevice,
|
||||||
onForgetSingleDevice,
|
onForgetSingleDevice,
|
||||||
onCancel,
|
onCancel,
|
||||||
onDuplicateDevice
|
onDuplicateDevice,
|
||||||
}
|
};
|
@ -1,9 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as NOTIFICATION from './constants/notification';
|
import * as NOTIFICATION from './constants/notification';
|
||||||
|
|
||||||
import type { Action, AsyncAction, GetState, Dispatch, RouterLocationState } from '~/flowtype';
|
import type {
|
||||||
|
Action, AsyncAction, GetState, Dispatch, RouterLocationState,
|
||||||
|
} from '~/flowtype';
|
||||||
import type { CallbackAction } from '../reducers/NotificationReducer';
|
import type { CallbackAction } from '../reducers/NotificationReducer';
|
||||||
|
|
||||||
export type NotificationAction = {
|
export type NotificationAction = {
|
||||||
@ -23,31 +25,27 @@ export type NotificationAction = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const close = (payload: any = {}): Action => {
|
export const close = (payload: any = {}): Action => ({
|
||||||
return {
|
|
||||||
type: NOTIFICATION.CLOSE,
|
type: NOTIFICATION.CLOSE,
|
||||||
payload
|
payload,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// called from RouterService
|
// called from RouterService
|
||||||
export const clear = (currentParams: RouterLocationState, requestedParams: RouterLocationState): AsyncAction => {
|
export const clear = (currentParams: RouterLocationState, requestedParams: RouterLocationState): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
// if route has been changed from device view into something else (like other device, settings...)
|
// if route has been changed from device view into something else (like other device, settings...)
|
||||||
// try to remove all Notifications which are linked to previous device (they are not cancelable by user)
|
// try to remove all Notifications which are linked to previous device (they are not cancelable by user)
|
||||||
if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
|
if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
|
||||||
const entries = getState().notifications.filter(entry => typeof entry.devicePath === 'string');
|
const entries = getState().notifications.filter(entry => typeof entry.devicePath === 'string');
|
||||||
entries.forEach(entry => {
|
entries.forEach((entry) => {
|
||||||
if (typeof entry.devicePath === 'string') {
|
if (typeof entry.devicePath === 'string') {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.CLOSE,
|
type: NOTIFICATION.CLOSE,
|
||||||
payload: {
|
payload: {
|
||||||
devicePath: entry.devicePath
|
devicePath: entry.devicePath,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as PENDING from './constants/pendingTx';
|
import * as PENDING from './constants/pendingTx';
|
||||||
import type { State, PendingTx } from '../reducers/PendingTxReducer'
|
import type { State, PendingTx } from '../reducers/PendingTxReducer';
|
||||||
|
|
||||||
export type PendingTxAction = {
|
export type PendingTxAction = {
|
||||||
type: typeof PENDING.FROM_STORAGE,
|
type: typeof PENDING.FROM_STORAGE,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import TrezorConnect from 'trezor-connect';
|
import TrezorConnect from 'trezor-connect';
|
||||||
import * as RECEIVE from './constants/receive';
|
import * as RECEIVE from './constants/receive';
|
||||||
@ -8,7 +8,9 @@ import * as NOTIFICATION from './constants/notification';
|
|||||||
import { initialState } from '../reducers/ReceiveReducer';
|
import { initialState } from '../reducers/ReceiveReducer';
|
||||||
import type { State } from '../reducers/ReceiveReducer';
|
import type { State } from '../reducers/ReceiveReducer';
|
||||||
|
|
||||||
import type { TrezorDevice, ThunkAction, AsyncAction, Action, GetState, Dispatch } from '~/flowtype';
|
import type {
|
||||||
|
TrezorDevice, ThunkAction, AsyncAction, Action, GetState, Dispatch,
|
||||||
|
} from '~/flowtype';
|
||||||
|
|
||||||
export type ReceiveAction = {
|
export type ReceiveAction = {
|
||||||
type: typeof RECEIVE.INIT,
|
type: typeof RECEIVE.INIT,
|
||||||
@ -26,43 +28,34 @@ export type ReceiveAction = {
|
|||||||
type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS
|
type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS
|
||||||
}
|
}
|
||||||
|
|
||||||
export const init = (): ThunkAction => {
|
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
const state: State = {
|
const state: State = {
|
||||||
...initialState,
|
...initialState,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: RECEIVE.INIT,
|
type: RECEIVE.INIT,
|
||||||
state: state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const dispose = (): Action => {
|
export const dispose = (): Action => ({
|
||||||
return {
|
type: RECEIVE.DISPOSE,
|
||||||
type: RECEIVE.DISPOSE
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const showUnverifiedAddress = (): Action => {
|
export const showUnverifiedAddress = (): Action => ({
|
||||||
return {
|
type: RECEIVE.SHOW_UNVERIFIED_ADDRESS,
|
||||||
type: RECEIVE.SHOW_UNVERIFIED_ADDRESS
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export const showAddress = (address_n: string): AsyncAction => {
|
//export const showAddress = (address_n: string): AsyncAction => {
|
||||||
export const showAddress = (address_n: Array<number>): AsyncAction => {
|
export const showAddress = (address_n: Array<number>): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
const selected = getState().wallet.selectedDevice;
|
||||||
if (!selected) return;
|
if (!selected) return;
|
||||||
|
|
||||||
if (selected && (!selected.connected || !selected.available)) {
|
if (selected && (!selected.connected || !selected.available)) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: RECEIVE.REQUEST_UNVERIFIED,
|
type: RECEIVE.REQUEST_UNVERIFIED,
|
||||||
device: selected
|
device: selected,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -71,7 +64,7 @@ export const showAddress = (address_n: Array<number>): AsyncAction => {
|
|||||||
device: {
|
device: {
|
||||||
path: selected.path,
|
path: selected.path,
|
||||||
instance: selected.instance,
|
instance: selected.instance,
|
||||||
state: selected.state
|
state: selected.state,
|
||||||
},
|
},
|
||||||
path: address_n,
|
path: address_n,
|
||||||
useEmptyPassphrase: !selected.instance,
|
useEmptyPassphrase: !selected.instance,
|
||||||
@ -79,12 +72,12 @@ export const showAddress = (address_n: Array<number>): AsyncAction => {
|
|||||||
|
|
||||||
if (response && response.success) {
|
if (response && response.success) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: RECEIVE.SHOW_ADDRESS
|
type: RECEIVE.SHOW_ADDRESS,
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: RECEIVE.HIDE_ADDRESS
|
type: RECEIVE.HIDE_ADDRESS,
|
||||||
})
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.ADD,
|
type: NOTIFICATION.ADD,
|
||||||
@ -97,19 +90,18 @@ export const showAddress = (address_n: Array<number>): AsyncAction => {
|
|||||||
{
|
{
|
||||||
label: 'Try again',
|
label: 'Try again',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
dispatch(showAddress(address_n))
|
dispatch(showAddress(address_n));
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
init,
|
init,
|
||||||
dispose,
|
dispose,
|
||||||
showAddress,
|
showAddress,
|
||||||
showUnverifiedAddress
|
showUnverifiedAddress,
|
||||||
}
|
};
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { LOCATION_CHANGE } from 'react-router-redux';
|
import { LOCATION_CHANGE } from 'react-router-redux';
|
||||||
import * as ACCOUNT from './constants/account';
|
import * as ACCOUNT from './constants/account';
|
||||||
@ -32,15 +32,13 @@ export type SelectedAccountAction = {
|
|||||||
payload: $ElementType<State, 'selectedAccount'>
|
payload: $ElementType<State, 'selectedAccount'>
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => {
|
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
const locationChange: boolean = action.type === LOCATION_CHANGE;
|
const locationChange: boolean = action.type === LOCATION_CHANGE;
|
||||||
const state: State = getState();
|
const state: State = getState();
|
||||||
const location = state.router.location;
|
const location = state.router.location;
|
||||||
const prevLocation = prevState.router.location;
|
const prevLocation = prevState.router.location;
|
||||||
|
|
||||||
let needUpdate: boolean = false;
|
const needUpdate: boolean = false;
|
||||||
|
|
||||||
// reset form to default
|
// reset form to default
|
||||||
if (action.type === SEND.TX_COMPLETE) {
|
if (action.type === SEND.TX_COMPLETE) {
|
||||||
@ -56,8 +54,6 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
|| prevState.tokens !== state.tokens
|
|| prevState.tokens !== state.tokens
|
||||||
|| prevState.pending !== state.pending
|
|| prevState.pending !== state.pending
|
||||||
|| prevState.web3 !== state.web3) {
|
|| prevState.web3 !== state.web3) {
|
||||||
|
|
||||||
|
|
||||||
if (locationChange) {
|
if (locationChange) {
|
||||||
// dispose current account view
|
// dispose current account view
|
||||||
dispatch(dispose());
|
dispatch(dispose());
|
||||||
@ -77,8 +73,8 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
discovery,
|
discovery,
|
||||||
tokens,
|
tokens,
|
||||||
pending,
|
pending,
|
||||||
web3
|
web3,
|
||||||
}
|
};
|
||||||
|
|
||||||
let needUpdate: boolean = false;
|
let needUpdate: boolean = false;
|
||||||
Object.keys(payload).forEach((key) => {
|
Object.keys(payload).forEach((key) => {
|
||||||
@ -86,12 +82,10 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) {
|
if (Array.isArray(state.selectedAccount[key]) && payload[key].length !== state.selectedAccount[key].length) {
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (payload[key] !== state.selectedAccount[key]) {
|
||||||
if (payload[key] !== state.selectedAccount[key]) {
|
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
})
|
|
||||||
|
|
||||||
if (needUpdate) {
|
if (needUpdate) {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -100,18 +94,18 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
});
|
});
|
||||||
|
|
||||||
// initialize SendFormReducer
|
// initialize SendFormReducer
|
||||||
if (location.state.send && getState().sendForm.currency === "") {
|
if (location.state.send && getState().sendForm.currency === '') {
|
||||||
dispatch(SendFormActions.init());
|
dispatch(SendFormActions.init());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.state.send) {
|
if (location.state.send) {
|
||||||
const rejectedTxs = pending.filter(tx => tx.rejected);
|
const rejectedTxs = pending.filter(tx => tx.rejected);
|
||||||
rejectedTxs.forEach(tx => {
|
rejectedTxs.forEach((tx) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.ADD,
|
type: NOTIFICATION.ADD,
|
||||||
payload: {
|
payload: {
|
||||||
type: "warning",
|
type: 'warning',
|
||||||
title: "Pending transaction rejected",
|
title: 'Pending transaction rejected',
|
||||||
message: `Transaction with id: ${tx.id} not found.`,
|
message: `Transaction with id: ${tx.id} not found.`,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
actions: [
|
actions: [
|
||||||
@ -122,20 +116,17 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
type: PENDING.TX_RESOLVED,
|
type: PENDING.TX_RESOLVED,
|
||||||
tx,
|
tx,
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const dispose = (): Action => {
|
export const dispose = (): Action => ({
|
||||||
return {
|
type: ACCOUNT.DISPOSE,
|
||||||
type: ACCOUNT.DISPOSE
|
});
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,17 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as SEND from './constants/send';
|
|
||||||
import * as NOTIFICATION from './constants/notification';
|
|
||||||
|
|
||||||
import * as SessionStorageActions from './SessionStorageActions';
|
|
||||||
|
|
||||||
import { estimateGas, getGasPrice, pushTx } from './Web3Actions';
|
|
||||||
|
|
||||||
import EthereumjsUtil from 'ethereumjs-util';
|
import EthereumjsUtil from 'ethereumjs-util';
|
||||||
import EthereumjsUnits from 'ethereumjs-units';
|
import EthereumjsUnits from 'ethereumjs-units';
|
||||||
import EthereumjsTx from 'ethereumjs-tx';
|
import EthereumjsTx from 'ethereumjs-tx';
|
||||||
import TrezorConnect from 'trezor-connect';
|
import TrezorConnect from 'trezor-connect';
|
||||||
import { strip } from '../utils/ethUtils';
|
|
||||||
import { push } from 'react-router-redux';
|
import { push } from 'react-router-redux';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { strip } from '../utils/ethUtils';
|
||||||
|
import { estimateGas, getGasPrice, pushTx } from './Web3Actions';
|
||||||
|
import * as SessionStorageActions from './SessionStorageActions';
|
||||||
|
import * as NOTIFICATION from './constants/notification';
|
||||||
|
import * as SEND from './constants/send';
|
||||||
|
|
||||||
import { initialState } from '../reducers/SendFormReducer';
|
import { initialState } from '../reducers/SendFormReducer';
|
||||||
import { findAccount } from '../reducers/AccountsReducer';
|
import { findAccount } from '../reducers/AccountsReducer';
|
||||||
@ -30,7 +27,7 @@ import type {
|
|||||||
ThunkAction,
|
ThunkAction,
|
||||||
AsyncAction,
|
AsyncAction,
|
||||||
RouterLocationState,
|
RouterLocationState,
|
||||||
TrezorDevice
|
TrezorDevice,
|
||||||
} from '~/flowtype';
|
} from '~/flowtype';
|
||||||
import type { State as AccountState } from '../reducers/SelectedAccountReducer';
|
import type { State as AccountState } from '../reducers/SelectedAccountReducer';
|
||||||
import type { Web3Instance } from '../reducers/Web3Reducer';
|
import type { Web3Instance } from '../reducers/Web3Reducer';
|
||||||
@ -128,7 +125,7 @@ export const calculateFee = (gasPrice: string, gasLimit: string): string => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const calculateTotal = (amount: string, gasPrice: string, gasLimit: string): string => {
|
export const calculateTotal = (amount: string, gasPrice: string, gasLimit: string): string => {
|
||||||
try {
|
try {
|
||||||
@ -136,7 +133,7 @@ export const calculateTotal = (amount: string, gasPrice: string, gasLimit: strin
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimit: string): string => {
|
export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimit: string): string => {
|
||||||
try {
|
try {
|
||||||
@ -148,8 +145,7 @@ export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimi
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const calculate = (prevProps: Props, props: Props) => {
|
export const calculate = (prevProps: Props, props: Props) => {
|
||||||
const {
|
const {
|
||||||
@ -173,7 +169,6 @@ export const calculate = (prevProps: Props, props: Props) => {
|
|||||||
|
|
||||||
|
|
||||||
if (state.setMax) {
|
if (state.setMax) {
|
||||||
|
|
||||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, isToken);
|
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, isToken);
|
||||||
|
|
||||||
if (isToken) {
|
if (isToken) {
|
||||||
@ -195,11 +190,11 @@ export const calculate = (prevProps: Props, props: Props) => {
|
|||||||
state.selectedFeeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`;
|
state.selectedFeeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`;
|
||||||
state.selectedFeeLevel.gasPrice = state.gasPrice;
|
state.selectedFeeLevel.gasPrice = state.gasPrice;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLimit: string, selected?: FeeLevel): Array<FeeLevel> => {
|
export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLimit: string, selected?: FeeLevel): Array<FeeLevel> => {
|
||||||
const price: BigNumber = typeof gasPrice === 'string' ? new BigNumber(gasPrice) : gasPrice
|
const price: BigNumber = typeof gasPrice === 'string' ? new BigNumber(gasPrice) : gasPrice;
|
||||||
const quarter: BigNumber = price.dividedBy(4);
|
const quarter: BigNumber = price.dividedBy(4);
|
||||||
const high: string = price.plus(quarter.times(2)).toString(10);
|
const high: string = price.plus(quarter.times(2)).toString(10);
|
||||||
const low: string = price.minus(quarter.times(2)).toString(10);
|
const low: string = price.minus(quarter.times(2)).toString(10);
|
||||||
@ -208,42 +203,40 @@ export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLi
|
|||||||
value: 'Custom',
|
value: 'Custom',
|
||||||
gasPrice: selected.gasPrice,
|
gasPrice: selected.gasPrice,
|
||||||
// label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }`
|
// label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }`
|
||||||
label: `${ calculateFee(selected.gasPrice, gasLimit) } ${ symbol }`
|
label: `${calculateFee(selected.gasPrice, gasLimit)} ${symbol}`,
|
||||||
} : {
|
} : {
|
||||||
value: 'Custom',
|
value: 'Custom',
|
||||||
gasPrice: low,
|
gasPrice: low,
|
||||||
label: ''
|
label: '',
|
||||||
}
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
value: 'High',
|
value: 'High',
|
||||||
gasPrice: high,
|
gasPrice: high,
|
||||||
label: `${ calculateFee(high, gasLimit) } ${ symbol }`
|
label: `${calculateFee(high, gasLimit)} ${symbol}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'Normal',
|
value: 'Normal',
|
||||||
gasPrice: gasPrice.toString(),
|
gasPrice: gasPrice.toString(),
|
||||||
label: `${ calculateFee(price.toString(10), gasLimit) } ${ symbol }`
|
label: `${calculateFee(price.toString(10), gasLimit)} ${symbol}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'Low',
|
value: 'Low',
|
||||||
gasPrice: low,
|
gasPrice: low,
|
||||||
label: `${ calculateFee(low, gasLimit) } ${ symbol }`
|
label: `${calculateFee(low, gasLimit)} ${symbol}`,
|
||||||
},
|
},
|
||||||
customLevel
|
customLevel,
|
||||||
]
|
];
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
// initialize component
|
// initialize component
|
||||||
export const init = (): ThunkAction => {
|
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
web3
|
web3,
|
||||||
} = getState().selectedAccount;
|
} = getState().selectedAccount;
|
||||||
|
|
||||||
if (!account || !network || !web3) return;
|
if (!account || !network || !web3) return;
|
||||||
@ -252,7 +245,7 @@ export const init = (): ThunkAction => {
|
|||||||
if (stateFromStorage) {
|
if (stateFromStorage) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.INIT,
|
type: SEND.INIT,
|
||||||
state: stateFromStorage
|
state: stateFromStorage,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -280,20 +273,16 @@ export const init = (): ThunkAction => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.INIT,
|
type: SEND.INIT,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const toggleAdvanced = (address: string): Action => {
|
export const toggleAdvanced = (address: string): Action => ({
|
||||||
return {
|
type: SEND.TOGGLE_ADVANCED,
|
||||||
type: SEND.TOGGLE_ADVANCED
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const addressValidation = (): ThunkAction => {
|
const addressValidation = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
@ -322,7 +311,7 @@ const addressValidation = (): ThunkAction => {
|
|||||||
const otherNetworkAccount = savedAccounts[0];
|
const otherNetworkAccount = savedAccounts[0];
|
||||||
const device: ?TrezorDevice = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState);
|
const device: ?TrezorDevice = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState);
|
||||||
const coins = getState().localStorage.config.coins;
|
const coins = getState().localStorage.config.coins;
|
||||||
const otherNetwork: ?Coin = coins.find(c => c.network === otherNetworkAccount.network)
|
const otherNetwork: ?Coin = coins.find(c => c.network === otherNetworkAccount.network);
|
||||||
if (device && otherNetwork) {
|
if (device && otherNetwork) {
|
||||||
warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`;
|
warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`;
|
||||||
}
|
}
|
||||||
@ -337,15 +326,13 @@ const addressValidation = (): ThunkAction => {
|
|||||||
state: {
|
state: {
|
||||||
...state,
|
...state,
|
||||||
infos,
|
infos,
|
||||||
warnings
|
warnings,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const validation = (props: Props): void => {
|
export const validation = (props: Props): void => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
@ -388,7 +375,6 @@ export const validation = (props: Props): void => {
|
|||||||
} else if (state.amount.length > 0 && !state.amount.match(numberRegExp)) {
|
} else if (state.amount.length > 0 && !state.amount.match(numberRegExp)) {
|
||||||
errors.amount = 'Amount is not a number';
|
errors.amount = 'Amount is not a number';
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
let decimalRegExp: RegExp;
|
let decimalRegExp: RegExp;
|
||||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, state.currency !== state.networkSymbol);
|
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, state.currency !== state.networkSymbol);
|
||||||
|
|
||||||
@ -397,7 +383,7 @@ export const validation = (props: Props): void => {
|
|||||||
if (token) {
|
if (token) {
|
||||||
if (parseInt(token.decimals) > 0) {
|
if (parseInt(token.decimals) > 0) {
|
||||||
//decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$');
|
//decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9]+\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$');
|
||||||
decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,' + token.decimals + '})?|[1-9][0-9]*\\.?([0-9]{0,' + token.decimals + '})?|\\.[0-9]{1,' + token.decimals + '})$');
|
decimalRegExp = new RegExp(`^(0|0\\.([0-9]{0,${token.decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${token.decimals}})?|\\.[0-9]{1,${token.decimals}})$`);
|
||||||
} else {
|
} else {
|
||||||
// decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$');
|
// decimalRegExp = new RegExp('^(0|0\\.?|[1-9]+\\.?)$');
|
||||||
decimalRegExp = new RegExp('^[0-9]+$');
|
decimalRegExp = new RegExp('^[0-9]+$');
|
||||||
@ -413,11 +399,10 @@ export const validation = (props: Props): void => {
|
|||||||
errors.amount = 'Amount is too low';
|
errors.amount = 'Amount is too low';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$');
|
decimalRegExp = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$');
|
||||||
if (!state.amount.match(decimalRegExp)) {
|
if (!state.amount.match(decimalRegExp)) {
|
||||||
errors.amount = `Maximum 18 decimals allowed`;
|
errors.amount = 'Maximum 18 decimals allowed';
|
||||||
} else if (new BigNumber(state.total).greaterThan(new BigNumber(account.balance).minus(pendingAmount))) {
|
} else if (new BigNumber(state.total).greaterThan(new BigNumber(account.balance).minus(pendingAmount))) {
|
||||||
errors.amount = 'Not enough funds';
|
errors.amount = 'Not enough funds';
|
||||||
}
|
}
|
||||||
@ -487,14 +472,10 @@ export const validation = (props: Props): void => {
|
|||||||
state.errors = errors;
|
state.errors = errors;
|
||||||
state.warnings = warnings;
|
state.warnings = warnings;
|
||||||
state.infos = infos;
|
state.infos = infos;
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
export const onAddressChange = (address: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
|
|
||||||
export const onAddressChange = (address: string): ThunkAction => {
|
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const state: State = getState().sendForm;
|
const state: State = getState().sendForm;
|
||||||
const touched = { ...state.touched };
|
const touched = { ...state.touched };
|
||||||
touched.address = true;
|
touched.address = true;
|
||||||
@ -505,16 +486,14 @@ export const onAddressChange = (address: string): ThunkAction => {
|
|||||||
...state,
|
...state,
|
||||||
untouched: false,
|
untouched: false,
|
||||||
touched,
|
touched,
|
||||||
address
|
address,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(addressValidation());
|
dispatch(addressValidation());
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onAmountChange = (amount: string): ThunkAction => {
|
export const onAmountChange = (amount: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const state = getState().sendForm;
|
const state = getState().sendForm;
|
||||||
const touched = { ...state.touched };
|
const touched = { ...state.touched };
|
||||||
touched.amount = true;
|
touched.amount = true;
|
||||||
@ -527,16 +506,14 @@ export const onAmountChange = (amount: string): ThunkAction => {
|
|||||||
touched,
|
touched,
|
||||||
setMax: false,
|
setMax: false,
|
||||||
amount,
|
amount,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onCurrencyChange = (currency: { value: string, label: string }): ThunkAction => {
|
export const onCurrencyChange = (currency: { value: string, label: string }): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network
|
network,
|
||||||
} = getState().selectedAccount;
|
} = getState().selectedAccount;
|
||||||
if (!account || !network) return;
|
if (!account || !network) return;
|
||||||
|
|
||||||
@ -560,13 +537,11 @@ export const onCurrencyChange = (currency: { value: string, label: string }): Th
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.CURRENCY_CHANGE,
|
type: SEND.CURRENCY_CHANGE,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onSetMax = (): ThunkAction => {
|
export const onSetMax = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const state = getState().sendForm;
|
const state = getState().sendForm;
|
||||||
const touched = { ...state.touched };
|
const touched = { ...state.touched };
|
||||||
touched.amount = true;
|
touched.amount = true;
|
||||||
@ -578,15 +553,13 @@ export const onSetMax = (): ThunkAction => {
|
|||||||
untouched: false,
|
untouched: false,
|
||||||
touched,
|
touched,
|
||||||
setMax: !state.setMax,
|
setMax: !state.setMax,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
|
export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const {
|
const {
|
||||||
network
|
network,
|
||||||
} = getState().selectedAccount;
|
} = getState().selectedAccount;
|
||||||
if (!network) return;
|
if (!network) return;
|
||||||
|
|
||||||
@ -605,11 +578,10 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
|
|||||||
feeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`;
|
feeLevel.label = `${calculateFee(state.gasPrice, state.gasLimit)} ${state.networkSymbol}`;
|
||||||
} else {
|
} else {
|
||||||
const customLevel: ?FeeLevel = state.feeLevels.find(f => f.value === 'Custom');
|
const customLevel: ?FeeLevel = state.feeLevels.find(f => f.value === 'Custom');
|
||||||
if (customLevel)
|
if (customLevel) customLevel.label = '';
|
||||||
customLevel.label = '';
|
|
||||||
state.gasPrice = feeLevel.gasPrice;
|
state.gasPrice = feeLevel.gasPrice;
|
||||||
if (isToken) {
|
if (isToken) {
|
||||||
state.gasLimit = network.defaultGasLimitTokens.toString()
|
state.gasLimit = network.defaultGasLimitTokens.toString();
|
||||||
} else {
|
} else {
|
||||||
state.gasLimit = state.data.length > 0 ? state.gasLimit : network.defaultGasLimit.toString();
|
state.gasLimit = state.data.length > 0 ? state.gasLimit : network.defaultGasLimit.toString();
|
||||||
}
|
}
|
||||||
@ -617,19 +589,17 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.FEE_LEVEL_CHANGE,
|
type: SEND.FEE_LEVEL_CHANGE,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// Manually triggered from user
|
// Manually triggered from user
|
||||||
// Update gasPrice to recommended value
|
// Update gasPrice to recommended value
|
||||||
|
|
||||||
export const updateFeeLevels = (): ThunkAction => {
|
export const updateFeeLevels = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network
|
network,
|
||||||
} = getState().selectedAccount;
|
} = getState().selectedAccount;
|
||||||
if (!account || !network) return;
|
if (!account || !network) return;
|
||||||
|
|
||||||
@ -659,14 +629,11 @@ export const updateFeeLevels = (): ThunkAction => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.UPDATE_FEE_LEVELS,
|
type: SEND.UPDATE_FEE_LEVELS,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onGasPriceChange = (gasPrice: string): ThunkAction => {
|
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
|
export const onGasPriceChange = (gasPrice: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
const currentState: State = getState().sendForm;
|
const currentState: State = getState().sendForm;
|
||||||
const isToken: boolean = currentState.currency !== currentState.networkSymbol;
|
const isToken: boolean = currentState.currency !== currentState.networkSymbol;
|
||||||
|
|
||||||
@ -677,7 +644,7 @@ export const onGasPriceChange = (gasPrice: string): ThunkAction => {
|
|||||||
...currentState,
|
...currentState,
|
||||||
untouched: false,
|
untouched: false,
|
||||||
touched,
|
touched,
|
||||||
gasPrice: gasPrice,
|
gasPrice,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (currentState.selectedFeeLevel.value !== 'Custom') {
|
if (currentState.selectedFeeLevel.value !== 'Custom') {
|
||||||
@ -688,14 +655,11 @@ export const onGasPriceChange = (gasPrice: string): ThunkAction => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.GAS_PRICE_CHANGE,
|
type: SEND.GAS_PRICE_CHANGE,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onGasLimitChange = (gasLimit: string, updateFeeLevels: boolean = false): ThunkAction => {
|
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
|
export const onGasLimitChange = (gasLimit: string, updateFeeLevels: boolean = false): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
const currentState: State = getState().sendForm;
|
const currentState: State = getState().sendForm;
|
||||||
const isToken: boolean = currentState.currency !== currentState.networkSymbol;
|
const isToken: boolean = currentState.currency !== currentState.networkSymbol;
|
||||||
|
|
||||||
@ -718,13 +682,11 @@ export const onGasLimitChange = (gasLimit: string, updateFeeLevels: boolean = fa
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.GAS_LIMIT_CHANGE,
|
type: SEND.GAS_LIMIT_CHANGE,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onNonceChange = (nonce: string): AsyncAction => {
|
export const onNonceChange = (nonce: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
const currentState: State = getState().sendForm;
|
const currentState: State = getState().sendForm;
|
||||||
const touched = { ...currentState.touched };
|
const touched = { ...currentState.touched };
|
||||||
touched.nonce = true;
|
touched.nonce = true;
|
||||||
@ -738,13 +700,11 @@ export const onNonceChange = (nonce: string): AsyncAction => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.NONCE_CHANGE,
|
type: SEND.NONCE_CHANGE,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onDataChange = (data: string): AsyncAction => {
|
export const onDataChange = (data: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
const currentState: State = getState().sendForm;
|
const currentState: State = getState().sendForm;
|
||||||
const touched = { ...currentState.touched };
|
const touched = { ...currentState.touched };
|
||||||
touched.data = true;
|
touched.data = true;
|
||||||
@ -765,20 +725,17 @@ export const onDataChange = (data: string): AsyncAction => {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.DATA_CHANGE,
|
type: SEND.DATA_CHANGE,
|
||||||
state
|
state,
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch(estimateGasPrice());
|
dispatch(estimateGasPrice());
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const estimateGasPrice = (): AsyncAction => {
|
const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
web3,
|
web3,
|
||||||
network
|
network,
|
||||||
} = getState().selectedAccount;
|
} = getState().selectedAccount;
|
||||||
if (!web3 || !network) return;
|
if (!web3 || !network) return;
|
||||||
|
|
||||||
@ -801,7 +758,7 @@ const estimateGasPrice = (): AsyncAction => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: allow data starting with 0x ...
|
// TODO: allow data starting with 0x ...
|
||||||
const data: string = '0x' + (state.data.length % 2 === 0 ? state.data : '0' + state.data);
|
const data: string = `0x${state.data.length % 2 === 0 ? state.data : `0${state.data}`}`;
|
||||||
const gasLimit = await estimateGas(w3, {
|
const gasLimit = await estimateGas(w3, {
|
||||||
to: '0x0000000000000000000000000000000000000000',
|
to: '0x0000000000000000000000000000000000000000',
|
||||||
data,
|
data,
|
||||||
@ -812,18 +769,14 @@ const estimateGasPrice = (): AsyncAction => {
|
|||||||
if (getState().sendForm.data === requestedData) {
|
if (getState().sendForm.data === requestedData) {
|
||||||
dispatch(onGasLimitChange(gasLimit.toString()));
|
dispatch(onGasLimitChange(gasLimit.toString()));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const onSend = (): AsyncAction => {
|
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
|
export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
web3,
|
web3,
|
||||||
pending
|
pending,
|
||||||
} = getState().selectedAccount;
|
} = getState().selectedAccount;
|
||||||
if (!account || !web3 || !network) return;
|
if (!account || !web3 || !network) return;
|
||||||
|
|
||||||
@ -834,7 +787,7 @@ export const onSend = (): AsyncAction => {
|
|||||||
|
|
||||||
const address_n = account.addressPath;
|
const address_n = account.addressPath;
|
||||||
|
|
||||||
let data: string = '0x' + currentState.data;
|
let data: string = `0x${currentState.data}`;
|
||||||
let txAmount: string = w3.toHex(w3.toWei(currentState.amount, 'ether'));
|
let txAmount: string = w3.toHex(w3.toWei(currentState.amount, 'ether'));
|
||||||
let txAddress: string = currentState.address;
|
let txAddress: string = currentState.address;
|
||||||
if (isToken) {
|
if (isToken) {
|
||||||
@ -847,7 +800,7 @@ export const onSend = (): AsyncAction => {
|
|||||||
data = contract.transfer.getData(currentState.address, amountValue, {
|
data = contract.transfer.getData(currentState.address, amountValue, {
|
||||||
from: account.address,
|
from: account.address,
|
||||||
gasLimit: currentState.gasLimit,
|
gasLimit: currentState.gasLimit,
|
||||||
gasPrice: currentState.gasPrice
|
gasPrice: currentState.gasPrice,
|
||||||
});
|
});
|
||||||
txAmount = '0x00';
|
txAmount = '0x00';
|
||||||
txAddress = token.address;
|
txAddress = token.address;
|
||||||
@ -856,7 +809,7 @@ export const onSend = (): AsyncAction => {
|
|||||||
const pendingNonce: number = stateUtils.getPendingNonce(pending);
|
const pendingNonce: number = stateUtils.getPendingNonce(pending);
|
||||||
const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce;
|
const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce;
|
||||||
|
|
||||||
console.warn("NONCE", nonce, account.nonce, pendingNonce)
|
console.warn('NONCE', nonce, account.nonce, pendingNonce);
|
||||||
|
|
||||||
const txData = {
|
const txData = {
|
||||||
address_n,
|
address_n,
|
||||||
@ -870,17 +823,17 @@ export const onSend = (): AsyncAction => {
|
|||||||
gasPrice: w3.toHex(EthereumjsUnits.convert(currentState.gasPrice, 'gwei', 'wei')),
|
gasPrice: w3.toHex(EthereumjsUnits.convert(currentState.gasPrice, 'gwei', 'wei')),
|
||||||
r: '',
|
r: '',
|
||||||
s: '',
|
s: '',
|
||||||
v: ''
|
v: '',
|
||||||
}
|
};
|
||||||
|
|
||||||
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
||||||
if (!selected) return;
|
if (!selected) return;
|
||||||
|
|
||||||
let signedTransaction = await TrezorConnect.ethereumSignTransaction({
|
const signedTransaction = await TrezorConnect.ethereumSignTransaction({
|
||||||
device: {
|
device: {
|
||||||
path: selected.path,
|
path: selected.path,
|
||||||
instance: selected.instance,
|
instance: selected.instance,
|
||||||
state: selected.state
|
state: selected.state,
|
||||||
},
|
},
|
||||||
useEmptyPassphrase: !selected.instance,
|
useEmptyPassphrase: !selected.instance,
|
||||||
path: txData.address_n,
|
path: txData.address_n,
|
||||||
@ -890,11 +843,10 @@ export const onSend = (): AsyncAction => {
|
|||||||
to: strip(txData.to),
|
to: strip(txData.to),
|
||||||
value: strip(txData.value),
|
value: strip(txData.value),
|
||||||
data: strip(txData.data),
|
data: strip(txData.data),
|
||||||
chainId: txData.chainId
|
chainId: txData.chainId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!signedTransaction || !signedTransaction.success) {
|
if (!signedTransaction || !signedTransaction.success) {
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.ADD,
|
type: NOTIFICATION.ADD,
|
||||||
payload: {
|
payload: {
|
||||||
@ -902,26 +854,25 @@ export const onSend = (): AsyncAction => {
|
|||||||
title: 'Transaction error',
|
title: 'Transaction error',
|
||||||
message: signedTransaction.payload.error,
|
message: signedTransaction.payload.error,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
actions: [ ]
|
actions: [],
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
txData.r = '0x' + signedTransaction.payload.r;
|
txData.r = `0x${signedTransaction.payload.r}`;
|
||||||
txData.s = '0x' + signedTransaction.payload.s;
|
txData.s = `0x${signedTransaction.payload.s}`;
|
||||||
txData.v = w3.toHex(signedTransaction.payload.v);
|
txData.v = w3.toHex(signedTransaction.payload.v);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tx = new EthereumjsTx(txData);
|
const tx = new EthereumjsTx(txData);
|
||||||
const serializedTx = '0x' + tx.serialize().toString('hex');
|
const serializedTx = `0x${tx.serialize().toString('hex')}`;
|
||||||
const txid: string = await pushTx(w3, serializedTx);
|
const txid: string = await pushTx(w3, serializedTx);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SEND.TX_COMPLETE,
|
type: SEND.TX_COMPLETE,
|
||||||
account: account,
|
account,
|
||||||
selectedCurrency: currentState.currency,
|
selectedCurrency: currentState.currency,
|
||||||
amount: currentState.amount,
|
amount: currentState.amount,
|
||||||
total: currentState.total,
|
total: currentState.total,
|
||||||
@ -945,12 +896,10 @@ export const onSend = (): AsyncAction => {
|
|||||||
title: 'Transaction success',
|
title: 'Transaction success',
|
||||||
message: `<a href="${network.explorer.tx}${txid}" class="green" target="_blank" rel="noreferrer noopener">See transaction detail</a>`,
|
message: `<a href="${network.explorer.tx}${txid}" class="green" target="_blank" rel="noreferrer noopener">See transaction detail</a>`,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
actions: []
|
actions: [],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.ADD,
|
type: NOTIFICATION.ADD,
|
||||||
payload: {
|
payload: {
|
||||||
@ -958,12 +907,11 @@ export const onSend = (): AsyncAction => {
|
|||||||
title: 'Transaction error',
|
title: 'Transaction error',
|
||||||
message: error.message || error,
|
message: error.message || error,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
actions: [ ]
|
actions: [],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
toggleAdvanced,
|
toggleAdvanced,
|
||||||
@ -978,4 +926,4 @@ export default {
|
|||||||
onNonceChange,
|
onNonceChange,
|
||||||
onDataChange,
|
onDataChange,
|
||||||
onSend,
|
onSend,
|
||||||
}
|
};
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import type { State as SendFormState } from '../reducers/SendFormReducer';
|
import type { State as SendFormState } from '../reducers/SendFormReducer';
|
||||||
import type {
|
import type {
|
||||||
@ -10,8 +10,7 @@ import type {
|
|||||||
|
|
||||||
const PREFIX: string = 'trezor:draft-tx:';
|
const PREFIX: string = 'trezor:draft-tx:';
|
||||||
|
|
||||||
export const save = (): ThunkAction => {
|
export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
if (typeof window.localStorage === 'undefined') return;
|
||||||
|
|
||||||
const location = getState().router.location.pathname;
|
const location = getState().router.location.pathname;
|
||||||
@ -20,14 +19,12 @@ export const save = (): ThunkAction => {
|
|||||||
try {
|
try {
|
||||||
window.sessionStorage.setItem(`${PREFIX}${location}`, JSON.stringify(state));
|
window.sessionStorage.setItem(`${PREFIX}${location}`, JSON.stringify(state));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Saving sessionStorage error: " + error)
|
console.error(`Saving sessionStorage error: ${error}`);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const load = (location: string): ?SendFormState => {
|
export const load = (location: string): ?SendFormState => {
|
||||||
|
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
if (typeof window.localStorage === 'undefined') return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -39,20 +36,16 @@ export const load = (location: string): ?SendFormState => {
|
|||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Loading sessionStorage error: " + error)
|
console.error(`Loading sessionStorage error: ${error}`);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return;
|
export const clear = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
}
|
|
||||||
|
|
||||||
export const clear = (): ThunkAction => {
|
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
if (typeof window.localStorage === 'undefined') return;
|
if (typeof window.localStorage === 'undefined') return;
|
||||||
const location = getState().router.location.pathname;
|
const location = getState().router.location.pathname;
|
||||||
try {
|
try {
|
||||||
window.sessionStorage.removeItem(`${PREFIX}${location}`);
|
window.sessionStorage.removeItem(`${PREFIX}${location}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Clearing sessionStorage error: " + error)
|
console.error(`Clearing sessionStorage error: ${error}`);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import EthereumjsUtil from 'ethereumjs-util';
|
import EthereumjsUtil from 'ethereumjs-util';
|
||||||
import * as SUMMARY from './constants/summary';
|
import * as SUMMARY from './constants/summary';
|
||||||
@ -7,7 +7,9 @@ import * as TOKEN from './constants/token';
|
|||||||
import { resolveAfter } from '../utils/promiseUtils';
|
import { resolveAfter } from '../utils/promiseUtils';
|
||||||
import { initialState } from '../reducers/SummaryReducer';
|
import { initialState } from '../reducers/SummaryReducer';
|
||||||
|
|
||||||
import type { ThunkAction, AsyncAction, Action, GetState, Dispatch } from '~/flowtype';
|
import type {
|
||||||
|
ThunkAction, AsyncAction, Action, GetState, Dispatch,
|
||||||
|
} from '~/flowtype';
|
||||||
import type { State } from '../reducers/SummaryReducer';
|
import type { State } from '../reducers/SummaryReducer';
|
||||||
import type { Token } from '../reducers/TokensReducer';
|
import type { Token } from '../reducers/TokensReducer';
|
||||||
|
|
||||||
@ -20,29 +22,21 @@ export type SummaryAction = {
|
|||||||
type: typeof SUMMARY.DETAILS_TOGGLE
|
type: typeof SUMMARY.DETAILS_TOGGLE
|
||||||
}
|
}
|
||||||
|
|
||||||
export const init = (): ThunkAction => {
|
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
const state: State = {
|
const state: State = {
|
||||||
...initialState,
|
...initialState,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SUMMARY.INIT,
|
type: SUMMARY.INIT,
|
||||||
state: state
|
state,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const dispose = (): Action => {
|
export const dispose = (): Action => ({
|
||||||
return {
|
type: SUMMARY.DISPOSE,
|
||||||
type: SUMMARY.DISPOSE
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const onDetailsToggle = (): Action => {
|
|
||||||
return {
|
|
||||||
type: SUMMARY.DETAILS_TOGGLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export const onDetailsToggle = (): Action => ({
|
||||||
|
type: SUMMARY.DETAILS_TOGGLE,
|
||||||
|
});
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as TOKEN from './constants/token';
|
import * as TOKEN from './constants/token';
|
||||||
import { getTokenInfoAsync, getTokenBalanceAsync } from './Web3Actions';
|
import { getTokenInfoAsync, getTokenBalanceAsync } from './Web3Actions';
|
||||||
|
|
||||||
import type { GetState, AsyncAction, Action, Dispatch } from '~/flowtype';
|
import type {
|
||||||
|
GetState, AsyncAction, Action, Dispatch,
|
||||||
|
} from '~/flowtype';
|
||||||
import type { State, Token } from '../reducers/TokensReducer';
|
import type { State, Token } from '../reducers/TokensReducer';
|
||||||
import type { Account } from '../reducers/AccountsReducer';
|
import type { Account } from '../reducers/AccountsReducer';
|
||||||
import type { NetworkToken } from '../reducers/LocalStorageReducer';
|
import type { NetworkToken } from '../reducers/LocalStorageReducer';
|
||||||
@ -29,42 +31,32 @@ type SelectOptions = {
|
|||||||
|
|
||||||
|
|
||||||
// action from component <reactSelect>
|
// action from component <reactSelect>
|
||||||
export const load = (input: string, network: string): AsyncAction => {
|
export const load = (input: string, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<any> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<any> => {
|
|
||||||
|
|
||||||
if (input.length < 1) return;
|
if (input.length < 1) return;
|
||||||
|
|
||||||
const tokens = getState().localStorage.tokens[network];
|
const tokens = getState().localStorage.tokens[network];
|
||||||
const value = input.toLowerCase();
|
const value = input.toLowerCase();
|
||||||
const result = tokens.filter(t =>
|
const result = tokens.filter(t => t.symbol.toLowerCase().indexOf(value) >= 0
|
||||||
t.symbol.toLowerCase().indexOf(value) >= 0 ||
|
|| t.address.toLowerCase().indexOf(value) >= 0
|
||||||
t.address.toLowerCase().indexOf(value) >= 0 ||
|
|| t.name.toLowerCase().indexOf(value) >= 0);
|
||||||
t.name.toLowerCase().indexOf(value) >= 0
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
return { options: result };
|
return { options: result };
|
||||||
} else {
|
}
|
||||||
const web3instance = getState().web3.find(w3 => w3.network === network);
|
const web3instance = getState().web3.find(w3 => w3.network === network);
|
||||||
if (!web3instance) return;
|
if (!web3instance) return;
|
||||||
|
|
||||||
const info = await getTokenInfoAsync(web3instance.erc20, input);
|
const info = await getTokenInfoAsync(web3instance.erc20, input);
|
||||||
if (info) {
|
if (info) {
|
||||||
return {
|
return {
|
||||||
options: [ info ]
|
options: [info],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
//await resolveAfter(300000);
|
//await resolveAfter(300000);
|
||||||
//await resolveAfter(3000);
|
//await resolveAfter(3000);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const add = (token: NetworkToken, account: Account): AsyncAction => {
|
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
const web3instance = getState().web3.find(w3 => w3.network === account.network);
|
const web3instance = getState().web3.find(w3 => w3.network === account.network);
|
||||||
if (!web3instance) return;
|
if (!web3instance) return;
|
||||||
|
|
||||||
@ -77,23 +69,21 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => {
|
|||||||
address: token.address,
|
address: token.address,
|
||||||
ethAddress: account.address,
|
ethAddress: account.address,
|
||||||
decimals: token.decimals,
|
decimals: token.decimals,
|
||||||
balance: '0'
|
balance: '0',
|
||||||
}
|
};
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOKEN.ADD,
|
type: TOKEN.ADD,
|
||||||
payload: tkn
|
payload: tkn,
|
||||||
});
|
});
|
||||||
|
|
||||||
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, tkn);
|
const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, tkn);
|
||||||
dispatch( setBalance(token.address, account.address, tokenBalance) )
|
dispatch(setBalance(token.address, account.address, tokenBalance));
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => {
|
export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
const newState: Array<Token> = [...getState().tokens];
|
const newState: Array<Token> = [...getState().tokens];
|
||||||
let token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress);
|
const token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress);
|
||||||
if (token) {
|
if (token) {
|
||||||
token.loaded = true;
|
token.loaded = true;
|
||||||
token.balance = balance;
|
token.balance = balance;
|
||||||
@ -101,14 +91,11 @@ export const setBalance = (tokenAddress: string, ethAddress: string, balance: st
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOKEN.SET_BALANCE,
|
type: TOKEN.SET_BALANCE,
|
||||||
payload: newState
|
payload: newState,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const remove = (token: Token): Action => {
|
export const remove = (token: Token): Action => ({
|
||||||
return {
|
|
||||||
type: TOKEN.REMOVE,
|
type: TOKEN.REMOVE,
|
||||||
token
|
token,
|
||||||
}
|
});
|
||||||
}
|
|
@ -1,7 +1,9 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import TrezorConnect, { UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT } from 'trezor-connect';
|
|
||||||
|
import TrezorConnect, {
|
||||||
|
UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT,
|
||||||
|
} from 'trezor-connect';
|
||||||
import * as TOKEN from './constants/token';
|
import * as TOKEN from './constants/token';
|
||||||
import * as CONNECT from './constants/TrezorConnect';
|
import * as CONNECT from './constants/TrezorConnect';
|
||||||
import * as NOTIFICATION from './constants/notification';
|
import * as NOTIFICATION from './constants/notification';
|
||||||
@ -20,7 +22,7 @@ import type {
|
|||||||
TransportMessage,
|
TransportMessage,
|
||||||
DeviceMessageType,
|
DeviceMessageType,
|
||||||
TransportMessageType,
|
TransportMessageType,
|
||||||
UiMessageType
|
UiMessageType,
|
||||||
} from 'trezor-connect';
|
} from 'trezor-connect';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@ -30,7 +32,7 @@ import type {
|
|||||||
ThunkAction,
|
ThunkAction,
|
||||||
AsyncAction,
|
AsyncAction,
|
||||||
TrezorDevice,
|
TrezorDevice,
|
||||||
RouterLocationState
|
RouterLocationState,
|
||||||
} from '~/flowtype';
|
} from '~/flowtype';
|
||||||
|
|
||||||
|
|
||||||
@ -81,15 +83,14 @@ export type TrezorConnectAction = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const init = (): AsyncAction => {
|
export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
// set listeners
|
// set listeners
|
||||||
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
|
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
|
||||||
// post event to reducers
|
// post event to reducers
|
||||||
const type: DeviceMessageType = event.type; // assert flow type
|
const type: DeviceMessageType = event.type; // assert flow type
|
||||||
dispatch({
|
dispatch({
|
||||||
type,
|
type,
|
||||||
device: event.payload
|
device: event.payload,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,7 +99,7 @@ export const init = (): AsyncAction => {
|
|||||||
const type: UiMessageType = event.type; // assert flow type
|
const type: UiMessageType = event.type; // assert flow type
|
||||||
dispatch({
|
dispatch({
|
||||||
type,
|
type,
|
||||||
payload: event.payload
|
payload: event.payload,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,7 +108,7 @@ export const init = (): AsyncAction => {
|
|||||||
const type: TransportMessageType = event.type; // assert flow type
|
const type: TransportMessageType = event.type; // assert flow type
|
||||||
dispatch({
|
dispatch({
|
||||||
type,
|
type,
|
||||||
payload: event.payload
|
payload: event.payload,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -119,7 +120,7 @@ export const init = (): AsyncAction => {
|
|||||||
debug: false,
|
debug: false,
|
||||||
popup: false,
|
popup: false,
|
||||||
webusb: true,
|
webusb: true,
|
||||||
pendingTransportEvent: (getState().devices.length < 1)
|
pendingTransportEvent: (getState().devices.length < 1),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// dispatch({
|
// dispatch({
|
||||||
@ -127,17 +128,14 @@ export const init = (): AsyncAction => {
|
|||||||
// error
|
// error
|
||||||
// })
|
// })
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// called after backend was initialized
|
// called after backend was initialized
|
||||||
// set listeners for connect/disconnect
|
// set listeners for connect/disconnect
|
||||||
export const postInit = (): ThunkAction => {
|
export const postInit = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
const handleDeviceConnect = (device: Device) => {
|
const handleDeviceConnect = (device: Device) => {
|
||||||
dispatch(initConnectedDevice(device));
|
dispatch(initConnectedDevice(device));
|
||||||
}
|
};
|
||||||
|
|
||||||
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
|
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
|
||||||
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
|
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
|
||||||
@ -168,7 +166,7 @@ export const postInit = (): ThunkAction => {
|
|||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
if (initialParams) {
|
if (initialParams) {
|
||||||
if (!initialParams.hasOwnProperty("network") && initialPathname !== getState().router.location.pathname) {
|
if (!initialParams.hasOwnProperty('network') && initialPathname !== getState().router.location.pathname) {
|
||||||
// dispatch( push(initialPathname) );
|
// dispatch( push(initialPathname) );
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -176,21 +174,16 @@ export const postInit = (): ThunkAction => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => {
|
const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => devices.sort((a, b) => {
|
||||||
return devices.sort((a, b) => {
|
|
||||||
if (!a.ts || !b.ts) {
|
if (!a.ts || !b.ts) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
}
|
||||||
return a.ts > b.ts ? -1 : 1;
|
return a.ts > b.ts ? -1 : 1;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => {
|
export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
const selected = getState().wallet.selectedDevice;
|
||||||
// if (!selected || (selected && selected.state)) {
|
// if (!selected || (selected && selected.state)) {
|
||||||
dispatch(onSelectDevice(device));
|
dispatch(onSelectDevice(device));
|
||||||
@ -200,15 +193,13 @@ export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction
|
|||||||
// } else if (!selected) {
|
// } else if (!selected) {
|
||||||
// dispatch( onSelectDevice(device) );
|
// dispatch( onSelectDevice(device) );
|
||||||
// }
|
// }
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// selection from Aside dropdown button
|
// selection from Aside dropdown button
|
||||||
// after device_connect event
|
// after device_connect event
|
||||||
// or after acquiring device
|
// or after acquiring device
|
||||||
// device type could be local TrezorDevice or Device (from trezor-connect device_connect event)
|
// device type could be local TrezorDevice or Device (from trezor-connect device_connect event)
|
||||||
export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => {
|
export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
// || device.isUsedElsewhere
|
// || device.isUsedElsewhere
|
||||||
|
|
||||||
// switch to initial url and reset this value
|
// switch to initial url and reset this value
|
||||||
@ -222,7 +213,6 @@ export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => {
|
|||||||
} else if (typeof device.instance === 'number') {
|
} else if (typeof device.instance === 'number') {
|
||||||
dispatch(push(`/device/${device.features.device_id}:${device.instance}`));
|
dispatch(push(`/device/${device.features.device_id}:${device.instance}`));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
const deviceId: string = device.features.device_id;
|
const deviceId: string = device.features.device_id;
|
||||||
const urlParams: RouterLocationState = getState().router.location.state;
|
const urlParams: RouterLocationState = getState().router.location.state;
|
||||||
// let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`;
|
// let url: string = `/device/${ device.features.device_id }/network/ethereum/account/0`;
|
||||||
@ -247,12 +237,9 @@ export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => {
|
|||||||
dispatch(push(url));
|
dispatch(push(url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const switchToFirstAvailableDevice = (): AsyncAction => {
|
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
|
export const switchToFirstAvailableDevice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const { devices } = getState();
|
const { devices } = getState();
|
||||||
if (devices.length > 0) {
|
if (devices.length > 0) {
|
||||||
// TODO: Priority:
|
// TODO: Priority:
|
||||||
@ -270,23 +257,20 @@ export const switchToFirstAvailableDevice = (): AsyncAction => {
|
|||||||
} else {
|
} else {
|
||||||
dispatch(push('/'));
|
dispatch(push('/'));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const getSelectedDeviceState = (): AsyncAction => {
|
export const getSelectedDeviceState = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
const selected = getState().wallet.selectedDevice;
|
const selected = getState().wallet.selectedDevice;
|
||||||
if (selected
|
if (selected
|
||||||
&& selected.connected
|
&& selected.connected
|
||||||
&& (selected.features && !selected.features.bootloader_mode && selected.features.initialized)
|
&& (selected.features && !selected.features.bootloader_mode && selected.features.initialized)
|
||||||
&& !selected.state) {
|
&& !selected.state) {
|
||||||
|
|
||||||
const response = await TrezorConnect.getDeviceState({
|
const response = await TrezorConnect.getDeviceState({
|
||||||
device: {
|
device: {
|
||||||
path: selected.path,
|
path: selected.path,
|
||||||
instance: selected.instance,
|
instance: selected.instance,
|
||||||
state: selected.state
|
state: selected.state,
|
||||||
},
|
},
|
||||||
useEmptyPassphrase: !selected.instance,
|
useEmptyPassphrase: !selected.instance,
|
||||||
});
|
});
|
||||||
@ -295,7 +279,7 @@ export const getSelectedDeviceState = (): AsyncAction => {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.AUTH_DEVICE,
|
type: CONNECT.AUTH_DEVICE,
|
||||||
device: selected,
|
device: selected,
|
||||||
state: response.payload.state
|
state: response.payload.state,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -312,22 +296,19 @@ export const getSelectedDeviceState = (): AsyncAction => {
|
|||||||
callback: () => {
|
callback: () => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NOTIFICATION.CLOSE,
|
type: NOTIFICATION.CLOSE,
|
||||||
payload: { devicePath: selected.path }
|
payload: { devicePath: selected.path },
|
||||||
});
|
});
|
||||||
dispatch(getSelectedDeviceState());
|
dispatch(getSelectedDeviceState());
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const deviceDisconnect = (device: Device): AsyncAction => {
|
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
|
export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
||||||
|
|
||||||
if (device && device.features) {
|
if (device && device.features) {
|
||||||
@ -344,11 +325,9 @@ export const deviceDisconnect = (device: Device): AsyncAction => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const coinChanged = (network: ?string): ThunkAction => {
|
export const coinChanged = (network: ?string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
||||||
if (!selected) return;
|
if (!selected) return;
|
||||||
|
|
||||||
@ -357,23 +336,21 @@ export const coinChanged = (network: ?string): ThunkAction => {
|
|||||||
if (network) {
|
if (network) {
|
||||||
dispatch(DiscoveryActions.start(selected, network));
|
dispatch(DiscoveryActions.start(selected, network));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export function reload(): AsyncAction {
|
export function reload(): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function acquire(): AsyncAction {
|
export function acquire(): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
const selected: ?TrezorDevice = getState().wallet.selectedDevice;
|
||||||
if (!selected) return;
|
if (!selected) return;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.START_ACQUIRING,
|
type: CONNECT.START_ACQUIRING,
|
||||||
})
|
});
|
||||||
|
|
||||||
const response = await TrezorConnect.getFeatures({
|
const response = await TrezorConnect.getFeatures({
|
||||||
device: {
|
device: {
|
||||||
@ -398,41 +375,35 @@ export function acquire(): AsyncAction {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ]
|
// ]
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.STOP_ACQUIRING,
|
type: CONNECT.STOP_ACQUIRING,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => {
|
export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
if (device.features) {
|
if (device.features) {
|
||||||
const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`;
|
const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`;
|
||||||
dispatch(push(`/device/${devUrl}/settings`));
|
dispatch(push(`/device/${devUrl}/settings`));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// called from Aside - device menu (forget single instance)
|
// called from Aside - device menu (forget single instance)
|
||||||
export const forget = (device: TrezorDevice): Action => {
|
export const forget = (device: TrezorDevice): Action => ({
|
||||||
return {
|
|
||||||
type: CONNECT.FORGET_REQUEST,
|
type: CONNECT.FORGET_REQUEST,
|
||||||
device
|
device,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const duplicateDevice = (device: TrezorDevice): AsyncAction => {
|
export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CONNECT.TRY_TO_DUPLICATE,
|
type: CONNECT.TRY_TO_DUPLICATE,
|
||||||
device
|
device,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function addAccount(): ThunkAction {
|
export function addAccount(): ThunkAction {
|
||||||
@ -440,5 +411,5 @@ export function addAccount(): ThunkAction {
|
|||||||
const selected = getState().wallet.selectedDevice;
|
const selected = getState().wallet.selectedDevice;
|
||||||
if (!selected) return;
|
if (!selected) return;
|
||||||
dispatch(DiscoveryActions.start(selected, getState().router.location.state.network, true)); // TODO: network nicer
|
dispatch(DiscoveryActions.start(selected, getState().router.location.state.network, true)); // TODO: network nicer
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import { LOCATION_CHANGE } from 'react-router-redux';
|
import { LOCATION_CHANGE } from 'react-router-redux';
|
||||||
import * as WALLET from './constants/wallet';
|
import * as WALLET from './constants/wallet';
|
||||||
@ -21,7 +21,7 @@ import type
|
|||||||
Action,
|
Action,
|
||||||
Dispatch,
|
Dispatch,
|
||||||
GetState,
|
GetState,
|
||||||
State
|
State,
|
||||||
} from '~/flowtype';
|
} from '~/flowtype';
|
||||||
|
|
||||||
export type WalletAction = {
|
export type WalletAction = {
|
||||||
@ -47,62 +47,48 @@ export type WalletAction = {
|
|||||||
devices: Array<TrezorDevice>
|
devices: Array<TrezorDevice>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const init = (): ThunkAction => {
|
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
const updateOnlineStatus = (event) => {
|
const updateOnlineStatus = (event) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WALLET.ONLINE_STATUS,
|
type: WALLET.ONLINE_STATUS,
|
||||||
online: navigator.onLine
|
online: navigator.onLine,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
window.addEventListener('online', updateOnlineStatus);
|
window.addEventListener('online', updateOnlineStatus);
|
||||||
window.addEventListener('offline', updateOnlineStatus);
|
window.addEventListener('offline', updateOnlineStatus);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export const onBeforeUnload = (): WalletAction => {
|
export const onBeforeUnload = (): WalletAction => ({
|
||||||
return {
|
type: WALLET.ON_BEFORE_UNLOAD,
|
||||||
type: WALLET.ON_BEFORE_UNLOAD
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const toggleDeviceDropdown = (opened: boolean): WalletAction => {
|
export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({
|
||||||
return {
|
|
||||||
type: WALLET.TOGGLE_DEVICE_DROPDOWN,
|
type: WALLET.TOGGLE_DEVICE_DROPDOWN,
|
||||||
opened
|
opened,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// This method will be called after each DEVICE.CONNECT action
|
// This method will be called after each DEVICE.CONNECT action
|
||||||
// if connected device has different "passphrase_protection" settings than saved instances
|
// if connected device has different "passphrase_protection" settings than saved instances
|
||||||
// all saved instances will be removed immediately inside DevicesReducer
|
// all saved instances will be removed immediately inside DevicesReducer
|
||||||
// This method will clear leftovers associated with removed instances from reducers.
|
// This method will clear leftovers associated with removed instances from reducers.
|
||||||
// (DiscoveryReducer, AccountReducer, TokensReducer)
|
// (DiscoveryReducer, AccountReducer, TokensReducer)
|
||||||
export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => {
|
export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
|
||||||
return (dispatch: Dispatch, getState: GetState): void => {
|
|
||||||
|
|
||||||
if (!device.features) return;
|
if (!device.features) return;
|
||||||
|
|
||||||
const affectedDevices = prevState.devices.filter(d =>
|
const affectedDevices = prevState.devices.filter(d => d.features
|
||||||
d.features
|
|
||||||
&& d.features.device_id === device.features.device_id
|
&& d.features.device_id === device.features.device_id
|
||||||
&& d.features.passphrase_protection !== device.features.passphrase_protection
|
&& d.features.passphrase_protection !== device.features.passphrase_protection);
|
||||||
);
|
|
||||||
|
|
||||||
if (affectedDevices.length > 0) {
|
if (affectedDevices.length > 0) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
|
type: WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA,
|
||||||
devices: affectedDevices,
|
devices: affectedDevices,
|
||||||
})
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => {
|
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
const locationChange: boolean = action.type === LOCATION_CHANGE;
|
const locationChange: boolean = action.type === LOCATION_CHANGE;
|
||||||
const state: State = getState();
|
const state: State = getState();
|
||||||
|
|
||||||
@ -113,16 +99,14 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
|
|||||||
if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
|
if (device && stateUtils.isSelectedDevice(state.wallet.selectedDevice, device)) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WALLET.UPDATE_SELECTED_DEVICE,
|
type: WALLET.UPDATE_SELECTED_DEVICE,
|
||||||
device
|
device,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WALLET.SET_SELECTED_DEVICE,
|
type: WALLET.SET_SELECTED_DEVICE,
|
||||||
device
|
device,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import Web3 from 'web3';
|
import Web3 from 'web3';
|
||||||
import HDKey from 'hdkey';
|
import HDKey from 'hdkey';
|
||||||
@ -7,11 +7,14 @@ import HDKey from 'hdkey';
|
|||||||
import EthereumjsUtil from 'ethereumjs-util';
|
import EthereumjsUtil from 'ethereumjs-util';
|
||||||
import EthereumjsTx from 'ethereumjs-tx';
|
import EthereumjsTx from 'ethereumjs-tx';
|
||||||
import TrezorConnect from 'trezor-connect';
|
import TrezorConnect from 'trezor-connect';
|
||||||
|
import type { ContractFactory, EstimateGasOptions } from 'web3';
|
||||||
|
import type BigNumber from 'bignumber.js';
|
||||||
|
import type { TransactionStatus, TransactionReceipt } from 'web3';
|
||||||
import { strip } from '../utils/ethUtils';
|
import { strip } from '../utils/ethUtils';
|
||||||
import * as WEB3 from './constants/web3';
|
import * as WEB3 from './constants/web3';
|
||||||
import * as PENDING from './constants/pendingTx';
|
import * as PENDING from './constants/pendingTx';
|
||||||
import * as AccountsActions from '../actions/AccountsActions';
|
import * as AccountsActions from './AccountsActions';
|
||||||
import * as TokenActions from '../actions/TokenActions';
|
import * as TokenActions from './TokenActions';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
@ -19,15 +22,12 @@ import type {
|
|||||||
Action,
|
Action,
|
||||||
AsyncAction,
|
AsyncAction,
|
||||||
} from '~/flowtype';
|
} from '~/flowtype';
|
||||||
import type { ContractFactory, EstimateGasOptions } from 'web3';
|
|
||||||
import type BigNumber from 'bignumber.js';
|
|
||||||
|
|
||||||
import type { Account } from '../reducers/AccountsReducer';
|
import type { Account } from '../reducers/AccountsReducer';
|
||||||
import type { PendingTx } from '../reducers/PendingTxReducer';
|
import type { PendingTx } from '../reducers/PendingTxReducer';
|
||||||
import type { Web3Instance } from '../reducers/Web3Reducer';
|
import type { Web3Instance } from '../reducers/Web3Reducer';
|
||||||
import type { Token } from '../reducers/TokensReducer';
|
import type { Token } from '../reducers/TokensReducer';
|
||||||
import type { NetworkToken } from '../reducers/LocalStorageReducer';
|
import type { NetworkToken } from '../reducers/LocalStorageReducer';
|
||||||
import type { TransactionStatus, TransactionReceipt } from 'web3';
|
|
||||||
|
|
||||||
export type Web3Action = {
|
export type Web3Action = {
|
||||||
type: typeof WEB3.READY,
|
type: typeof WEB3.READY,
|
||||||
@ -53,7 +53,6 @@ export type Web3UpdateGasPriceAction = {
|
|||||||
|
|
||||||
export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
const { config, ERC20Abi } = getState().localStorage;
|
const { config, ERC20Abi } = getState().localStorage;
|
||||||
|
|
||||||
const coin = config.coins[coinIndex];
|
const coin = config.coins[coinIndex];
|
||||||
@ -72,12 +71,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
|
|
||||||
if (instance) {
|
if (instance) {
|
||||||
const currentHost = instance.currentProvider.host;
|
const currentHost = instance.currentProvider.host;
|
||||||
let currentHostIndex: number = urls.indexOf(currentHost);
|
const currentHostIndex: number = urls.indexOf(currentHost);
|
||||||
|
|
||||||
if (currentHostIndex + 1 < urls.length) {
|
if (currentHostIndex + 1 < urls.length) {
|
||||||
web3host = urls[currentHostIndex + 1];
|
web3host = urls[currentHostIndex + 1];
|
||||||
} else {
|
} else {
|
||||||
console.error("TODO: Backend " + network + " not working", instance.currentProvider );
|
console.error(`TODO: Backend ${network} not working`, instance.currentProvider);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WEB3.CREATE,
|
type: WEB3.CREATE,
|
||||||
@ -88,7 +87,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
erc20: instance.eth.contract(ERC20Abi),
|
erc20: instance.eth.contract(ERC20Abi),
|
||||||
latestBlock: '0',
|
latestBlock: '0',
|
||||||
gasPrice: '0',
|
gasPrice: '0',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// try next coin
|
// try next coin
|
||||||
@ -122,12 +121,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
type: WEB3.CREATE,
|
type: WEB3.CREATE,
|
||||||
instance: {
|
instance: {
|
||||||
network,
|
network,
|
||||||
web3: web3,
|
web3,
|
||||||
chainId: coin.chainId,
|
chainId: coin.chainId,
|
||||||
erc20,
|
erc20,
|
||||||
latestBlock: '0',
|
latestBlock: '0',
|
||||||
gasPrice: '0',
|
gasPrice: '0',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// dispatch({
|
// dispatch({
|
||||||
@ -137,8 +136,6 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// console.log("GET CHAIN", instance.version.network)
|
// console.log("GET CHAIN", instance.version.network)
|
||||||
|
|
||||||
// instance.version.getWhisper((err, shh) => {
|
// instance.version.getWhisper((err, shh) => {
|
||||||
@ -157,10 +154,9 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
|
|
||||||
const onBlockMined = async (error: ?Error, blockHash: ?string) => {
|
const onBlockMined = async (error: ?Error, blockHash: ?string) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
// try again
|
// try again
|
||||||
onBlockMined(new Error("manually_triggered_error"), undefined);
|
onBlockMined(new Error('manually_triggered_error'), undefined);
|
||||||
}, 30000);
|
}, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +164,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: WEB3.BLOCK_UPDATED,
|
type: WEB3.BLOCK_UPDATED,
|
||||||
network,
|
network,
|
||||||
blockHash
|
blockHash,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +183,6 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
}
|
}
|
||||||
dispatch(getBalance(account));
|
dispatch(getBalance(account));
|
||||||
// dispatch( getNonce(account) );
|
// dispatch( getNonce(account) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokens = getState().tokens.filter(t => t.network === network);
|
const tokens = getState().tokens.filter(t => t.network === network);
|
||||||
@ -201,11 +196,10 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
for (const tx of pending) {
|
for (const tx of pending) {
|
||||||
dispatch(getTransactionReceipt(tx));
|
dispatch(getTransactionReceipt(tx));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// latestBlockFilter.watch(onBlockMined);
|
// latestBlockFilter.watch(onBlockMined);
|
||||||
onBlockMined(new Error("manually_triggered_error"), undefined);
|
onBlockMined(new Error('manually_triggered_error'), undefined);
|
||||||
|
|
||||||
|
|
||||||
// init next coin
|
// init next coin
|
||||||
@ -217,13 +211,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
|
|||||||
// instance2.eth.getGasPrice((error, gasPrice) => {
|
// instance2.eth.getGasPrice((error, gasPrice) => {
|
||||||
// console.log("---gasss price from UBQ", gasPrice)
|
// console.log("---gasss price from UBQ", gasPrice)
|
||||||
// });
|
// });
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getGasPrice(network: string): AsyncAction {
|
export function getGasPrice(network: string): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
const index: number = getState().web3.findIndex(w3 => w3.network === network);
|
const index: number = getState().web3.findIndex(w3 => w3.network === network);
|
||||||
|
|
||||||
const web3instance = getState().web3[index];
|
const web3instance = getState().web3[index];
|
||||||
@ -233,18 +226,17 @@ export function getGasPrice(network: string): AsyncAction {
|
|||||||
if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) {
|
if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: WEB3.GAS_PRICE_UPDATED,
|
type: WEB3.GAS_PRICE_UPDATED,
|
||||||
network: network,
|
network,
|
||||||
gasPrice
|
gasPrice,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBalance(account: Account): AsyncAction {
|
export function getBalance(account: Account): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
|
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
|
||||||
const web3: Web3 = web3instance.web3;
|
const web3: Web3 = web3instance.web3;
|
||||||
|
|
||||||
@ -256,19 +248,18 @@ export function getBalance(account: Account): AsyncAction {
|
|||||||
account.address,
|
account.address,
|
||||||
account.network,
|
account.network,
|
||||||
account.deviceState,
|
account.deviceState,
|
||||||
newBalance
|
newBalance,
|
||||||
));
|
));
|
||||||
|
|
||||||
// dispatch( loadHistory(addr) );
|
// dispatch( loadHistory(addr) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTokenBalance(token: Token): AsyncAction {
|
export function getTokenBalance(token: Token): AsyncAction {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
const web3instance = getState().web3.filter(w3 => w3.network === token.network)[0];
|
const web3instance = getState().web3.filter(w3 => w3.network === token.network)[0];
|
||||||
const web3 = web3instance.web3;
|
const web3 = web3instance.web3;
|
||||||
const contract = web3instance.erc20.at(token.address);
|
const contract = web3instance.erc20.at(token.address);
|
||||||
@ -280,18 +271,16 @@ export function getTokenBalance(token: Token): AsyncAction {
|
|||||||
dispatch(TokenActions.setBalance(
|
dispatch(TokenActions.setBalance(
|
||||||
token.address,
|
token.address,
|
||||||
token.ethAddress,
|
token.ethAddress,
|
||||||
newBalance
|
newBalance,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNonce(account: Account): AsyncAction {
|
export function getNonce(account: Account): AsyncAction {
|
||||||
|
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
|
|
||||||
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
|
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
|
||||||
const web3 = web3instance.web3;
|
const web3 = web3instance.web3;
|
||||||
|
|
||||||
@ -302,12 +291,10 @@ export function getNonce(account: Account): AsyncAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTransactionReceipt = (tx: PendingTx): AsyncAction => {
|
export const getTransactionReceipt = (tx: PendingTx): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
||||||
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
|
|
||||||
|
|
||||||
const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0];
|
const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0];
|
||||||
const web3 = web3instance.web3;
|
const web3 = web3instance.web3;
|
||||||
|
|
||||||
@ -316,31 +303,28 @@ export const getTransactionReceipt = (tx: PendingTx): AsyncAction => {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: PENDING.TX_NOT_FOUND,
|
type: PENDING.TX_NOT_FOUND,
|
||||||
tx,
|
tx,
|
||||||
})
|
});
|
||||||
} else if (status && status.blockNumber) {
|
} else if (status && status.blockNumber) {
|
||||||
web3.eth.getTransactionReceipt(tx.id, (error: Error, receipt: TransactionReceipt) => {
|
web3.eth.getTransactionReceipt(tx.id, (error: Error, receipt: TransactionReceipt) => {
|
||||||
if (receipt) {
|
if (receipt) {
|
||||||
if (status.gas !== receipt.gasUsed) {
|
if (status.gas !== receipt.gasUsed) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: PENDING.TX_TOKEN_ERROR,
|
type: PENDING.TX_TOKEN_ERROR,
|
||||||
tx
|
tx,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: PENDING.TX_RESOLVED,
|
type: PENDING.TX_RESOLVED,
|
||||||
tx,
|
tx,
|
||||||
receipt
|
receipt,
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getTransaction = (web3: Web3, txid: string): Promise<any> => {
|
export const getTransaction = (web3: Web3, txid: string): Promise<any> => new Promise((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
web3.eth.getTransaction(txid, (error, result) => {
|
web3.eth.getTransaction(txid, (error, result) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@ -349,10 +333,8 @@ export const getTransaction = (web3: Web3, txid: string): Promise<any> => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber> => {
|
export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber> => new Promise((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
web3.eth.getBalance(address, (error: Error, result: BigNumber) => {
|
web3.eth.getBalance(address, (error: Error, result: BigNumber) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@ -361,11 +343,8 @@ export const getBalanceAsync = (web3: Web3, address: string): Promise<BigNumber>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Promise<string> => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
|
|
||||||
|
export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Promise<string> => new Promise((resolve, reject) => {
|
||||||
const contract = erc20.at(token.address);
|
const contract = erc20.at(token.address);
|
||||||
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
|
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -376,10 +355,8 @@ export const getTokenBalanceAsync = (erc20: ContractFactory, token: Token): Prom
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const getNonceAsync = (web3: Web3, address: string): Promise<number> => {
|
export const getNonceAsync = (web3: Web3, address: string): Promise<number> => new Promise((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
web3.eth.getTransactionCount(address, (error: Error, result: number) => {
|
web3.eth.getTransactionCount(address, (error: Error, result: number) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@ -388,12 +365,9 @@ export const getNonceAsync = (web3: Web3, address: string): Promise<number> => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => {
|
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => new Promise((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
|
|
||||||
const contract = erc20.at(address, (error, res) => {
|
const contract = erc20.at(address, (error, res) => {
|
||||||
// console.warn("callback", error, res)
|
// console.warn("callback", error, res)
|
||||||
});
|
});
|
||||||
@ -402,24 +376,24 @@ export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Prom
|
|||||||
address,
|
address,
|
||||||
name: '',
|
name: '',
|
||||||
symbol: '',
|
symbol: '',
|
||||||
decimals: 0
|
decimals: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
contract.name.call((error: Error, name: string) => {
|
contract.name.call((error: Error, name: string) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
info.name = name;
|
|
||||||
}
|
}
|
||||||
|
info.name = name;
|
||||||
|
|
||||||
|
|
||||||
contract.symbol.call((error: Error, symbol: string) => {
|
contract.symbol.call((error: Error, symbol: string) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
info.symbol = symbol;
|
|
||||||
}
|
}
|
||||||
|
info.symbol = symbol;
|
||||||
|
|
||||||
|
|
||||||
contract.decimals.call((error: Error, decimals: BigNumber) => {
|
contract.decimals.call((error: Error, decimals: BigNumber) => {
|
||||||
if (decimals) {
|
if (decimals) {
|
||||||
@ -429,13 +403,11 @@ export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Prom
|
|||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => {
|
export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => new Promise((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => {
|
web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@ -443,11 +415,9 @@ export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<nu
|
|||||||
resolve(gas);
|
resolve(gas);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export const pushTx = (web3: Web3, tx: any): Promise<string> => {
|
export const pushTx = (web3: Web3, tx: any): Promise<string> => new Promise((resolve, reject) => {
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
web3.eth.sendRawTransaction(tx, (error: Error, result: string) => {
|
web3.eth.sendRawTransaction(tx, (error: Error, result: string) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@ -455,5 +425,4 @@ export const pushTx = (web3: Web3, tx: any): Promise<string> => {
|
|||||||
resolve(result);
|
resolve(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
//regExp1 : string = '(.*)'
|
//regExp1 : string = '(.*)'
|
||||||
//regExp2 : '$1' = '$1'
|
//regExp2 : '$1' = '$1'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const INIT: 'account__init' = 'account__init';
|
export const INIT: 'account__init' = 'account__init';
|
||||||
export const DISPOSE: 'account__dispose' = 'account__dispose';
|
export const DISPOSE: 'account__dispose' = 'account__dispose';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const START: 'discovery__start' = 'discovery__start';
|
export const START: 'discovery__start' = 'discovery__start';
|
||||||
export const STOP: 'discovery__stop' = 'discovery__stop';
|
export const STOP: 'discovery__stop' = 'discovery__stop';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const SAVE: 'storage__save' = 'storage__save';
|
export const SAVE: 'storage__save' = 'storage__save';
|
||||||
export const READY: 'storage__ready' = 'storage__ready';
|
export const READY: 'storage__ready' = 'storage__ready';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const OPEN: 'log__open' = 'log__open';
|
export const OPEN: 'log__open' = 'log__open';
|
||||||
export const CLOSE: 'log__close' = 'log__close';
|
export const CLOSE: 'log__close' = 'log__close';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const ON_PASSPHRASE_CHANGE: 'action__on_passphrase_change' = 'action__on_passphrase_change';
|
export const ON_PASSPHRASE_CHANGE: 'action__on_passphrase_change' = 'action__on_passphrase_change';
|
||||||
export const ON_PASSPHRASE_SHOW: 'action__on_passphrase_show' = 'action__on_passphrase_show';
|
export const ON_PASSPHRASE_SHOW: 'action__on_passphrase_show' = 'action__on_passphrase_show';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const ADD: 'notification__add' = 'notification__add';
|
export const ADD: 'notification__add' = 'notification__add';
|
||||||
export const CLOSE: 'notification__close' = 'notification__close';
|
export const CLOSE: 'notification__close' = 'notification__close';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage';
|
export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage';
|
||||||
export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved';
|
export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const INIT: 'receive__init' = 'receive__init';
|
export const INIT: 'receive__init' = 'receive__init';
|
||||||
export const DISPOSE: 'receive__dispose' = 'receive__dispose';
|
export const DISPOSE: 'receive__dispose' = 'receive__dispose';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const INIT: 'send__init' = 'send__init';
|
export const INIT: 'send__init' = 'send__init';
|
||||||
export const DISPOSE: 'send__dispose' = 'send__dispose';
|
export const DISPOSE: 'send__dispose' = 'send__dispose';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const INIT: 'summary__init' = 'summary__init';
|
export const INIT: 'summary__init' = 'summary__init';
|
||||||
export const DISPOSE: 'summary__dispose' = 'summary__dispose';
|
export const DISPOSE: 'summary__dispose' = 'summary__dispose';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const ADD: 'token__add' = 'token__add';
|
export const ADD: 'token__add' = 'token__add';
|
||||||
export const REMOVE: 'token__remove' = 'token__remove';
|
export const REMOVE: 'token__remove' = 'token__remove';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const ON_BEFORE_UNLOAD: 'wallet__on_before_unload' = 'wallet__on_before_unload';
|
export const ON_BEFORE_UNLOAD: 'wallet__on_before_unload' = 'wallet__on_before_unload';
|
||||||
export const TOGGLE_DEVICE_DROPDOWN: 'wallet__toggle_dropdown' = 'wallet__toggle_dropdown';
|
export const TOGGLE_DEVICE_DROPDOWN: 'wallet__toggle_dropdown' = 'wallet__toggle_dropdown';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
export const START: 'web3__start' = 'web3__start';
|
export const START: 'web3__start' = 'web3__start';
|
||||||
export const STOP: 'web3__stop' = 'web3__stop';
|
export const STOP: 'web3__stop' = 'web3__stop';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -12,8 +12,7 @@ type Props = {
|
|||||||
toggle: typeof LogActions.toggle
|
toggle: typeof LogActions.toggle
|
||||||
}
|
}
|
||||||
|
|
||||||
const Footer = (props: Props): React$Element<string> => {
|
const Footer = (props: Props): React$Element<string> => (
|
||||||
return (
|
|
||||||
<footer>
|
<footer>
|
||||||
<span>© 2018</span>
|
<span>© 2018</span>
|
||||||
<a href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" className="satoshi green">SatoshiLabs</a>
|
<a href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" className="satoshi green">SatoshiLabs</a>
|
||||||
@ -21,17 +20,12 @@ const Footer = (props: Props): React$Element<string> => {
|
|||||||
<a onClick={props.toggle} className="green">Show Log</a>
|
<a onClick={props.toggle} className="green">Show Log</a>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
(state: State) => {
|
(state: State) => ({
|
||||||
return {
|
|
||||||
|
|
||||||
}
|
}),
|
||||||
},
|
(dispatch: Dispatch) => ({
|
||||||
(dispatch: Dispatch) => {
|
|
||||||
return {
|
|
||||||
toggle: bindActionCreators(LogActions.toggle, dispatch),
|
toggle: bindActionCreators(LogActions.toggle, dispatch),
|
||||||
};
|
}),
|
||||||
}
|
|
||||||
)(Footer);
|
)(Footer);
|
||||||
|
@ -1,12 +1,53 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import colors from '~/js/config/colors';
|
||||||
|
|
||||||
const Header = (): React$Element<string> => {
|
const Wrapper = styled.header`
|
||||||
return (
|
width: 100%;
|
||||||
<header>
|
height: 52px;
|
||||||
<div className="layout-wrapper">
|
background: ${colors.color_header};
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: ${colors.color_white};
|
||||||
|
height: 28px;
|
||||||
|
width: 100px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LayoutWrapper = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 1170px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const A = styled.a`
|
||||||
|
color: ${colors.color_white};
|
||||||
|
margin-left: 24px;
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
color: ${colors.color_white};
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
color: ${colors.color_text_secondary};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = (): React$Element<string> => (
|
||||||
|
<Wrapper>
|
||||||
|
<LayoutWrapper>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 163.7 41.9" width="100%" height="100%" preserveAspectRatio="xMinYMin meet">
|
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 163.7 41.9" width="100%" height="100%" preserveAspectRatio="xMinYMin meet">
|
||||||
<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" />
|
<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="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" />
|
||||||
@ -17,14 +58,13 @@ const Header = (): React$Element<string> => {
|
|||||||
<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 " />
|
<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>
|
</svg>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://trezor.io/" target="_blank" rel="noreferrer noopener">TREZOR</a>
|
<A href="https://trezor.io/" target="_blank" rel="noreferrer noopener">TREZOR</A>
|
||||||
<a href="https://doc.satoshilabs.com/trezor-user/" target="_blank" rel="noreferrer noopener">Docs</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://blog.trezor.io/" target="_blank" rel="noreferrer noopener">Blog</A>
|
||||||
<a href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</a>
|
<A href="https://trezor.io/support/" target="_blank" rel="noreferrer noopener">Support</A>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</LayoutWrapper>
|
||||||
</header>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default Header;
|
export default Header;
|
40
src/js/components/common/Heading.js
Normal file
40
src/js/components/common/Heading.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import styled, { css } from 'styled-components';
|
||||||
|
import colors from '~/js/config/colors';
|
||||||
|
|
||||||
|
const baseStyles = css`
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
color: ${colors.black};
|
||||||
|
font-weight: bold;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const H1 = styled.h1`
|
||||||
|
${baseStyles}
|
||||||
|
font-size: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const H2 = styled.h2`
|
||||||
|
${baseStyles};
|
||||||
|
font-size: 14px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const H3 = styled.h3`
|
||||||
|
${baseStyles};
|
||||||
|
font-size: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const H4 = styled.h4`
|
||||||
|
${baseStyles};
|
||||||
|
font-size: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Heading = {
|
||||||
|
H1,
|
||||||
|
H2,
|
||||||
|
H3,
|
||||||
|
H4,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Heading;
|
@ -1,14 +1,13 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default (props: { size: string, label?: string }): React$Element<string> => {
|
export default (props: { size: string, label?: string }): React$Element<string> => {
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
width: `${props.size}px`,
|
width: `${props.size}px`,
|
||||||
height: `${props.size}px`,
|
height: `${props.size}px`,
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="loader-circle" style={style}>
|
<div className="loader-circle" style={style}>
|
||||||
@ -19,4 +18,4 @@ export default (props: { size: string, label?: string }): React$Element<string>
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -14,8 +14,7 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Log = (props: Props): ?React$Element<string> => {
|
const Log = (props: Props): ?React$Element<string> => {
|
||||||
if (!props.log.opened)
|
if (!props.log.opened) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
// const entries = props.log.entries.map(entry => {
|
// const entries = props.log.entries.map(entry => {
|
||||||
// return (
|
// return (
|
||||||
@ -25,23 +24,19 @@ const Log = (props: Props): ?React$Element<string> => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="log">
|
<div className="log">
|
||||||
<button className="log-close transparent" onClick={ props.toggle }></button>
|
<button className="log-close transparent" onClick={props.toggle} />
|
||||||
<h2>Log</h2>
|
<h2>Log</h2>
|
||||||
<p>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</p>
|
<p>Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.</p>
|
||||||
<textarea value={ JSON.stringify(props.log.entries) } readOnly></textarea>
|
<textarea value={JSON.stringify(props.log.entries)} readOnly />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
(state: State) => {
|
(state: State) => ({
|
||||||
return {
|
log: state.log,
|
||||||
log: state.log
|
}),
|
||||||
};
|
(dispatch: Dispatch) => ({
|
||||||
},
|
|
||||||
(dispatch: Dispatch) => {
|
|
||||||
return {
|
|
||||||
toggle: bindActionCreators(LogActions.toggle, dispatch),
|
toggle: bindActionCreators(LogActions.toggle, dispatch),
|
||||||
};
|
}),
|
||||||
}
|
|
||||||
)(Log);
|
)(Log);
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -27,21 +27,21 @@ type NProps = {
|
|||||||
export const Notification = (props: NProps): React$Element<string> => {
|
export const Notification = (props: NProps): React$Element<string> => {
|
||||||
const className = `notification ${props.className}`;
|
const className = `notification ${props.className}`;
|
||||||
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
|
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
|
||||||
const actionButtons = props.actions ? props.actions.map((a, i) => {
|
const actionButtons = props.actions ? props.actions.map((a, i) => (
|
||||||
return (
|
<button key={i} onClick={(event) => { close(); a.callback(); }} className="transparent">{ a.label }</button>
|
||||||
<button key={ i } onClick={ event => { close(); a.callback(); } } className="transparent">{ a.label }</button>
|
)) : null;
|
||||||
)
|
|
||||||
}) : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
{ props.cancelable ? (
|
{ props.cancelable ? (
|
||||||
<button className="notification-close transparent"
|
<button
|
||||||
onClick={ event => close() }></button>
|
className="notification-close transparent"
|
||||||
|
onClick={event => close()}
|
||||||
|
/>
|
||||||
) : null }
|
) : null }
|
||||||
<div className="notification-body">
|
<div className="notification-body">
|
||||||
<h2>{ props.title }</h2>
|
<h2>{ props.title }</h2>
|
||||||
{ props.message ? (<p dangerouslySetInnerHTML={{__html: props.message }}></p>) : null }
|
{ props.message ? (<p dangerouslySetInnerHTML={{ __html: props.message }} />) : null }
|
||||||
</div>
|
</div>
|
||||||
{ props.actions && props.actions.length > 0 ? (
|
{ props.actions && props.actions.length > 0 ? (
|
||||||
<div className="notification-action">
|
<div className="notification-action">
|
||||||
@ -50,13 +50,12 @@ export const Notification = (props: NProps): React$Element<string> => {
|
|||||||
) : null }
|
) : null }
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const NotificationGroup = (props: Props) => {
|
export const NotificationGroup = (props: Props) => {
|
||||||
const { notifications, close } = props;
|
const { notifications, close } = props;
|
||||||
return notifications.map((n, i) => {
|
return notifications.map((n, i) => (
|
||||||
return (
|
|
||||||
<Notification
|
<Notification
|
||||||
key={i}
|
key={i}
|
||||||
className={n.type}
|
className={n.type}
|
||||||
@ -66,19 +65,14 @@ export const NotificationGroup = (props: Props) => {
|
|||||||
actions={n.actions}
|
actions={n.actions}
|
||||||
close={close}
|
close={close}
|
||||||
/>
|
/>
|
||||||
)
|
));
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
(state: State) => {
|
(state: State) => ({
|
||||||
return {
|
notifications: state.notifications,
|
||||||
notifications: state.notifications
|
}),
|
||||||
};
|
(dispatch: Dispatch) => ({
|
||||||
},
|
|
||||||
(dispatch: Dispatch) => {
|
|
||||||
return {
|
|
||||||
close: bindActionCreators(NotificationActions.close, dispatch),
|
close: bindActionCreators(NotificationActions.close, dispatch),
|
||||||
};
|
}),
|
||||||
}
|
|
||||||
)(NotificationGroup);
|
)(NotificationGroup);
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import TrezorConnect from 'trezor-connect';
|
import TrezorConnect from 'trezor-connect';
|
||||||
@ -25,14 +25,12 @@ const DisconnectDevice = (props: Props) => {
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="image">
|
<div className="image" />
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const ConnectHIDDevice = (props: Props) => {
|
const ConnectHIDDevice = (props: Props) => (
|
||||||
return (
|
|
||||||
<main>
|
<main>
|
||||||
<h2 className="claim">The private bank in your hands.</h2>
|
<h2 className="claim">The private bank in your hands.</h2>
|
||||||
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
|
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
|
||||||
@ -42,10 +40,10 @@ const ConnectHIDDevice = (props: Props) => {
|
|||||||
<span>
|
<span>
|
||||||
<svg width="12px" height="35px" viewBox="0 0 20 57">
|
<svg width="12px" height="35px" viewBox="0 0 20 57">
|
||||||
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
|
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
|
||||||
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5"></rect>
|
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
|
||||||
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect>
|
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
|
||||||
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z"></path>
|
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
|
||||||
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></rect>
|
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
Connect TREZOR to continue
|
Connect TREZOR to continue
|
||||||
@ -59,7 +57,6 @@ const ConnectHIDDevice = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectWebUsbDevice extends Component<Props> {
|
class ConnectWebUsbDevice extends Component<Props> {
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@ -81,10 +78,10 @@ class ConnectWebUsbDevice extends Component<Props> {
|
|||||||
<span>
|
<span>
|
||||||
<svg width="12px" height="35px" viewBox="0 0 20 57">
|
<svg width="12px" height="35px" viewBox="0 0 20 57">
|
||||||
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
|
<g stroke="none" strokeWidth="1" fill="none" transform="translate(1, 1)">
|
||||||
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5"></rect>
|
<rect className="connect-usb-pin" fill="#01B757" x="6" y="39" width="6" height="5" />
|
||||||
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11"></rect>
|
<rect className="connect-usb-cable" stroke="#01B757" strokeWidth="1" x="8.5" y="44.5" width="1" height="11" />
|
||||||
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z"></path>
|
<path stroke="#01B757" d="M8.90856859,33.9811778 L6.43814432,33.9811778 C5.45301486,34.0503113 4.69477081,33.6889084 4.1634122,32.8969691 C3.36637428,31.7090602 -0.000402169348,26.3761977 0.0748097911,23.2982514 C0.124878873,21.2492429 0.0999525141,14.5598149 3.07156595e-05,3.22996744 C-0.000274213164,3.1963928 0.00243636275,3.162859 0.00812115776,3.12976773 C0.28477346,1.51937083 1.22672004,0.617538852 2.8339609,0.424271782 C4.45813658,0.228968338 6.54411954,0.0875444105 9.09190977,0 L9.09190977,0.0169167084 C11.5566027,0.104886477 13.5814718,0.244169993 15.1665175,0.434768145 C16.7530267,0.625542287 17.6912941,1.50671985 17.9813196,3.07830083 C17.9943481,3.14889902 18.0005888,3.22058224 17.9999563,3.29236974 L17.9999901,3.29237004 C17.9004498,14.5907444 17.875676,21.2628703 17.9256686,23.3087478 C18.0008805,26.3866941 14.6341041,31.7195566 13.8370662,32.9074655 C13.3057075,33.6994047 12.5474635,34.0608076 11.562334,33.9916742 L8.90856859,33.9916742 L8.90856859,33.9811778 Z" />
|
||||||
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625"></rect>
|
<rect fill="#01B757" x="2" y="7" width="14" height="7" rx="0.5625" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
Connect TREZOR
|
Connect TREZOR
|
||||||
@ -107,12 +104,11 @@ class ConnectWebUsbDevice extends Component<Props> {
|
|||||||
const ConnectDevice = (props: Props) => {
|
const ConnectDevice = (props: Props) => {
|
||||||
const { transport, disconnectRequest } = props;
|
const { transport, disconnectRequest } = props;
|
||||||
if (disconnectRequest) {
|
if (disconnectRequest) {
|
||||||
return <DisconnectDevice {...props} />
|
return <DisconnectDevice {...props} />;
|
||||||
} else if (transport && transport.version.indexOf('webusb') >= 0) {
|
} if (transport && transport.version.indexOf('webusb') >= 0) {
|
||||||
return <ConnectWebUsbDevice {...props} />
|
return <ConnectWebUsbDevice {...props} />;
|
||||||
} else {
|
|
||||||
return <ConnectHIDDevice {...props} />
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return <ConnectHIDDevice {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
export default ConnectDevice;
|
export default ConnectDevice;
|
@ -1,9 +1,9 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Preloader from './Preloader';
|
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
|
import Preloader from './Preloader';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
version: string;
|
version: string;
|
||||||
@ -35,7 +35,6 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class InstallBridge extends Component<Props, State> {
|
export default class InstallBridge extends Component<Props, State> {
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -49,7 +48,7 @@ export default class InstallBridge extends Component<Props, State> {
|
|||||||
|
|
||||||
onChange(value: InstallTarget) {
|
onChange(value: InstallTarget) {
|
||||||
this.setState({
|
this.setState({
|
||||||
target: value
|
target: value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +56,8 @@ export default class InstallBridge extends Component<Props, State> {
|
|||||||
if (this.props.browserState.osname && !this.state.target) {
|
if (this.props.browserState.osname && !this.state.target) {
|
||||||
const currentTarget: ?InstallTarget = installers.find(i => i.id === this.props.browserState.osname);
|
const currentTarget: ?InstallTarget = installers.find(i => i.id === this.props.browserState.osname);
|
||||||
this.setState({
|
this.setState({
|
||||||
target: currentTarget
|
target: currentTarget,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +80,8 @@ export default class InstallBridge extends Component<Props, State> {
|
|||||||
clearable={false}
|
clearable={false}
|
||||||
value={this.state.target}
|
value={this.state.target}
|
||||||
onChange={this.onChange.bind(this)}
|
onChange={this.onChange.bind(this)}
|
||||||
options={ installers } />
|
options={installers}
|
||||||
|
/>
|
||||||
<a href={url} className="button">Download for { label }</a>
|
<a href={url} className="button">Download for { label }</a>
|
||||||
</div>
|
</div>
|
||||||
<p>Learn more about latest version in <a href="https://github.com/trezor/trezord-go/blob/master/CHANGELOG.md" className="green" target="_blank" rel="noreferrer noopener">Changelog</a></p>
|
<p>Learn more about latest version in <a href="https://github.com/trezor/trezord-go/blob/master/CHANGELOG.md" className="green" target="_blank" rel="noreferrer noopener">Changelog</a></p>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Preloader from './Preloader';
|
import Preloader from './Preloader';
|
||||||
@ -14,8 +14,7 @@ import Notifications, { Notification } from '../common/Notification';
|
|||||||
|
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
const BrowserNotSupported = (props: {}): React$Element<string> => {
|
const BrowserNotSupported = (props: {}): React$Element<string> => (
|
||||||
return (
|
|
||||||
<main>
|
<main>
|
||||||
<h2>Your browser is not supported</h2>
|
<h2>Your browser is not supported</h2>
|
||||||
<p>Please choose one of the supported browsers</p>
|
<p>Please choose one of the supported browsers</p>
|
||||||
@ -30,13 +29,10 @@ const BrowserNotSupported = (props: {}): React$Element<string> => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
)
|
);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default (props: Props) => {
|
export default (props: Props) => {
|
||||||
|
|
||||||
const web3 = props.web3;
|
const web3 = props.web3;
|
||||||
const { devices } = props;
|
const { devices } = props;
|
||||||
const { browserState, transport } = props.connect;
|
const { browserState, transport } = props.connect;
|
||||||
@ -49,14 +45,16 @@ export default (props: Props) => {
|
|||||||
const bridgeRoute: boolean = props.router.location.state.hasOwnProperty('bridge');
|
const bridgeRoute: boolean = props.router.location.state.hasOwnProperty('bridge');
|
||||||
|
|
||||||
if (localStorageError) {
|
if (localStorageError) {
|
||||||
notification = (<Notification
|
notification = (
|
||||||
|
<Notification
|
||||||
title="Initialization error"
|
title="Initialization error"
|
||||||
message="Config files are missing"
|
message="Config files are missing"
|
||||||
className="error"
|
className="error"
|
||||||
/>);
|
/>
|
||||||
|
);
|
||||||
css += ' config-error';
|
css += ' config-error';
|
||||||
} else if (browserState.supported === false) {
|
} else if (browserState.supported === false) {
|
||||||
css += ' browser-not-supported'
|
css += ' browser-not-supported';
|
||||||
body = <BrowserNotSupported />;
|
body = <BrowserNotSupported />;
|
||||||
} else if (connectError || bridgeRoute) {
|
} else if (connectError || bridgeRoute) {
|
||||||
css += ' install-bridge';
|
css += ' install-bridge';
|
||||||
@ -77,7 +75,6 @@ export default (props: Props) => {
|
|||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
return (<Preloader />);
|
return (<Preloader />);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default (props: {}): React$Element<string> => {
|
export default (props: {}): React$Element<string> => (
|
||||||
return (
|
|
||||||
<section className="landing">
|
<section className="landing">
|
||||||
localstorage ERROR
|
localstorage ERROR
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
@ -1,13 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Loader from '../common/LoaderCircle';
|
import Loader from '../common/LoaderCircle';
|
||||||
|
|
||||||
export default (props: {}): React$Element<string> => {
|
export default (props: {}): React$Element<string> => (
|
||||||
return (
|
|
||||||
<section className="landing">
|
<section className="landing">
|
||||||
<Loader label="Loading" size="100" />
|
<Loader label="Loading" size="100" />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
@ -1,12 +1,10 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default (props: {}): React$Element<string> => {
|
export default (props: {}): React$Element<string> => (
|
||||||
return (
|
|
||||||
<section className="landing">
|
<section className="landing">
|
||||||
connect ERROR
|
connect ERROR
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -31,8 +31,7 @@ type OwnProps = {
|
|||||||
|
|
||||||
export type Props = StateProps & DispatchProps;
|
export type Props = StateProps & DispatchProps;
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => {
|
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => ({
|
||||||
return {
|
|
||||||
localStorage: state.localStorage,
|
localStorage: state.localStorage,
|
||||||
modal: state.modal,
|
modal: state.modal,
|
||||||
web3: state.web3,
|
web3: state.web3,
|
||||||
@ -41,13 +40,10 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
|
|||||||
router: state.router,
|
router: state.router,
|
||||||
wallet: state.wallet,
|
wallet: state.wallet,
|
||||||
devices: state.devices,
|
devices: state.devices,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => {
|
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||||
return {
|
|
||||||
|
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(LandingPage);
|
export default connect(mapStateToProps, mapDispatchToProps)(LandingPage);
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { findAccount } from '~/js/reducers/AccountsReducer';
|
import { findAccount } from '~/js/reducers/AccountsReducer';
|
||||||
@ -7,10 +7,9 @@ import { findAccount } from '~/js/reducers/AccountsReducer';
|
|||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
const ConfirmAddress = (props: Props) => {
|
const ConfirmAddress = (props: Props) => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network
|
network,
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
if (!account || !network) return null;
|
if (!account || !network) return null;
|
||||||
|
|
||||||
@ -26,11 +25,10 @@ const ConfirmAddress = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
export default ConfirmAddress;
|
export default ConfirmAddress;
|
||||||
|
|
||||||
export class ConfirmUnverifiedAddress extends Component<Props> {
|
export class ConfirmUnverifiedAddress extends Component<Props> {
|
||||||
|
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
keyboardHandler(event: KeyboardEvent): void {
|
keyboardHandler(event: KeyboardEvent): void {
|
||||||
@ -52,13 +50,12 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
|
|||||||
verifyAddress() {
|
verifyAddress() {
|
||||||
if (!this.props.modal.opened) return;
|
if (!this.props.modal.opened) return;
|
||||||
const {
|
const {
|
||||||
account
|
account,
|
||||||
} = this.props.selectedAccount;
|
} = this.props.selectedAccount;
|
||||||
if (!account) return null;
|
if (!account) return null;
|
||||||
|
|
||||||
this.props.modalActions.onCancel();
|
this.props.modalActions.onCancel();
|
||||||
this.props.receiveActions.showAddress(account.addressPath);
|
this.props.receiveActions.showAddress(account.addressPath);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showUnverifiedAddress() {
|
showUnverifiedAddress() {
|
||||||
@ -71,11 +68,11 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
|
|||||||
render() {
|
render() {
|
||||||
if (!this.props.modal.opened) return null;
|
if (!this.props.modal.opened) return null;
|
||||||
const {
|
const {
|
||||||
device
|
device,
|
||||||
} = this.props.modal;
|
} = this.props.modal;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
onCancel
|
onCancel,
|
||||||
} = this.props.modalActions;
|
} = this.props.modalActions;
|
||||||
|
|
||||||
let deviceStatus: string;
|
let deviceStatus: string;
|
||||||
@ -83,7 +80,7 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
|
|||||||
|
|
||||||
if (!device.connected) {
|
if (!device.connected) {
|
||||||
deviceStatus = `${device.label} is not connected`;
|
deviceStatus = `${device.label} is not connected`;
|
||||||
claim = 'Please connect your device'
|
claim = 'Please connect your device';
|
||||||
} else {
|
} else {
|
||||||
// corner-case where device is connected but it is unavailable because it was created with different "passphrase_protection" settings
|
// corner-case where device is connected but it is unavailable because it was created with different "passphrase_protection" settings
|
||||||
const enable: string = device.features && device.features.passphrase_protection ? 'enable' : 'disable';
|
const enable: string = device.features && device.features.passphrase_protection ? 'enable' : 'disable';
|
||||||
@ -93,7 +90,7 @@ export class ConfirmUnverifiedAddress extends Component<Props> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="confirm-address-unverified">
|
<div className="confirm-address-unverified">
|
||||||
<button className="close-modal transparent" onClick={ onCancel }></button>
|
<button className="close-modal transparent" onClick={onCancel} />
|
||||||
<h3>{ deviceStatus }</h3>
|
<h3>{ deviceStatus }</h3>
|
||||||
<p>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</p>
|
<p>To prevent phishing attacks, you should verify the address on your TREZOR first. { claim } to continue with the verification process.</p>
|
||||||
<button onClick={event => this.verifyAddress()}>Try again</button>
|
<button onClick={event => this.verifyAddress()}>Try again</button>
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
const Confirmation = (props: Props) => {
|
const Confirmation = (props: Props) => {
|
||||||
|
|
||||||
if (!props.modal.opened) return null;
|
if (!props.modal.opened) return null;
|
||||||
const { device } = props.modal;
|
const { device } = props.modal;
|
||||||
|
|
||||||
@ -14,7 +13,7 @@ const Confirmation = (props: Props) => {
|
|||||||
address,
|
address,
|
||||||
currency,
|
currency,
|
||||||
total,
|
total,
|
||||||
selectedFeeLevel
|
selectedFeeLevel,
|
||||||
} = props.sendForm;
|
} = props.sendForm;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -33,6 +32,6 @@ const Confirmation = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Confirmation;
|
export default Confirmation;
|
@ -1,8 +1,8 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { getNewInstance } from '~/js/reducers/DevicesReducer'
|
import { getNewInstance } from '~/js/reducers/DevicesReducer';
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@ -13,9 +13,10 @@ type State = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class DuplicateDevice extends Component<Props, State> {
|
export default class DuplicateDevice extends Component<Props, State> {
|
||||||
|
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
state: State;
|
state: State;
|
||||||
|
|
||||||
input: ?HTMLInputElement;
|
input: ?HTMLInputElement;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
@ -31,7 +32,7 @@ export default class DuplicateDevice extends Component<Props, State> {
|
|||||||
instance,
|
instance,
|
||||||
instanceName: null,
|
instanceName: null,
|
||||||
isUsed: false,
|
isUsed: false,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboardHandler(event: KeyboardEvent): void {
|
keyboardHandler(event: KeyboardEvent): void {
|
||||||
@ -43,8 +44,7 @@ export default class DuplicateDevice extends Component<Props, State> {
|
|||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
// one time autofocus
|
// one time autofocus
|
||||||
if (this.input)
|
if (this.input) this.input.focus();
|
||||||
this.input.focus();
|
|
||||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||||
window.addEventListener('keydown', this.keyboardHandler, false);
|
window.addEventListener('keydown', this.keyboardHandler, false);
|
||||||
}
|
}
|
||||||
@ -54,7 +54,6 @@ export default class DuplicateDevice extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onNameChange = (value: string): void => {
|
onNameChange = (value: string): void => {
|
||||||
|
|
||||||
let isUsed: boolean = false;
|
let isUsed: boolean = false;
|
||||||
if (value.length > 0) {
|
if (value.length > 0) {
|
||||||
isUsed = (this.props.devices.find(d => d.instanceName === value) !== undefined);
|
isUsed = (this.props.devices.find(d => d.instanceName === value) !== undefined);
|
||||||
@ -62,7 +61,7 @@ export default class DuplicateDevice extends Component<Props, State> {
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
instanceName: value.length > 0 ? value : null,
|
instanceName: value.length > 0 ? value : null,
|
||||||
isUsed
|
isUsed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +71,6 @@ export default class DuplicateDevice extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
if (!this.props.modal.opened) return null;
|
if (!this.props.modal.opened) return null;
|
||||||
|
|
||||||
const { device } = this.props.modal;
|
const { device } = this.props.modal;
|
||||||
@ -80,12 +78,12 @@ export default class DuplicateDevice extends Component<Props, State> {
|
|||||||
const {
|
const {
|
||||||
defaultName,
|
defaultName,
|
||||||
instanceName,
|
instanceName,
|
||||||
isUsed
|
isUsed,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="duplicate">
|
<div className="duplicate">
|
||||||
<button className="close-modal transparent" onClick={ onCancel }></button>
|
<button className="close-modal transparent" onClick={onCancel} />
|
||||||
<h3>Clone { device.label }?</h3>
|
<h3>Clone { device.label }?</h3>
|
||||||
<p>This will create new instance of device which can be used with different passphrase</p>
|
<p>This will create new instance of device which can be used with different passphrase</p>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@ -100,7 +98,8 @@ export default class DuplicateDevice extends Component<Props, State> {
|
|||||||
placeholder={defaultName}
|
placeholder={defaultName}
|
||||||
ref={(element) => { this.input = element; }}
|
ref={(element) => { this.input = element; }}
|
||||||
onChange={event => this.onNameChange(event.currentTarget.value)}
|
onChange={event => this.onNameChange(event.currentTarget.value)}
|
||||||
defaultValue={ instanceName } />
|
defaultValue={instanceName}
|
||||||
|
/>
|
||||||
{ isUsed ? <span className="error">Instance name is already in use</span> : null }
|
{ isUsed ? <span className="error">Instance name is already in use</span> : null }
|
||||||
</div>
|
</div>
|
||||||
<button disabled={isUsed} onClick={event => this.submit()}>Create new instance</button>
|
<button disabled={isUsed} onClick={event => this.submit()}>Create new instance</button>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
@ -14,6 +14,6 @@ const InvalidPin = (props: Props) => {
|
|||||||
<p>Retrying...</p>
|
<p>Retrying...</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default InvalidPin;
|
export default InvalidPin;
|
@ -1,10 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import raf from 'raf';
|
import raf from 'raf';
|
||||||
|
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
deviceLabel: string;
|
deviceLabel: string;
|
||||||
singleInput: boolean;
|
singleInput: boolean;
|
||||||
@ -18,10 +19,12 @@ type State = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class PinModal extends Component<Props, State> {
|
export default class PinModal extends Component<Props, State> {
|
||||||
|
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
state: State;
|
state: State;
|
||||||
|
|
||||||
passphraseInput: ?HTMLInputElement;
|
passphraseInput: ?HTMLInputElement;
|
||||||
|
|
||||||
passphraseRevisionInput: ?HTMLInputElement;
|
passphraseRevisionInput: ?HTMLInputElement;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
@ -48,13 +51,11 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
passphraseRevisionFocused: false,
|
passphraseRevisionFocused: false,
|
||||||
passphraseRevisionTouched: false,
|
passphraseRevisionTouched: false,
|
||||||
match: true,
|
match: true,
|
||||||
visible: false
|
visible: false,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboardHandler(event: KeyboardEvent): void {
|
keyboardHandler(event: KeyboardEvent): void {
|
||||||
|
|
||||||
|
|
||||||
if (event.keyCode === 13) {
|
if (event.keyCode === 13) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
//this.passphraseInput.blur();
|
//this.passphraseInput.blur();
|
||||||
@ -73,13 +74,11 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
// one time autofocus
|
// one time autofocus
|
||||||
if (this.passphraseInput)
|
if (this.passphraseInput) this.passphraseInput.focus();
|
||||||
this.passphraseInput.focus();
|
|
||||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||||
window.addEventListener('keydown', this.keyboardHandler, false);
|
window.addEventListener('keydown', this.keyboardHandler, false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// document.oncontextmenu = (event) => {
|
// document.oncontextmenu = (event) => {
|
||||||
// const el = window.event.srcElement || event.target;
|
// const el = window.event.srcElement || event.target;
|
||||||
// const type = el.tagName.toLowerCase() || '';
|
// const type = el.tagName.toLowerCase() || '';
|
||||||
@ -106,7 +105,7 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
passphraseRevision,
|
passphraseRevision,
|
||||||
passphraseFocused,
|
passphraseFocused,
|
||||||
passphraseRevisionFocused,
|
passphraseRevisionFocused,
|
||||||
visible
|
visible,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
// } = this.props.modal;
|
// } = this.props.modal;
|
||||||
|
|
||||||
@ -121,11 +120,11 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
|
|
||||||
if (this.passphraseInput) {
|
if (this.passphraseInput) {
|
||||||
this.passphraseInput.value = passphraseInputValue;
|
this.passphraseInput.value = passphraseInputValue;
|
||||||
this.passphraseInput.setAttribute("type", visible || (!visible && !passphraseFocused) ? "text" : "password");
|
this.passphraseInput.setAttribute('type', visible || (!visible && !passphraseFocused) ? 'text' : 'password');
|
||||||
}
|
}
|
||||||
if (this.passphraseRevisionInput) {
|
if (this.passphraseRevisionInput) {
|
||||||
this.passphraseRevisionInput.value = passphraseRevisionInputValue;
|
this.passphraseRevisionInput.value = passphraseRevisionInputValue;
|
||||||
this.passphraseRevisionInput.setAttribute("type", visible || (!visible && !passphraseRevisionFocused) ? "text" : "password");
|
this.passphraseRevisionInput.setAttribute('type', visible || (!visible && !passphraseRevisionFocused) ? 'text' : 'password');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,13 +135,13 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
if (input === 'passphrase') {
|
if (input === 'passphrase') {
|
||||||
this.setState({
|
this.setState({
|
||||||
match: this.state.singleInput || this.state.passphraseRevision === value,
|
match: this.state.singleInput || this.state.passphraseRevision === value,
|
||||||
passphrase: value
|
passphrase: value,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
match: this.state.passphrase === value,
|
match: this.state.passphrase === value,
|
||||||
passphraseRevision: value,
|
passphraseRevision: value,
|
||||||
passphraseRevisionTouched: true
|
passphraseRevisionTouched: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,11 +149,11 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
onPassphraseFocus = (input: string): void => {
|
onPassphraseFocus = (input: string): void => {
|
||||||
if (input === 'passphrase') {
|
if (input === 'passphrase') {
|
||||||
this.setState({
|
this.setState({
|
||||||
passphraseFocused: true
|
passphraseFocused: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
passphraseRevisionFocused: true
|
passphraseRevisionFocused: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,24 +161,24 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
onPassphraseBlur = (input: string): void => {
|
onPassphraseBlur = (input: string): void => {
|
||||||
if (input === 'passphrase') {
|
if (input === 'passphrase') {
|
||||||
this.setState({
|
this.setState({
|
||||||
passphraseFocused: false
|
passphraseFocused: false,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
passphraseRevisionFocused: false
|
passphraseRevisionFocused: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPassphraseShow = (): void => {
|
onPassphraseShow = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
visible: true
|
visible: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onPassphraseHide = (): void => {
|
onPassphraseHide = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
visible: false
|
visible: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,14 +202,13 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
passphraseRevision: '',
|
passphraseRevision: '',
|
||||||
passphraseFocused: false,
|
passphraseFocused: false,
|
||||||
passphraseRevisionFocused: false,
|
passphraseRevisionFocused: false,
|
||||||
visible: false
|
visible: false,
|
||||||
})
|
});
|
||||||
|
|
||||||
raf(() => onPassphraseSubmit(empty ? '' : passphrase));
|
raf(() => onPassphraseSubmit(empty ? '' : passphrase));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
if (!this.props.modal.opened) return null;
|
if (!this.props.modal.opened) return null;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -229,9 +227,9 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
passphraseRevisionTouched,
|
passphraseRevisionTouched,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
let passphraseInputType: string = visible || (!visible && !passphraseFocused) ? "text" : "password";
|
let passphraseInputType: string = visible || (!visible && !passphraseFocused) ? 'text' : 'password';
|
||||||
let passphraseRevisionInputType: string = visible || (!visible && !passphraseRevisionFocused) ? "text" : "password";
|
let passphraseRevisionInputType: string = visible || (!visible && !passphraseRevisionFocused) ? 'text' : 'password';
|
||||||
passphraseInputType = passphraseRevisionInputType = "text";
|
passphraseInputType = passphraseRevisionInputType = 'text';
|
||||||
//let passphraseInputType: string = visible || passphraseFocused ? "text" : "password";
|
//let passphraseInputType: string = visible || passphraseFocused ? "text" : "password";
|
||||||
//let passphraseRevisionInputType: string = visible || passphraseRevisionFocused ? "text" : "password";
|
//let passphraseRevisionInputType: string = visible || passphraseRevisionFocused ? "text" : "password";
|
||||||
|
|
||||||
@ -257,7 +255,8 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
onFocus={event => this.onPassphraseFocus('passphrase')}
|
onFocus={event => this.onPassphraseFocus('passphrase')}
|
||||||
onBlur={event => this.onPassphraseBlur('passphrase')}
|
onBlur={event => this.onPassphraseBlur('passphrase')}
|
||||||
|
|
||||||
tabIndex="1" />
|
tabIndex="1"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{ singleInput ? null : (
|
{ singleInput ? null : (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@ -274,7 +273,8 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
onFocus={event => this.onPassphraseFocus('revision')}
|
onFocus={event => this.onPassphraseFocus('revision')}
|
||||||
onBlur={event => this.onPassphraseBlur('revision')}
|
onBlur={event => this.onPassphraseBlur('revision')}
|
||||||
|
|
||||||
tabIndex="2" />
|
tabIndex="2"
|
||||||
|
/>
|
||||||
{ !match && passphraseRevisionTouched ? <span className="error">Passphrases do not match</span> : null }
|
{ !match && passphraseRevisionTouched ? <span className="error">Passphrases do not match</span> : null }
|
||||||
</div>
|
</div>
|
||||||
) }
|
) }
|
||||||
@ -283,7 +283,7 @@ export default class PinModal extends Component<Props, State> {
|
|||||||
<div className="row">
|
<div className="row">
|
||||||
<label className="custom-checkbox">
|
<label className="custom-checkbox">
|
||||||
<input type="checkbox" tabIndex="3" onChange={showPassphraseCheckboxFn} checked={visible} />
|
<input type="checkbox" tabIndex="3" onChange={showPassphraseCheckboxFn} checked={visible} />
|
||||||
<span className="indicator"></span>
|
<span className="indicator" />
|
||||||
Show passphrase
|
Show passphrase
|
||||||
</label>
|
</label>
|
||||||
{/* <label className="custom-checkbox">
|
{/* <label className="custom-checkbox">
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
const Confirmation = (props: Props) => {
|
const Confirmation = (props: Props) => {
|
||||||
|
|
||||||
if (!props.modal.opened) return null;
|
if (!props.modal.opened) return null;
|
||||||
const { device } = props.modal;
|
const { device } = props.modal;
|
||||||
|
|
||||||
@ -16,6 +15,6 @@ const Confirmation = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Confirmation;
|
export default Confirmation;
|
@ -1,16 +1,17 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
pin: string;
|
pin: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Pin extends Component<Props, State> {
|
export default class Pin extends Component<Props, State> {
|
||||||
|
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
state: State;
|
state: State;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
@ -18,7 +19,7 @@ export default class Pin extends Component<Props, State> {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
pin: '',
|
pin: '',
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onPinAdd = (input: number): void => {
|
onPinAdd = (input: number): void => {
|
||||||
@ -26,7 +27,7 @@ export default class Pin extends Component<Props, State> {
|
|||||||
if (pin.length < 9) {
|
if (pin.length < 9) {
|
||||||
pin += input;
|
pin += input;
|
||||||
this.setState({
|
this.setState({
|
||||||
pin: pin
|
pin,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,7 +94,6 @@ export default class Pin extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
componentWillMount(): void {
|
componentWillMount(): void {
|
||||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||||
window.addEventListener('keydown', this.keyboardHandler, false);
|
window.addEventListener('keydown', this.keyboardHandler, false);
|
||||||
@ -104,7 +104,6 @@ export default class Pin extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
if (!this.props.modal.opened) return null;
|
if (!this.props.modal.opened) return null;
|
||||||
|
|
||||||
const { onPinSubmit } = this.props.modalActions;
|
const { onPinSubmit } = this.props.modalActions;
|
||||||
@ -119,7 +118,7 @@ export default class Pin extends Component<Props, State> {
|
|||||||
|
|
||||||
<div className="pin-input-row">
|
<div className="pin-input-row">
|
||||||
<input type="password" autoComplete="off" maxLength="9" disabled value={pin} />
|
<input type="password" autoComplete="off" maxLength="9" disabled value={pin} />
|
||||||
<button type="button" className="pin-backspace transparent" onClick={ event => this.onPinBackspace() }></button>
|
<button type="button" className="pin-backspace transparent" onClick={event => this.onPinBackspace()} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pin-row">
|
<div className="pin-row">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Loader from '../common/LoaderCircle';
|
import Loader from '../common/LoaderCircle';
|
||||||
@ -12,8 +12,8 @@ type State = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class RememberDevice extends Component<Props, State> {
|
export default class RememberDevice extends Component<Props, State> {
|
||||||
|
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
state: State;
|
state: State;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
@ -21,7 +21,7 @@ export default class RememberDevice extends Component<Props, State> {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
countdown: 10,
|
countdown: 10,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboardHandler(event: KeyboardEvent): void {
|
keyboardHandler(event: KeyboardEvent): void {
|
||||||
@ -32,7 +32,6 @@ export default class RememberDevice extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
|
|
||||||
const ticker = () => {
|
const ticker = () => {
|
||||||
if (this.state.countdown - 1 <= 0) {
|
if (this.state.countdown - 1 <= 0) {
|
||||||
// TODO: possible race condition,
|
// TODO: possible race condition,
|
||||||
@ -43,14 +42,14 @@ export default class RememberDevice extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
countdown: this.state.countdown - 1
|
countdown: this.state.countdown - 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
countdown: 10,
|
countdown: 10,
|
||||||
ticker: window.setInterval(ticker, 1000)
|
ticker: window.setInterval(ticker, 1000),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.keyboardHandler = this.keyboardHandler.bind(this);
|
this.keyboardHandler = this.keyboardHandler.bind(this);
|
||||||
@ -76,7 +75,7 @@ export default class RememberDevice extends Component<Props, State> {
|
|||||||
const { onForgetDevice, onRememberDevice } = this.props.modalActions;
|
const { onForgetDevice, onRememberDevice } = this.props.modalActions;
|
||||||
|
|
||||||
let label = device.label;
|
let label = device.label;
|
||||||
const devicePlural: string = instances && instances.length > 1 ? "devices or to remember them" : "device or to remember it";
|
const devicePlural: string = instances && instances.length > 1 ? 'devices or to remember them' : 'device or to remember it';
|
||||||
if (instances && instances.length > 0) {
|
if (instances && instances.length > 0) {
|
||||||
label = instances.map((instance, index) => {
|
label = instances.map((instance, index) => {
|
||||||
let comma: string = '';
|
let comma: string = '';
|
||||||
@ -98,7 +97,6 @@ export default class RememberDevice extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ForgetDevice extends Component<Props> {
|
export class ForgetDevice extends Component<Props> {
|
||||||
|
|
||||||
keyboardHandler: (event: KeyboardEvent) => void;
|
keyboardHandler: (event: KeyboardEvent) => void;
|
||||||
|
|
||||||
keyboardHandler(event: KeyboardEvent): void {
|
keyboardHandler(event: KeyboardEvent): void {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -57,14 +57,14 @@ const Fade = ({ children, ...props }) => (
|
|||||||
<CSSTransition
|
<CSSTransition
|
||||||
{...props}
|
{...props}
|
||||||
timeout={1000}
|
timeout={1000}
|
||||||
classNames="fade">
|
classNames="fade"
|
||||||
|
>
|
||||||
{ children }
|
{ children }
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
);
|
);
|
||||||
|
|
||||||
class Modal extends Component<Props> {
|
class Modal extends Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
if (!this.props.modal.opened) return null;
|
if (!this.props.modal.opened) return null;
|
||||||
|
|
||||||
const { opened, windowType } = this.props.modal;
|
const { opened, windowType } = this.props.modal;
|
||||||
@ -80,29 +80,29 @@ class Modal extends Component<Props> {
|
|||||||
case UI.REQUEST_PASSPHRASE:
|
case UI.REQUEST_PASSPHRASE:
|
||||||
component = (<Passphrase {...this.props} />);
|
component = (<Passphrase {...this.props} />);
|
||||||
break;
|
break;
|
||||||
case "ButtonRequest_SignTx" :
|
case 'ButtonRequest_SignTx':
|
||||||
component = (<ConfirmSignTx { ...this.props } />)
|
component = (<ConfirmSignTx {...this.props} />);
|
||||||
break;
|
break;
|
||||||
// case "ButtonRequest_Address" :
|
// case "ButtonRequest_Address" :
|
||||||
// component = (<ConfirmAddress { ...this.props } />)
|
// component = (<ConfirmAddress { ...this.props } />)
|
||||||
// break;
|
// break;
|
||||||
case "ButtonRequest_PassphraseType" :
|
case 'ButtonRequest_PassphraseType':
|
||||||
component = (<PassphraseType { ...this.props } />)
|
component = (<PassphraseType {...this.props} />);
|
||||||
break;
|
break;
|
||||||
case RECEIVE.REQUEST_UNVERIFIED:
|
case RECEIVE.REQUEST_UNVERIFIED:
|
||||||
component = (<ConfirmUnverifiedAddress { ...this.props } />)
|
component = (<ConfirmUnverifiedAddress {...this.props} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONNECT.REMEMBER_REQUEST:
|
case CONNECT.REMEMBER_REQUEST:
|
||||||
component = (<RememberDevice { ...this.props } />)
|
component = (<RememberDevice {...this.props} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONNECT.FORGET_REQUEST:
|
case CONNECT.FORGET_REQUEST:
|
||||||
component = (<ForgetDevice { ...this.props } />)
|
component = (<ForgetDevice {...this.props} />);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONNECT.TRY_TO_DUPLICATE:
|
case CONNECT.TRY_TO_DUPLICATE:
|
||||||
component = (<DuplicateDevice { ...this.props } />)
|
component = (<DuplicateDevice {...this.props} />);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,8 +123,7 @@ class Modal extends Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => {
|
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
|
||||||
return {
|
|
||||||
modal: state.modal,
|
modal: state.modal,
|
||||||
accounts: state.accounts,
|
accounts: state.accounts,
|
||||||
devices: state.devices,
|
devices: state.devices,
|
||||||
@ -133,18 +132,15 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
|
|||||||
sendForm: state.sendForm,
|
sendForm: state.sendForm,
|
||||||
receive: state.receive,
|
receive: state.receive,
|
||||||
localStorage: state.localStorage,
|
localStorage: state.localStorage,
|
||||||
wallet: state.wallet
|
wallet: state.wallet,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => {
|
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||||
return {
|
|
||||||
modalActions: bindActionCreators(ModalActions, dispatch),
|
modalActions: bindActionCreators(ModalActions, dispatch),
|
||||||
receiveActions: bindActionCreators(ReceiveActions, dispatch),
|
receiveActions: bindActionCreators(ReceiveActions, dispatch),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// export default connect(mapStateToProps, mapDispatchToProps)(Modal);
|
// export default connect(mapStateToProps, mapDispatchToProps)(Modal);
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
connect(mapStateToProps, mapDispatchToProps)(Modal)
|
connect(mapStateToProps, mapDispatchToProps)(Modal),
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
@ -16,8 +16,8 @@ type State = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Indicator extends Component<Props, State> {
|
class Indicator extends Component<Props, State> {
|
||||||
|
|
||||||
reposition: () => void;
|
reposition: () => void;
|
||||||
|
|
||||||
state: State;
|
state: State;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
@ -26,9 +26,9 @@ class Indicator extends Component<Props, State> {
|
|||||||
this.state = {
|
this.state = {
|
||||||
style: {
|
style: {
|
||||||
width: 0,
|
width: 0,
|
||||||
left: 0
|
left: 0,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
this.reposition = this.reposition.bind(this);
|
this.reposition = this.reposition.bind(this);
|
||||||
}
|
}
|
||||||
@ -63,9 +63,9 @@ class Indicator extends Component<Props, State> {
|
|||||||
this.setState({
|
this.setState({
|
||||||
style: {
|
style: {
|
||||||
width: bounds.width,
|
width: bounds.width,
|
||||||
left: left,
|
left,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,6 @@ class Indicator extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AccountTabs = (props: any) => {
|
const AccountTabs = (props: any) => {
|
||||||
|
|
||||||
const urlParams = props.match.params;
|
const urlParams = props.match.params;
|
||||||
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
|
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
|
||||||
|
|
||||||
@ -101,6 +100,6 @@ const AccountTabs = (props: any) => {
|
|||||||
<Indicator pathname={props.match.pathname} />
|
<Indicator pathname={props.match.pathname} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AccountTabs;
|
export default AccountTabs;
|
@ -1,10 +1,12 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Notification } from '~/js/components/common/Notification';
|
import { Notification } from '~/js/components/common/Notification';
|
||||||
|
|
||||||
import type { State, TrezorDevice, Action, ThunkAction } from '~/flowtype';
|
import type {
|
||||||
|
State, TrezorDevice, Action, ThunkAction,
|
||||||
|
} from '~/flowtype';
|
||||||
import type { Account } from '~/js/reducers/AccountsReducer';
|
import type { Account } from '~/js/reducers/AccountsReducer';
|
||||||
import type { Discovery } from '~/js/reducers/DiscoveryReducer';
|
import type { Discovery } from '~/js/reducers/DiscoveryReducer';
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ const SelectedAccount = (props: Props) => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
discovery
|
discovery,
|
||||||
} = accountState;
|
} = accountState;
|
||||||
|
|
||||||
// account not found (yet). checking why...
|
// account not found (yet). checking why...
|
||||||
@ -45,7 +47,7 @@ const SelectedAccount = (props: Props) => {
|
|||||||
<Notification className="info" title="Loading accounts..." />
|
<Notification className="info" title="Loading accounts..." />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
|
// case 2: device is unavailable (created with different passphrase settings) account cannot be accessed
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
@ -57,7 +59,6 @@ const SelectedAccount = (props: Props) => {
|
|||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// case 3: device is disconnected
|
// case 3: device is disconnected
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
@ -68,22 +69,21 @@ const SelectedAccount = (props: Props) => {
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
} if (discovery.waitingForBackend) {
|
||||||
} else if (discovery.waitingForBackend) {
|
|
||||||
// case 4: backend is not working
|
// case 4: backend is not working
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<Notification className="warning" title="Backend not working" />
|
<Notification className="warning" title="Backend not working" />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
} else if (discovery.completed) {
|
} if (discovery.completed) {
|
||||||
// case 5: account not found and discovery is completed
|
// case 5: account not found and discovery is completed
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<Notification className="warning" title="Account does not exist" />
|
<Notification className="warning" title="Account does not exist" />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
// case 6: discovery is not completed yet
|
// case 6: discovery is not completed yet
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
@ -91,7 +91,6 @@ const SelectedAccount = (props: Props) => {
|
|||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
|
||||||
let notification: ?React$Element<typeof Notification> = null;
|
let notification: ?React$Element<typeof Notification> = null;
|
||||||
if (!device.connected) {
|
if (!device.connected) {
|
||||||
@ -109,9 +108,7 @@ const SelectedAccount = (props: Props) => {
|
|||||||
{ notification }
|
{ notification }
|
||||||
{ props.children }
|
{ props.children }
|
||||||
</section>
|
</section>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SelectedAccount;
|
export default SelectedAccount;
|
@ -1,7 +1,7 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { H2 } from '~/js/components/common/Heading';
|
||||||
|
|
||||||
import Tooltip from 'rc-tooltip';
|
import Tooltip from 'rc-tooltip';
|
||||||
import { QRCode } from 'react-qr-svg';
|
import { QRCode } from 'react-qr-svg';
|
||||||
@ -11,10 +11,9 @@ import { Notification } from '~/js/components/common/Notification';
|
|||||||
|
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
|
const Wrapper = styled.div``;
|
||||||
|
|
||||||
const Receive = (props: Props) => {
|
const Receive = (props: Props) => {
|
||||||
|
|
||||||
const device = props.wallet.selectedDevice;
|
const device = props.wallet.selectedDevice;
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
@ -52,18 +51,18 @@ const Receive = (props: Props) => {
|
|||||||
address = account.address;
|
address = account.address;
|
||||||
className = addressUnverified ? 'address unverified' : 'address';
|
className = addressUnverified ? 'address unverified' : 'address';
|
||||||
|
|
||||||
const tooltip = addressUnverified ?
|
const tooltip = addressUnverified
|
||||||
(<div>Unverified address.<br/>{ device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }</div>)
|
? (<div>Unverified address.<br />{ device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }</div>)
|
||||||
:
|
: (<div>{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }</div>);
|
||||||
(<div>{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }</div>);
|
|
||||||
|
|
||||||
button = (
|
button = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
|
arrowContent={<div className="rc-tooltip-arrow-inner" />}
|
||||||
overlay={tooltip}
|
overlay={tooltip}
|
||||||
placement="bottomRight">
|
placement="bottomRight"
|
||||||
|
>
|
||||||
<button className="white" onClick={event => props.showAddress(account.addressPath)}>
|
<button className="white" onClick={event => props.showAddress(account.addressPath)}>
|
||||||
<span></span>
|
<span />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
@ -73,14 +72,13 @@ const Receive = (props: Props) => {
|
|||||||
if (props.modal.opened && props.modal.windowType === 'ButtonRequest_Address') {
|
if (props.modal.opened && props.modal.windowType === 'ButtonRequest_Address') {
|
||||||
className = 'address verifying';
|
className = 'address verifying';
|
||||||
address = account.address;
|
address = account.address;
|
||||||
// ver = (<div className="confirm">Confirm address on TREZOR</div>)
|
ver = (<div className="confirm">Confirm address on TREZOR</div>);
|
||||||
button = (<div className="confirm">{ account.network } account #{ (account.index + 1) }</div>);
|
button = (<div className="confirm">{ account.network } account #{ (account.index + 1) }</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Wrapper>
|
||||||
<h2>Receive Ethereum or tokens</h2>
|
<H2>Receive Ethereum or tokens</H2>
|
||||||
|
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
{ ver }
|
{ ver }
|
||||||
<div className="value">
|
<div className="value">
|
||||||
@ -89,14 +87,12 @@ const Receive = (props: Props) => {
|
|||||||
{ button }
|
{ button }
|
||||||
</div>
|
</div>
|
||||||
{ qrCode }
|
{ qrCode }
|
||||||
</div>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default (props: Props) => {
|
export default (props: Props) => (
|
||||||
return (
|
|
||||||
<SelectedAccount {...props}>
|
<SelectedAccount {...props}>
|
||||||
<Receive {...props} />
|
<Receive {...props} />
|
||||||
</SelectedAccount>
|
</SelectedAccount>
|
||||||
);
|
);
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -7,13 +7,13 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { default as ReceiveActions } from '~/js/actions/ReceiveActions';
|
import { default as ReceiveActions } from '~/js/actions/ReceiveActions';
|
||||||
import * as TokenActions from '~/js/actions/TokenActions';
|
import * as TokenActions from '~/js/actions/TokenActions';
|
||||||
|
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||||
import Receive from './Receive';
|
import Receive from './Receive';
|
||||||
|
|
||||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
|
||||||
import type { State, Dispatch } from '~/flowtype';
|
import type { State, Dispatch } from '~/flowtype';
|
||||||
import type {
|
import type {
|
||||||
StateProps as BaseStateProps,
|
StateProps as BaseStateProps,
|
||||||
DispatchProps as BaseDispatchProps
|
DispatchProps as BaseDispatchProps,
|
||||||
} from '../SelectedAccount';
|
} from '../SelectedAccount';
|
||||||
|
|
||||||
type OwnProps = { }
|
type OwnProps = { }
|
||||||
@ -29,21 +29,17 @@ type DispatchProps = BaseDispatchProps & {
|
|||||||
|
|
||||||
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
|
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => {
|
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
|
||||||
return {
|
className: 'receive',
|
||||||
className: "receive",
|
|
||||||
selectedAccount: state.selectedAccount,
|
selectedAccount: state.selectedAccount,
|
||||||
wallet: state.wallet,
|
wallet: state.wallet,
|
||||||
|
|
||||||
receive: state.receive,
|
receive: state.receive,
|
||||||
modal: state.modal,
|
modal: state.modal,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => {
|
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||||
return {
|
|
||||||
showAddress: bindActionCreators(ReceiveActions.showAddress, dispatch),
|
showAddress: bindActionCreators(ReceiveActions.showAddress, dispatch),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Receive);
|
export default connect(mapStateToProps, mapDispatchToProps)(Receive);
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Tooltip from 'rc-tooltip';
|
import Tooltip from 'rc-tooltip';
|
||||||
@ -13,10 +13,9 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const AdvancedForm = (props: Props) => {
|
const AdvancedForm = (props: Props) => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network
|
network,
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -31,7 +30,7 @@ const AdvancedForm = (props: Props) => {
|
|||||||
errors,
|
errors,
|
||||||
warnings,
|
warnings,
|
||||||
infos,
|
infos,
|
||||||
advanced
|
advanced,
|
||||||
} = props.sendForm;
|
} = props.sendForm;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -39,15 +38,17 @@ const AdvancedForm = (props: Props) => {
|
|||||||
onGasPriceChange,
|
onGasPriceChange,
|
||||||
onGasLimitChange,
|
onGasLimitChange,
|
||||||
onNonceChange,
|
onNonceChange,
|
||||||
onDataChange
|
onDataChange,
|
||||||
} = props.sendFormActions;
|
} = props.sendFormActions;
|
||||||
|
|
||||||
if (!advanced) return (
|
if (!advanced) {
|
||||||
|
return (
|
||||||
<div className="advanced-container">
|
<div className="advanced-container">
|
||||||
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
|
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
|
||||||
{ props.children }
|
{ props.children }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const nonceTooltip = (
|
const nonceTooltip = (
|
||||||
<div className="tooltip-wrapper">
|
<div className="tooltip-wrapper">
|
||||||
@ -90,7 +91,6 @@ const AdvancedForm = (props: Props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="advanced-container opened">
|
<div className="advanced-container opened">
|
||||||
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
|
<a className="advanced" onClick={toggleAdvanced}>Advanced settings</a>
|
||||||
@ -120,10 +120,11 @@ const AdvancedForm = (props: Props) => {
|
|||||||
<label>
|
<label>
|
||||||
Gas limit
|
Gas limit
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
|
arrowContent={<div className="rc-tooltip-arrow-inner" />}
|
||||||
overlay={gasLimitTooltip}
|
overlay={gasLimitTooltip}
|
||||||
placement="top">
|
placement="top"
|
||||||
<span className="what-is-it"></span>
|
>
|
||||||
|
<span className="what-is-it" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -134,7 +135,8 @@ const AdvancedForm = (props: Props) => {
|
|||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
value={gasLimit}
|
value={gasLimit}
|
||||||
disabled={networkSymbol === currency && data.length > 0}
|
disabled={networkSymbol === currency && data.length > 0}
|
||||||
onChange={ event => onGasLimitChange(event.target.value) } />
|
onChange={event => onGasLimitChange(event.target.value)}
|
||||||
|
/>
|
||||||
{ errors.gasLimit ? (<span className="error">{ errors.gasLimit }</span>) : null }
|
{ errors.gasLimit ? (<span className="error">{ errors.gasLimit }</span>) : null }
|
||||||
{ warnings.gasLimit ? (<span className="warning">{ warnings.gasLimit }</span>) : null }
|
{ warnings.gasLimit ? (<span className="warning">{ warnings.gasLimit }</span>) : null }
|
||||||
{ calculatingGasLimit ? (<span className="info">Calculating...</span>) : null }
|
{ calculatingGasLimit ? (<span className="info">Calculating...</span>) : null }
|
||||||
@ -143,10 +145,11 @@ const AdvancedForm = (props: Props) => {
|
|||||||
<label>
|
<label>
|
||||||
Gas price
|
Gas price
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
|
arrowContent={<div className="rc-tooltip-arrow-inner" />}
|
||||||
overlay={gasPriceTooltip}
|
overlay={gasPriceTooltip}
|
||||||
placement="top">
|
placement="top"
|
||||||
<span className="what-is-it"></span>
|
>
|
||||||
|
<span className="what-is-it" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -156,7 +159,8 @@ const AdvancedForm = (props: Props) => {
|
|||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
value={gasPrice}
|
value={gasPrice}
|
||||||
onChange={ event => onGasPriceChange(event.target.value) } />
|
onChange={event => onGasPriceChange(event.target.value)}
|
||||||
|
/>
|
||||||
{ errors.gasPrice ? (<span className="error">{ errors.gasPrice }</span>) : null }
|
{ errors.gasPrice ? (<span className="error">{ errors.gasPrice }</span>) : null }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -165,13 +169,14 @@ const AdvancedForm = (props: Props) => {
|
|||||||
<label>
|
<label>
|
||||||
Data
|
Data
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
|
arrowContent={<div className="rc-tooltip-arrow-inner" />}
|
||||||
overlay={dataTooltip}
|
overlay={dataTooltip}
|
||||||
placement="top">
|
placement="top"
|
||||||
<span className="what-is-it"></span>
|
>
|
||||||
|
<span className="what-is-it" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</label>
|
</label>
|
||||||
<textarea disabled={ networkSymbol !== currency } value={ networkSymbol !== currency ? '' : data } onChange={ event => onDataChange(event.target.value) }></textarea>
|
<textarea disabled={networkSymbol !== currency} value={networkSymbol !== currency ? '' : data} onChange={event => onDataChange(event.target.value)} />
|
||||||
{ errors.data ? (<span className="error">{ errors.data }</span>) : null }
|
{ errors.data ? (<span className="error">{ errors.data }</span>) : null }
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -180,7 +185,7 @@ const AdvancedForm = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AdvancedForm;
|
export default AdvancedForm;
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
@ -37,11 +37,13 @@ export default class CoinSelectOption extends React.Component<Props> {
|
|||||||
render() {
|
render() {
|
||||||
const css = `${this.props.className} ${this.props.option.value}`;
|
const css = `${this.props.className} ${this.props.option.value}`;
|
||||||
return (
|
return (
|
||||||
<div className={ css }
|
<div
|
||||||
|
className={css}
|
||||||
onMouseDown={this.handleMouseDown.bind(this)}
|
onMouseDown={this.handleMouseDown.bind(this)}
|
||||||
onMouseEnter={this.handleMouseEnter.bind(this)}
|
onMouseEnter={this.handleMouseEnter.bind(this)}
|
||||||
onMouseMove={this.handleMouseMove.bind(this)}
|
onMouseMove={this.handleMouseMove.bind(this)}
|
||||||
title={ this.props.option.label }>
|
title={this.props.option.label}
|
||||||
|
>
|
||||||
<span>{ this.props.children }</span>
|
<span>{ this.props.children }</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
|
||||||
export const FeeSelectValue = (props: any): any => {
|
export const FeeSelectValue = (props: any): any => (
|
||||||
return (
|
|
||||||
<div>
|
<div>
|
||||||
<div className="Select-value fee-option">
|
<div className="Select-value fee-option">
|
||||||
<span className="fee-value">{ props.value.value }</span>
|
<span className="fee-value">{ props.value.value }</span>
|
||||||
@ -14,7 +13,6 @@ export const FeeSelectValue = (props: any): any => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.Node,
|
children: React.Node,
|
||||||
@ -49,10 +47,12 @@ export class FeeSelectOption extends React.Component<Props> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={ this.props.className }
|
<div
|
||||||
|
className={this.props.className}
|
||||||
onMouseDown={this.handleMouseDown.bind(this)}
|
onMouseDown={this.handleMouseDown.bind(this)}
|
||||||
onMouseEnter={this.handleMouseEnter.bind(this)}
|
onMouseEnter={this.handleMouseEnter.bind(this)}
|
||||||
onMouseMove={ this.handleMouseMove.bind(this) }>
|
onMouseMove={this.handleMouseMove.bind(this)}
|
||||||
|
>
|
||||||
<span className="fee-value">{ this.props.option.value }</span>
|
<span className="fee-value">{ this.props.option.value }</span>
|
||||||
<span className="fee-label">{ this.props.option.label }</span>
|
<span className="fee-label">{ this.props.option.label }</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ColorHash from 'color-hash';
|
import ColorHash from 'color-hash';
|
||||||
@ -24,7 +24,6 @@ type Style = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PendingTransactions = (props: Props) => {
|
const PendingTransactions = (props: Props) => {
|
||||||
|
|
||||||
const pending = props.pending.filter(tx => !tx.rejected);
|
const pending = props.pending.filter(tx => !tx.rejected);
|
||||||
if (pending.length < 1) return null;
|
if (pending.length < 1) return null;
|
||||||
|
|
||||||
@ -34,7 +33,6 @@ const PendingTransactions = (props: Props) => {
|
|||||||
const textColorFactory = new ColorHash();
|
const textColorFactory = new ColorHash();
|
||||||
|
|
||||||
const pendingTxs: React$Element<string> = pending.map((tx, i) => {
|
const pendingTxs: React$Element<string> = pending.map((tx, i) => {
|
||||||
|
|
||||||
let iconColor: Style;
|
let iconColor: Style;
|
||||||
let symbol: string;
|
let symbol: string;
|
||||||
let name: string;
|
let name: string;
|
||||||
@ -45,17 +43,17 @@ const PendingTransactions = (props: Props) => {
|
|||||||
iconColor = {
|
iconColor = {
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
background: '#000000',
|
background: '#000000',
|
||||||
borderColor: '#000000'
|
borderColor: '#000000',
|
||||||
}
|
};
|
||||||
symbol = "Unknown";
|
symbol = 'Unknown';
|
||||||
name = "Unknown";
|
name = 'Unknown';
|
||||||
} else {
|
} else {
|
||||||
const bgColor: string = bgColorFactory.hex(token.name);
|
const bgColor: string = bgColorFactory.hex(token.name);
|
||||||
iconColor = {
|
iconColor = {
|
||||||
color: textColorFactory.hex(token.name),
|
color: textColorFactory.hex(token.name),
|
||||||
background: bgColor,
|
background: bgColor,
|
||||||
borderColor: bgColor
|
borderColor: bgColor,
|
||||||
}
|
};
|
||||||
symbol = token.symbol.toUpperCase();
|
symbol = token.symbol.toUpperCase();
|
||||||
name = token.name;
|
name = token.name;
|
||||||
}
|
}
|
||||||
@ -63,8 +61,8 @@ const PendingTransactions = (props: Props) => {
|
|||||||
iconColor = {
|
iconColor = {
|
||||||
color: textColorFactory.hex(tx.network),
|
color: textColorFactory.hex(tx.network),
|
||||||
background: bgColorFactory.hex(tx.network),
|
background: bgColorFactory.hex(tx.network),
|
||||||
borderColor: bgColorFactory.hex(tx.network)
|
borderColor: bgColorFactory.hex(tx.network),
|
||||||
}
|
};
|
||||||
symbol = props.network.symbol;
|
symbol = props.network.symbol;
|
||||||
name = props.network.name;
|
name = props.network.name;
|
||||||
}
|
}
|
||||||
@ -81,7 +79,7 @@ const PendingTransactions = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="amount">{ isSmartContractTx ? tx.amount : tx.total } { symbol }</div>
|
<div className="amount">{ isSmartContractTx ? tx.amount : tx.total } { symbol }</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -90,7 +88,7 @@ const PendingTransactions = (props: Props) => {
|
|||||||
<h2>Pending transactions</h2>
|
<h2>Pending transactions</h2>
|
||||||
{ pendingTxs }
|
{ pendingTxs }
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default PendingTransactions;
|
export default PendingTransactions;
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
@ -17,7 +17,6 @@ import type { Token } from '~/flowtype';
|
|||||||
|
|
||||||
|
|
||||||
export default class SendContainer extends Component<Props> {
|
export default class SendContainer extends Component<Props> {
|
||||||
|
|
||||||
componentWillReceiveProps(newProps: Props) {
|
componentWillReceiveProps(newProps: Props) {
|
||||||
calculate(this.props, newProps);
|
calculate(this.props, newProps);
|
||||||
validation(newProps);
|
validation(newProps);
|
||||||
@ -36,13 +35,12 @@ export default class SendContainer extends Component<Props> {
|
|||||||
|
|
||||||
|
|
||||||
const Send = (props: Props) => {
|
const Send = (props: Props) => {
|
||||||
|
|
||||||
const device = props.wallet.selectedDevice;
|
const device = props.wallet.selectedDevice;
|
||||||
const {
|
const {
|
||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
discovery,
|
discovery,
|
||||||
tokens
|
tokens,
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
|
|
||||||
if (!device || !account || !discovery || !network) return null;
|
if (!device || !account || !discovery || !network) return null;
|
||||||
@ -77,9 +75,7 @@ const Send = (props: Props) => {
|
|||||||
|
|
||||||
const fiatRate = props.fiat.find(f => f.network === network);
|
const fiatRate = props.fiat.find(f => f.network === network);
|
||||||
|
|
||||||
const tokensSelectData = tokens.map(t => {
|
const tokensSelectData = tokens.map(t => ({ value: t.symbol, label: t.symbol }));
|
||||||
return { value: t.symbol, label: t.symbol };
|
|
||||||
});
|
|
||||||
tokensSelectData.unshift({ value: network.symbol, label: network.symbol });
|
tokensSelectData.unshift({ value: network.symbol, label: network.symbol });
|
||||||
|
|
||||||
const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max';
|
const setMaxClassName: string = setMax ? 'set-max enabled' : 'set-max';
|
||||||
@ -88,7 +84,7 @@ const Send = (props: Props) => {
|
|||||||
if (gasPriceNeedsUpdate) {
|
if (gasPriceNeedsUpdate) {
|
||||||
updateFeeLevelsButton = (
|
updateFeeLevelsButton = (
|
||||||
<span className="update-fee-levels">Recommended fees updated. <a onClick={updateFeeLevels}>Click here to use them</a></span>
|
<span className="update-fee-levels">Recommended fees updated. <a onClick={updateFeeLevels}>Click here to use them</a></span>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let addressClassName: ?string;
|
let addressClassName: ?string;
|
||||||
@ -103,7 +99,7 @@ const Send = (props: Props) => {
|
|||||||
let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
|
let buttonDisabled: boolean = Object.keys(errors).length > 0 || total === '0' || amount.length === 0 || address.length === 0 || sending;
|
||||||
let buttonLabel: string = 'Send';
|
let buttonLabel: string = 'Send';
|
||||||
if (networkSymbol !== currency && amount.length > 0 && !errors.amount) {
|
if (networkSymbol !== currency && amount.length > 0 && !errors.amount) {
|
||||||
buttonLabel += ` ${amount} ${ currency.toUpperCase() }`
|
buttonLabel += ` ${amount} ${currency.toUpperCase()}`;
|
||||||
} else if (networkSymbol === currency && total !== '0') {
|
} else if (networkSymbol === currency && total !== '0') {
|
||||||
buttonLabel += ` ${total} ${network.symbol}`;
|
buttonLabel += ` ${total} ${network.symbol}`;
|
||||||
}
|
}
|
||||||
@ -132,8 +128,9 @@ const Send = (props: Props) => {
|
|||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
value={address}
|
value={address}
|
||||||
className={addressClassName}
|
className={addressClassName}
|
||||||
onChange={ event => onAddressChange(event.target.value) } />
|
onChange={event => onAddressChange(event.target.value)}
|
||||||
<span className="input-icon"></span>
|
/>
|
||||||
|
<span className="input-icon" />
|
||||||
{ errors.address ? (<span className="error">{ errors.address }</span>) : null }
|
{ errors.address ? (<span className="error">{ errors.address }</span>) : null }
|
||||||
{ warnings.address ? (<span className="warning">{ warnings.address }</span>) : null }
|
{ warnings.address ? (<span className="warning">{ warnings.address }</span>) : null }
|
||||||
{ infos.address ? (<span className="info">{ infos.address }</span>) : null }
|
{ infos.address ? (<span className="info">{ infos.address }</span>) : null }
|
||||||
@ -150,7 +147,8 @@ const Send = (props: Props) => {
|
|||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
value={amount}
|
value={amount}
|
||||||
className={errors.amount ? 'not-valid' : null}
|
className={errors.amount ? 'not-valid' : null}
|
||||||
onChange={ event => onAmountChange(event.target.value) } />
|
onChange={event => onAmountChange(event.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
<a className={setMaxClassName} onClick={onSetMax}>Set max</a>
|
<a className={setMaxClassName} onClick={onSetMax}>Set max</a>
|
||||||
|
|
||||||
@ -163,7 +161,8 @@ const Send = (props: Props) => {
|
|||||||
value={currency}
|
value={currency}
|
||||||
disabled={tokensSelectData.length < 2}
|
disabled={tokensSelectData.length < 2}
|
||||||
onChange={onCurrencyChange}
|
onChange={onCurrencyChange}
|
||||||
options={ tokensSelectData } />
|
options={tokensSelectData}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{ errors.amount ? (<span className="error">{ errors.amount }</span>) : null }
|
{ errors.amount ? (<span className="error">{ errors.amount }</span>) : null }
|
||||||
{ warnings.amount ? (<span className="warning">{ warnings.amount }</span>) : null }
|
{ warnings.amount ? (<span className="warning">{ warnings.amount }</span>) : null }
|
||||||
@ -182,21 +181,24 @@ const Send = (props: Props) => {
|
|||||||
optionComponent={FeeSelectOption}
|
optionComponent={FeeSelectOption}
|
||||||
disabled={networkSymbol === currency && data.length > 0}
|
disabled={networkSymbol === currency && data.length > 0}
|
||||||
optionClassName="fee-option"
|
optionClassName="fee-option"
|
||||||
options={ feeLevels } />
|
options={feeLevels}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AdvancedForm
|
<AdvancedForm
|
||||||
selectedAccount={props.selectedAccount}
|
selectedAccount={props.selectedAccount}
|
||||||
sendForm={props.sendForm}
|
sendForm={props.sendForm}
|
||||||
sendFormActions={ props.sendFormActions }>
|
sendFormActions={props.sendFormActions}
|
||||||
|
>
|
||||||
<button disabled={buttonDisabled} onClick={event => onSend()}>{ buttonLabel }</button>
|
<button disabled={buttonDisabled} onClick={event => onSend()}>{ buttonLabel }</button>
|
||||||
</AdvancedForm>
|
</AdvancedForm>
|
||||||
|
|
||||||
<PendingTransactions
|
<PendingTransactions
|
||||||
pending={props.selectedAccount.pending}
|
pending={props.selectedAccount.pending}
|
||||||
tokens={props.selectedAccount.tokens}
|
tokens={props.selectedAccount.tokens}
|
||||||
network={ network } />
|
network={network}
|
||||||
|
/>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -7,9 +7,9 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { default as SendFormActions } from '~/js/actions/SendFormActions';
|
import { default as SendFormActions } from '~/js/actions/SendFormActions';
|
||||||
import * as SessionStorageActions from '~/js/actions/SessionStorageActions';
|
import * as SessionStorageActions from '~/js/actions/SessionStorageActions';
|
||||||
|
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||||
import SendForm from './SendForm';
|
import SendForm from './SendForm';
|
||||||
|
|
||||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
|
||||||
import type { State, Dispatch } from '~/flowtype';
|
import type { State, Dispatch } from '~/flowtype';
|
||||||
import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount';
|
import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount';
|
||||||
|
|
||||||
@ -29,23 +29,19 @@ export type DispatchProps = BaseDispatchProps & {
|
|||||||
|
|
||||||
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
|
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => {
|
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
|
||||||
return {
|
className: 'send-from',
|
||||||
className: "send-from",
|
|
||||||
selectedAccount: state.selectedAccount,
|
selectedAccount: state.selectedAccount,
|
||||||
wallet: state.wallet,
|
wallet: state.wallet,
|
||||||
|
|
||||||
sendForm: state.sendForm,
|
sendForm: state.sendForm,
|
||||||
fiat: state.fiat,
|
fiat: state.fiat,
|
||||||
localStorage: state.localStorage
|
localStorage: state.localStorage,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => {
|
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||||
return {
|
|
||||||
sendFormActions: bindActionCreators(SendFormActions, dispatch),
|
sendFormActions: bindActionCreators(SendFormActions, dispatch),
|
||||||
saveSessionStorage: bindActionCreators(SessionStorageActions.save, dispatch),
|
saveSessionStorage: bindActionCreators(SessionStorageActions.save, dispatch),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(SendForm)
|
export default connect(mapStateToProps, mapDispatchToProps)(SendForm);
|
@ -1,33 +1,31 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
const SignVerify = () => {
|
const SignVerify = () => (
|
||||||
return (
|
|
||||||
<section className="signverify">
|
<section className="signverify">
|
||||||
<div className="sign">
|
<div className="sign">
|
||||||
<h2>Sign message</h2>
|
<h2>Sign message</h2>
|
||||||
<label>Message</label>
|
<label>Message</label>
|
||||||
<textarea rows="4" maxLength="255"></textarea>
|
<textarea rows="4" maxLength="255" />
|
||||||
<label>Address</label>
|
<label>Address</label>
|
||||||
<input type="text" />
|
<input type="text" />
|
||||||
<label>Signature</label>
|
<label>Signature</label>
|
||||||
<textarea rows="4" maxLength="255" readOnly="readonly"></textarea>
|
<textarea rows="4" maxLength="255" readOnly="readonly" />
|
||||||
</div>
|
</div>
|
||||||
<div className="verify">
|
<div className="verify">
|
||||||
<h2>Verify message</h2>
|
<h2>Verify message</h2>
|
||||||
<label>Message</label>
|
<label>Message</label>
|
||||||
<textarea rows="4" maxLength="255"></textarea>
|
<textarea rows="4" maxLength="255" />
|
||||||
<label>Address</label>
|
<label>Address</label>
|
||||||
<input type="text" />
|
<input type="text" />
|
||||||
<label>Signature</label>
|
<label>Signature</label>
|
||||||
<textarea rows="4" maxLength="255"></textarea>
|
<textarea rows="4" maxLength="255" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, null)(SignVerify);
|
export default connect(null, null)(SignVerify);
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
@ -22,7 +22,7 @@ const Summary = (props: Props) => {
|
|||||||
account,
|
account,
|
||||||
network,
|
network,
|
||||||
tokens,
|
tokens,
|
||||||
pending
|
pending,
|
||||||
} = props.selectedAccount;
|
} = props.selectedAccount;
|
||||||
|
|
||||||
// flow
|
// flow
|
||||||
@ -52,15 +52,17 @@ const Summary = (props: Props) => {
|
|||||||
network={network.network}
|
network={network.network}
|
||||||
fiat={props.fiat}
|
fiat={props.fiat}
|
||||||
localStorage={props.localStorage}
|
localStorage={props.localStorage}
|
||||||
onToggle={ props.onDetailsToggle } />
|
onToggle={props.onDetailsToggle}
|
||||||
|
/>
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
Tokens
|
Tokens
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
|
arrowContent={<div className="rc-tooltip-arrow-inner" />}
|
||||||
overlay={tokensTooltip}
|
overlay={tokensTooltip}
|
||||||
placement="top">
|
placement="top"
|
||||||
<span className="what-is-it"></span>
|
>
|
||||||
|
<span className="what-is-it" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</h2>
|
</h2>
|
||||||
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 Lahod */}
|
{/* 0x58cda554935e4a1f2acbe15f8757400af275e084 Lahod */}
|
||||||
@ -70,49 +72,45 @@ const Summary = (props: Props) => {
|
|||||||
className="token-select"
|
className="token-select"
|
||||||
multi={false}
|
multi={false}
|
||||||
autoload={false}
|
autoload={false}
|
||||||
ignoreCase={ true }
|
ignoreCase
|
||||||
backspaceRemoves={ true }
|
backspaceRemoves
|
||||||
value={null}
|
value={null}
|
||||||
onChange={token => props.addToken(token, account)}
|
onChange={token => props.addToken(token, account)}
|
||||||
loadOptions={input => props.loadTokens(input, account.network)}
|
loadOptions={input => props.loadTokens(input, account.network)}
|
||||||
filterOptions={
|
filterOptions={
|
||||||
(options: Array<NetworkToken>, search: string, values: Array<NetworkToken>) => {
|
(options: Array<NetworkToken>, search: string, values: Array<NetworkToken>) => options.map((o) => {
|
||||||
return options.map(o => {
|
|
||||||
const added = tokens.find(t => t.symbol === o.symbol);
|
const added = tokens.find(t => t.symbol === o.symbol);
|
||||||
if (added) {
|
if (added) {
|
||||||
return {
|
return {
|
||||||
...o,
|
...o,
|
||||||
name: `${o.name} (Already added)`,
|
name: `${o.name} (Already added)`,
|
||||||
disabled: true
|
disabled: true,
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
})
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
valueKey="symbol"
|
valueKey="symbol"
|
||||||
labelKey="name"
|
labelKey="name"
|
||||||
placeholder="Search for token"
|
placeholder="Search for token"
|
||||||
searchPromptText="Type token name or address"
|
searchPromptText="Type token name or address"
|
||||||
noResultsText="Token not found" />
|
noResultsText="Token not found"
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SummaryTokens
|
<SummaryTokens
|
||||||
pending={pending}
|
pending={pending}
|
||||||
tokens={tokens}
|
tokens={tokens}
|
||||||
removeToken={ props.removeToken } />
|
removeToken={props.removeToken}
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
export default (props: Props) => (
|
||||||
|
|
||||||
export default (props: Props) => {
|
|
||||||
return (
|
|
||||||
<SelectedAccount {...props}>
|
<SelectedAccount {...props}>
|
||||||
<Summary {...props} />
|
<Summary {...props} />
|
||||||
</SelectedAccount>
|
</SelectedAccount>
|
||||||
);
|
);
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
@ -18,12 +18,13 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SummaryDetails = (props: Props): ?React$Element<string> => {
|
const SummaryDetails = (props: Props): ?React$Element<string> => {
|
||||||
|
if (!props.summary.details) {
|
||||||
if (!props.summary.details) return (
|
return (
|
||||||
<div className="summary-details">
|
<div className="summary-details">
|
||||||
<div className="toggle" onClick={ props.onToggle }></div>
|
<div className="toggle" onClick={props.onToggle} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const selectedCoin = props.coin;
|
const selectedCoin = props.coin;
|
||||||
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
|
const fiatRate = props.fiat.find(f => f.network === selectedCoin.network);
|
||||||
@ -32,7 +33,6 @@ const SummaryDetails = (props: Props): ?React$Element<string> => {
|
|||||||
let rateColumn = null;
|
let rateColumn = null;
|
||||||
|
|
||||||
if (fiatRate) {
|
if (fiatRate) {
|
||||||
|
|
||||||
const accountBalance = new BigNumber(props.balance);
|
const accountBalance = new BigNumber(props.balance);
|
||||||
const fiatValue = new BigNumber(fiatRate.value);
|
const fiatValue = new BigNumber(fiatRate.value);
|
||||||
const fiat = accountBalance.times(fiatValue).toFixed(2);
|
const fiat = accountBalance.times(fiatValue).toFixed(2);
|
||||||
@ -51,7 +51,7 @@ const SummaryDetails = (props: Props): ?React$Element<string> => {
|
|||||||
<div className="fiat-value">${ fiatValue.toFixed(2) }</div>
|
<div className="fiat-value">${ fiatValue.toFixed(2) }</div>
|
||||||
<div className="value">1.00 { selectedCoin.symbol }</div>
|
<div className="value">1.00 { selectedCoin.symbol }</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
} else {
|
} else {
|
||||||
balanceColumn = (
|
balanceColumn = (
|
||||||
<div className="column">
|
<div className="column">
|
||||||
@ -63,13 +63,13 @@ const SummaryDetails = (props: Props): ?React$Element<string> => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="summary-details opened">
|
<div className="summary-details opened">
|
||||||
<div className="toggle" onClick={ props.onToggle }></div>
|
<div className="toggle" onClick={props.onToggle} />
|
||||||
<div className="content">
|
<div className="content">
|
||||||
{ balanceColumn }
|
{ balanceColumn }
|
||||||
{ rateColumn }
|
{ rateColumn }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default SummaryDetails;
|
export default SummaryDetails;
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ColorHash from 'color-hash';
|
import ColorHash from 'color-hash';
|
||||||
@ -16,18 +16,17 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SummaryTokens = (props: Props) => {
|
const SummaryTokens = (props: Props) => {
|
||||||
|
|
||||||
if (!props.tokens || props.tokens.length < 1) return null;
|
if (!props.tokens || props.tokens.length < 1) return null;
|
||||||
|
|
||||||
const bgColor = new ColorHash({ lightness: 0.7 });
|
const bgColor = new ColorHash({ lightness: 0.7 });
|
||||||
const textColor = new ColorHash();
|
const textColor = new ColorHash();
|
||||||
|
|
||||||
const tokens = props.tokens.map((token, index) => {
|
const tokens = props.tokens.map((token, index) => {
|
||||||
let iconColor = {
|
const iconColor = {
|
||||||
color: textColor.hex(token.name),
|
color: textColor.hex(token.name),
|
||||||
background: bgColor.hex(token.name),
|
background: bgColor.hex(token.name),
|
||||||
borderColor: bgColor.hex(token.name)
|
borderColor: bgColor.hex(token.name),
|
||||||
}
|
};
|
||||||
|
|
||||||
const pendingAmount: BigNumber = stateUtils.getPendingAmount(props.pending, token.symbol, true);
|
const pendingAmount: BigNumber = stateUtils.getPendingAmount(props.pending, token.symbol, true);
|
||||||
const balance: string = new BigNumber(token.balance).minus(pendingAmount).toString(10);
|
const balance: string = new BigNumber(token.balance).minus(pendingAmount).toString(10);
|
||||||
@ -40,16 +39,16 @@ const SummaryTokens = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="name">{ token.name }</div>
|
<div className="name">{ token.name }</div>
|
||||||
<div className="balance">{ balance } { token.symbol }</div>
|
<div className="balance">{ balance } { token.symbol }</div>
|
||||||
<button className="transparent" onClick={ event => props.removeToken(token) }></button>
|
<button className="transparent" onClick={event => props.removeToken(token)} />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{ tokens }
|
{ tokens }
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default SummaryTokens;
|
export default SummaryTokens;
|
@ -1,15 +1,15 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||||
import Summary from './Summary';
|
import Summary from './Summary';
|
||||||
import * as SummaryActions from '~/js/actions/SummaryActions';
|
import * as SummaryActions from '~/js/actions/SummaryActions';
|
||||||
import * as TokenActions from '~/js/actions/TokenActions';
|
import * as TokenActions from '~/js/actions/TokenActions';
|
||||||
|
|
||||||
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
|
|
||||||
import type { State, Dispatch } from '~/flowtype';
|
import type { State, Dispatch } from '~/flowtype';
|
||||||
import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount';
|
import type { StateProps as BaseStateProps, DispatchProps as BaseDispatchProps } from '../SelectedAccount';
|
||||||
|
|
||||||
@ -31,9 +31,8 @@ type DispatchProps = BaseDispatchProps & {
|
|||||||
|
|
||||||
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
|
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => {
|
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
|
||||||
return {
|
className: 'summary',
|
||||||
className: "summary",
|
|
||||||
selectedAccount: state.selectedAccount,
|
selectedAccount: state.selectedAccount,
|
||||||
wallet: state.wallet,
|
wallet: state.wallet,
|
||||||
|
|
||||||
@ -41,16 +40,13 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
|
|||||||
summary: state.summary,
|
summary: state.summary,
|
||||||
fiat: state.fiat,
|
fiat: state.fiat,
|
||||||
localStorage: state.localStorage,
|
localStorage: state.localStorage,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => {
|
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||||
return {
|
|
||||||
onDetailsToggle: bindActionCreators(SummaryActions.onDetailsToggle, dispatch),
|
onDetailsToggle: bindActionCreators(SummaryActions.onDetailsToggle, dispatch),
|
||||||
addToken: bindActionCreators(TokenActions.add, dispatch),
|
addToken: bindActionCreators(TokenActions.add, dispatch),
|
||||||
loadTokens: bindActionCreators(TokenActions.load, dispatch),
|
loadTokens: bindActionCreators(TokenActions.load, dispatch),
|
||||||
removeToken: bindActionCreators(TokenActions.remove, dispatch),
|
removeToken: bindActionCreators(TokenActions.remove, dispatch),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Summary)
|
export default connect(mapStateToProps, mapDispatchToProps)(Summary);
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
@ -14,7 +14,6 @@ import type { Props } from './index';
|
|||||||
import type { TrezorDevice, Accounts } from '~/flowtype';
|
import type { TrezorDevice, Accounts } from '~/flowtype';
|
||||||
|
|
||||||
const AccountSelection = (props: Props): ?React$Element<string> => {
|
const AccountSelection = (props: Props): ?React$Element<string> => {
|
||||||
|
|
||||||
const selected = props.wallet.selectedDevice;
|
const selected = props.wallet.selectedDevice;
|
||||||
if (!selected) return null;
|
if (!selected) return null;
|
||||||
|
|
||||||
@ -53,20 +52,20 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
|
|||||||
return (
|
return (
|
||||||
<NavLink key={i} activeClassName="selected" className="account" to={url}>
|
<NavLink key={i} activeClassName="selected" className="account" to={url}>
|
||||||
{ `Account #${(account.index + 1)}` }
|
{ `Account #${(account.index + 1)}` }
|
||||||
<span>{ account.loaded ? balance : "Loading..." }</span>
|
<span>{ account.loaded ? balance : 'Loading...' }</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (selectedAccounts.length < 1) {
|
if (selectedAccounts.length < 1) {
|
||||||
if (selected.connected) {
|
if (selected.connected) {
|
||||||
const url: string = location.pathname.replace(/account+\/([0-9]*)/, `account/0`);
|
const url: string = location.pathname.replace(/account+\/([0-9]*)/, 'account/0');
|
||||||
selectedAccounts = (
|
selectedAccounts = (
|
||||||
<NavLink activeClassName="selected" className="account" to={url}>
|
<NavLink activeClassName="selected" className="account" to={url}>
|
||||||
Account #1
|
Account #1
|
||||||
<span>Loading...</span>
|
<span>Loading...</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,19 +88,19 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
|
|||||||
<div className="aside-tooltip-wrapper">
|
<div className="aside-tooltip-wrapper">
|
||||||
To add a new account, last account must have some transactions.
|
To add a new account, last account must have some transactions.
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
discoveryStatus = (
|
discoveryStatus = (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrowContent={<div className="rc-tooltip-arrow-inner"></div>}
|
arrowContent={<div className="rc-tooltip-arrow-inner" />}
|
||||||
overlay={tooltip}
|
overlay={tooltip}
|
||||||
placement="top">
|
placement="top"
|
||||||
|
>
|
||||||
<div className="add-account disabled">
|
<div className="add-account disabled">
|
||||||
Add account
|
Add account
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!selected.connected || !selected.available) {
|
} else if (!selected.connected || !selected.available) {
|
||||||
discoveryStatus = (
|
discoveryStatus = (
|
||||||
<div className="discovery-status">
|
<div className="discovery-status">
|
||||||
@ -137,6 +136,6 @@ const AccountSelection = (props: Props): ?React$Element<string> => {
|
|||||||
{ discoveryStatus }
|
{ discoveryStatus }
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AccountSelection;
|
export default AccountSelection;
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
//import React, { Node } from 'react';
|
//import React, { Node } from 'react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
@ -20,44 +20,42 @@ type TransitionMenuProps = {
|
|||||||
children?: React.Node;
|
children?: React.Node;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGroup> => {
|
const TransitionMenu = (props: TransitionMenuProps): React$Element<TransitionGroup> => (
|
||||||
return (
|
|
||||||
<TransitionGroup component="div" className="transition-container">
|
<TransitionGroup component="div" className="transition-container">
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
key={props.animationType}
|
key={props.animationType}
|
||||||
onExit= { () => { window.dispatchEvent( new Event('resize') ) } }
|
onExit={() => { window.dispatchEvent(new Event('resize')); }}
|
||||||
onExited={() => window.dispatchEvent(new Event('resize'))}
|
onExited={() => window.dispatchEvent(new Event('resize'))}
|
||||||
in={ true }
|
in
|
||||||
out={ true }
|
out
|
||||||
classNames={props.animationType}
|
classNames={props.animationType}
|
||||||
appear={false}
|
appear={false}
|
||||||
timeout={ 300 }>
|
timeout={300}
|
||||||
|
>
|
||||||
{ props.children }
|
{ props.children }
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
)
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const Aside = (props: Props): React$Element<typeof StickyContainer | string> => {
|
const Aside = (props: Props): React$Element<typeof StickyContainer | string> => {
|
||||||
|
|
||||||
const selected: ?TrezorDevice = props.wallet.selectedDevice;
|
const selected: ?TrezorDevice = props.wallet.selectedDevice;
|
||||||
const { location } = props.router;
|
const { location } = props.router;
|
||||||
|
|
||||||
if (location.pathname === '/' || !selected) return (<aside></aside>);
|
if (location.pathname === '/' || !selected) return (<aside />);
|
||||||
|
|
||||||
let menu = <section></section>;
|
let menu = <section />;
|
||||||
|
|
||||||
if (props.deviceDropdownOpened) {
|
if (props.deviceDropdownOpened) {
|
||||||
menu = <DeviceDropdown {...props} />;
|
menu = <DeviceDropdown {...props} />;
|
||||||
} else if (location.state.network) {
|
} else if (location.state.network) {
|
||||||
menu = (
|
menu = (
|
||||||
<TransitionMenu animationType={ "slide-left" }>
|
<TransitionMenu animationType="slide-left">
|
||||||
<AccountSelection {...props} />
|
<AccountSelection {...props} />
|
||||||
</TransitionMenu>
|
</TransitionMenu>
|
||||||
);
|
);
|
||||||
} else if (selected.features && !selected.features.bootloader_mode && selected.features.initialized) {
|
} else if (selected.features && !selected.features.bootloader_mode && selected.features.initialized) {
|
||||||
menu = (
|
menu = (
|
||||||
<TransitionMenu animationType={ "slide-right" }>
|
<TransitionMenu animationType="slide-right">
|
||||||
<CoinSelection {...props} />
|
<CoinSelection {...props} />
|
||||||
</TransitionMenu>
|
</TransitionMenu>
|
||||||
);
|
);
|
||||||
@ -73,7 +71,7 @@ const Aside = (props: Props): React$Element<typeof StickyContainer | string> =>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</StickyContainer>
|
</StickyContainer>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Aside;
|
export default Aside;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
@ -20,15 +20,15 @@ const CoinSelection = (props: Props): React$Element<string> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const walletCoins = config.coins.map(item => {
|
const walletCoins = config.coins.map((item) => {
|
||||||
const url = `${baseUrl}/network/${item.network}/account/0`;
|
const url = `${baseUrl}/network/${item.network}/account/0`;
|
||||||
const className = `coin ${ item.network }`
|
const className = `coin ${item.network}`;
|
||||||
return (
|
return (
|
||||||
<NavLink key={item.network} to={url} className={className}>
|
<NavLink key={item.network} to={url} className={className}>
|
||||||
{ item.name }
|
{ item.name }
|
||||||
</NavLink>
|
</NavLink>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
@ -56,6 +56,6 @@ const CoinSelection = (props: Props): React$Element<string> => {
|
|||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CoinSelection;
|
export default CoinSelection;
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
@ -9,49 +9,48 @@ import type { Props } from './index';
|
|||||||
import type { TrezorDevice } from '~/flowtype';
|
import type { TrezorDevice } from '~/flowtype';
|
||||||
|
|
||||||
export const DeviceSelect = (props: Props) => {
|
export const DeviceSelect = (props: Props) => {
|
||||||
|
|
||||||
const { devices } = props;
|
const { devices } = props;
|
||||||
const { transport } = props.connect;
|
const { transport } = props.connect;
|
||||||
|
|
||||||
const selected: ?TrezorDevice = props.wallet.selectedDevice;
|
const selected: ?TrezorDevice = props.wallet.selectedDevice;
|
||||||
if (!selected) return null;
|
if (!selected) return null;
|
||||||
|
|
||||||
let deviceStatus: string = "Connected";
|
let deviceStatus: string = 'Connected';
|
||||||
let css: string = "device-select device";
|
let css: string = 'device-select device';
|
||||||
if (props.deviceDropdownOpened) css += " opened";
|
if (props.deviceDropdownOpened) css += ' opened';
|
||||||
|
|
||||||
if (!selected.connected) {
|
if (!selected.connected) {
|
||||||
css += " disconnected";
|
css += ' disconnected';
|
||||||
deviceStatus = "Disconnected";
|
deviceStatus = 'Disconnected';
|
||||||
} else if (!selected.available) {
|
} else if (!selected.available) {
|
||||||
css += " unavailable";
|
css += ' unavailable';
|
||||||
deviceStatus = "Unavailable";
|
deviceStatus = 'Unavailable';
|
||||||
} else {
|
} else {
|
||||||
if (selected.unacquired) {
|
if (selected.unacquired) {
|
||||||
css += " unacquired";
|
css += ' unacquired';
|
||||||
deviceStatus = "Used in other window";
|
deviceStatus = 'Used in other window';
|
||||||
}
|
}
|
||||||
if (selected.isUsedElsewhere) {
|
if (selected.isUsedElsewhere) {
|
||||||
css += " used-elsewhere";
|
css += ' used-elsewhere';
|
||||||
deviceStatus = "Used in other window";
|
deviceStatus = 'Used in other window';
|
||||||
} else if (selected.featuresNeedsReload) {
|
} else if (selected.featuresNeedsReload) {
|
||||||
css += " reload-features";
|
css += ' reload-features';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected.features && selected.features.major_version > 1) {
|
if (selected.features && selected.features.major_version > 1) {
|
||||||
css += " trezor-t";
|
css += ' trezor-t';
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOpen = () => {
|
const handleOpen = () => {
|
||||||
props.toggleDeviceDropdown(props.deviceDropdownOpened ? false : true);
|
props.toggleDeviceDropdown(!props.deviceDropdownOpened);
|
||||||
}
|
};
|
||||||
|
|
||||||
const deviceCount = devices.length;
|
const deviceCount = devices.length;
|
||||||
const webusb: boolean = (transport && transport.version.indexOf('webusb') >= 0) ? true : false;
|
const webusb: boolean = !!((transport && transport.version.indexOf('webusb') >= 0));
|
||||||
const disabled: boolean = (devices.length < 1 && !webusb) || (devices.length === 1 && !selected.features);
|
const disabled: boolean = (devices.length < 1 && !webusb) || (devices.length === 1 && !selected.features);
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
css += " disabled";
|
css += ' disabled';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -61,10 +60,10 @@ export const DeviceSelect = (props: Props) => {
|
|||||||
<span className="status">{ deviceStatus }</span>
|
<span className="status">{ deviceStatus }</span>
|
||||||
</div>
|
</div>
|
||||||
{ deviceCount > 1 ? <div className="counter">{ deviceCount }</div> : null }
|
{ deviceCount > 1 ? <div className="counter">{ deviceCount }</div> : null }
|
||||||
<div className="arrow"></div>
|
<div className="arrow" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
type DeviceMenuItem = {
|
type DeviceMenuItem = {
|
||||||
type: string;
|
type: string;
|
||||||
@ -72,8 +71,8 @@ type DeviceMenuItem = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DeviceDropdown extends Component<Props> {
|
export class DeviceDropdown extends Component<Props> {
|
||||||
|
|
||||||
mouseDownHandler: (event: MouseEvent) => void;
|
mouseDownHandler: (event: MouseEvent) => void;
|
||||||
|
|
||||||
blurHandler: (event: FocusEvent) => void;
|
blurHandler: (event: FocusEvent) => void;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
@ -84,8 +83,7 @@ export class DeviceDropdown extends Component<Props> {
|
|||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
const { transport } = this.props.connect;
|
const { transport } = this.props.connect;
|
||||||
if (transport && transport.version.indexOf('webusb') >= 0)
|
if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
|
||||||
TrezorConnect.renderWebUSBButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mouseDownHandler(event: MouseEvent): void {
|
mouseDownHandler(event: MouseEvent): void {
|
||||||
@ -113,8 +111,7 @@ export class DeviceDropdown extends Component<Props> {
|
|||||||
window.addEventListener('mousedown', this.mouseDownHandler, false);
|
window.addEventListener('mousedown', this.mouseDownHandler, false);
|
||||||
// window.addEventListener('blur', this.blurHandler, false);
|
// window.addEventListener('blur', this.blurHandler, false);
|
||||||
const { transport } = this.props.connect;
|
const { transport } = this.props.connect;
|
||||||
if (transport && transport.version.indexOf('webusb') >= 0)
|
if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
|
||||||
TrezorConnect.renderWebUSBButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(): void {
|
componentWillUnmount(): void {
|
||||||
@ -136,7 +133,6 @@ export class DeviceDropdown extends Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
const { devices } = this.props;
|
const { devices } = this.props;
|
||||||
const { transport } = this.props.connect;
|
const { transport } = this.props.connect;
|
||||||
const selected: ?TrezorDevice = this.props.wallet.selectedDevice;
|
const selected: ?TrezorDevice = this.props.wallet.selectedDevice;
|
||||||
@ -152,25 +148,23 @@ export class DeviceDropdown extends Component<Props> {
|
|||||||
const deviceMenuItems: Array<DeviceMenuItem> = [];
|
const deviceMenuItems: Array<DeviceMenuItem> = [];
|
||||||
|
|
||||||
if (selected.isUsedElsewhere) {
|
if (selected.isUsedElsewhere) {
|
||||||
deviceMenuItems.push({ type: "reload", label: "Renew session" });
|
deviceMenuItems.push({ type: 'reload', label: 'Renew session' });
|
||||||
} else if (selected.featuresNeedsReload) {
|
} else if (selected.featuresNeedsReload) {
|
||||||
deviceMenuItems.push({ type: "reload", label: "Renew session" });
|
deviceMenuItems.push({ type: 'reload', label: 'Renew session' });
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceMenuItems.push({ type: "settings", label: "Device settings" });
|
deviceMenuItems.push({ type: 'settings', label: 'Device settings' });
|
||||||
if (selected.features && selected.features.passphrase_protection && selected.connected && selected.available) {
|
if (selected.features && selected.features.passphrase_protection && selected.connected && selected.available) {
|
||||||
deviceMenuItems.push({ type: "clone", label: "Clone device" });
|
deviceMenuItems.push({ type: 'clone', label: 'Clone device' });
|
||||||
}
|
}
|
||||||
//if (selected.remember) {
|
//if (selected.remember) {
|
||||||
deviceMenuItems.push({ type: "forget", label: "Forget device" });
|
deviceMenuItems.push({ type: 'forget', label: 'Forget device' });
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
const deviceMenuButtons = deviceMenuItems.map((item, index) => {
|
const deviceMenuButtons = deviceMenuItems.map((item, index) => (
|
||||||
return (
|
<div key={item.type} className={item.type} onClick={event => this.onDeviceMenuClick(item, selected)}>{ item.label}</div>
|
||||||
<div key={ item.type } className={ item.type } onClick={ (event) => this.onDeviceMenuClick(item, selected) }>{ item.label}</div>
|
));
|
||||||
)
|
|
||||||
});
|
|
||||||
currentDeviceMenu = deviceMenuButtons.length < 1 ? null : (
|
currentDeviceMenu = deviceMenuButtons.length < 1 ? null : (
|
||||||
<div className="device-menu">
|
<div className="device-menu">
|
||||||
{ deviceMenuButtons }
|
{ deviceMenuButtons }
|
||||||
@ -181,21 +175,21 @@ export class DeviceDropdown extends Component<Props> {
|
|||||||
const deviceList = devices.map((dev, index) => {
|
const deviceList = devices.map((dev, index) => {
|
||||||
if (dev === selected) return null;
|
if (dev === selected) return null;
|
||||||
|
|
||||||
let deviceStatus: string = "Connected";
|
let deviceStatus: string = 'Connected';
|
||||||
let css: string = "device item"
|
let css: string = 'device item';
|
||||||
if (dev.unacquired || dev.isUsedElsewhere) {
|
if (dev.unacquired || dev.isUsedElsewhere) {
|
||||||
deviceStatus = "Used in other window";
|
deviceStatus = 'Used in other window';
|
||||||
css += " unacquired";
|
css += ' unacquired';
|
||||||
} else if (!dev.connected) {
|
} else if (!dev.connected) {
|
||||||
deviceStatus = "Disconnected";
|
deviceStatus = 'Disconnected';
|
||||||
css += " disconnected";
|
css += ' disconnected';
|
||||||
} else if (!dev.available) {
|
} else if (!dev.available) {
|
||||||
deviceStatus = "Unavailable";
|
deviceStatus = 'Unavailable';
|
||||||
css += " unavailable";
|
css += ' unavailable';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev.features && dev.features.major_version > 1) {
|
if (dev.features && dev.features.major_version > 1) {
|
||||||
css += " trezor-t";
|
css += ' trezor-t';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -204,11 +198,14 @@ export class DeviceDropdown extends Component<Props> {
|
|||||||
<span className="label">{ dev.instanceLabel }</span>
|
<span className="label">{ dev.instanceLabel }</span>
|
||||||
<span className="status">{ deviceStatus }</span>
|
<span className="status">{ deviceStatus }</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="forget-button" onClick={ (event) => {
|
<div
|
||||||
|
className="forget-button"
|
||||||
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.onDeviceMenuClick({ type: 'forget', label: '' }, dev);
|
this.onDeviceMenuClick({ type: 'forget', label: '' }, dev);
|
||||||
} }> </div>
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// https://github.com/KyleAMathews/react-headroom/blob/master/src/shouldUpdate.js
|
// https://github.com/KyleAMathews/react-headroom/blob/master/src/shouldUpdate.js
|
||||||
|
|
||||||
@ -14,18 +14,25 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class StickyContainer extends React.PureComponent<Props> {
|
export default class StickyContainer extends React.PureComponent<Props> {
|
||||||
|
|
||||||
// Class variables.
|
// Class variables.
|
||||||
currentScrollY: number = 0;
|
currentScrollY: number = 0;
|
||||||
|
|
||||||
lastKnownScrollY: number = 0;
|
lastKnownScrollY: number = 0;
|
||||||
|
|
||||||
topOffset: number = 0;
|
topOffset: number = 0;
|
||||||
|
|
||||||
firstRender: boolean = false;
|
firstRender: boolean = false;
|
||||||
|
|
||||||
framePending: boolean = false;
|
framePending: boolean = false;
|
||||||
|
|
||||||
stickToBottom: boolean = false;
|
stickToBottom: boolean = false;
|
||||||
|
|
||||||
top: number = 0;
|
top: number = 0;
|
||||||
|
|
||||||
aside: ?HTMLElement;
|
aside: ?HTMLElement;
|
||||||
|
|
||||||
wrapper: ?HTMLElement;
|
wrapper: ?HTMLElement;
|
||||||
|
|
||||||
subscribers = [];
|
subscribers = [];
|
||||||
|
|
||||||
handleResize = (event: Event) => {
|
handleResize = (event: Event) => {
|
||||||
@ -68,24 +75,21 @@ export default class StickyContainer extends React.PureComponent<Props> {
|
|||||||
if (this.topOffset > 0) this.topOffset = 0;
|
if (this.topOffset > 0) this.topOffset = 0;
|
||||||
if (maxTop < 0 && this.topOffset < maxTop) this.topOffset = maxTop;
|
if (maxTop < 0 && this.topOffset < maxTop) this.topOffset = maxTop;
|
||||||
wrapper.style.top = `${this.topOffset}px`;
|
wrapper.style.top = `${this.topOffset}px`;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
wrapper.classList.remove('fixed');
|
wrapper.classList.remove('fixed');
|
||||||
wrapper.style.top = `0px`;
|
wrapper.style.top = '0px';
|
||||||
this.topOffset = 0;
|
this.topOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrapperBounds.height > viewportHeight) {
|
if (wrapperBounds.height > viewportHeight) {
|
||||||
wrapper.classList.remove('fixed-bottom');
|
wrapper.classList.remove('fixed-bottom');
|
||||||
} else {
|
} else if (wrapper.classList.contains('fixed-bottom')) {
|
||||||
if (wrapper.classList.contains('fixed-bottom')) {
|
|
||||||
if (bottomBounds.top < wrapperBounds.bottom - bottomBounds.height) {
|
if (bottomBounds.top < wrapperBounds.bottom - bottomBounds.height) {
|
||||||
wrapper.classList.remove('fixed-bottom');
|
wrapper.classList.remove('fixed-bottom');
|
||||||
}
|
}
|
||||||
} else if (bottomBounds.bottom < viewportHeight) {
|
} else if (bottomBounds.bottom < viewportHeight) {
|
||||||
wrapper.classList.add('fixed-bottom');
|
wrapper.classList.add('fixed-bottom');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
aside.style.minHeight = `${wrapperBounds.height}px`;
|
aside.style.minHeight = `${wrapperBounds.height}px`;
|
||||||
}
|
}
|
||||||
@ -135,7 +139,8 @@ export default class StickyContainer extends React.PureComponent<Props> {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="sticky-container"
|
className="sticky-container"
|
||||||
ref={ node => this.wrapper = node }>
|
ref={node => this.wrapper = node}
|
||||||
|
>
|
||||||
{ this.props.children }
|
{ this.props.children }
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -43,8 +43,7 @@ type DispatchProps = {
|
|||||||
|
|
||||||
export type Props = StateProps & DispatchProps;
|
export type Props = StateProps & DispatchProps;
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => {
|
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({
|
||||||
return {
|
|
||||||
connect: state.connect,
|
connect: state.connect,
|
||||||
accounts: state.accounts,
|
accounts: state.accounts,
|
||||||
router: state.router,
|
router: state.router,
|
||||||
@ -55,11 +54,9 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
|
|||||||
wallet: state.wallet,
|
wallet: state.wallet,
|
||||||
devices: state.devices,
|
devices: state.devices,
|
||||||
pending: state.pending,
|
pending: state.pending,
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => {
|
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
|
||||||
return {
|
|
||||||
//onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch),
|
//onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch),
|
||||||
toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch),
|
toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch),
|
||||||
addAccount: bindActionCreators(TrezorConnectActions.addAccount, dispatch),
|
addAccount: bindActionCreators(TrezorConnectActions.addAccount, dispatch),
|
||||||
@ -68,10 +65,9 @@ const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps>
|
|||||||
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
|
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
|
||||||
gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch),
|
gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch),
|
||||||
onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch),
|
onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// export default connect(mapStateToProps, mapDispatchToProps)(Aside);
|
// export default connect(mapStateToProps, mapDispatchToProps)(Aside);
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
connect(mapStateToProps, mapDispatchToProps)(Aside)
|
connect(mapStateToProps, mapDispatchToProps)(Aside),
|
||||||
);
|
);
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -28,8 +28,7 @@ type ContentProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const Content = (props: ContentProps) => {
|
const Content = (props: ContentProps) => (
|
||||||
return (
|
|
||||||
<article>
|
<article>
|
||||||
<nav>
|
<nav>
|
||||||
<Route path="/device/:device/network/:network/account/:account" component={AccountTabs} />
|
<Route path="/device/:device/network/:network/account/:account" component={AccountTabs} />
|
||||||
@ -41,10 +40,8 @@ const Content = (props: ContentProps) => {
|
|||||||
<Footer />
|
<Footer />
|
||||||
</article>
|
</article>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const Wallet = (props: WalletContainerProps) => {
|
const Wallet = (props: WalletContainerProps) => (
|
||||||
return (
|
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<Header />
|
<Header />
|
||||||
{/* <div>{ props.wallet.online ? "ONLINE" : "OFFLINE" }</div> */}
|
{/* <div>{ props.wallet.online ? "ONLINE" : "OFFLINE" }</div> */}
|
||||||
@ -57,14 +54,11 @@ const Wallet = (props: WalletContainerProps) => {
|
|||||||
<ModalContainer />
|
<ModalContainer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State, own: {}): WalletContainerProps => {
|
const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State, own: {}): WalletContainerProps => ({
|
||||||
return {
|
wallet: state.wallet,
|
||||||
wallet: state.wallet
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
connect(mapStateToProps, null)(Wallet)
|
connect(mapStateToProps, null)(Wallet),
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -8,20 +8,20 @@ import { Notification } from '~/js/components/common/Notification';
|
|||||||
import * as TrezorConnectActions from '~/js/actions/TrezorConnectActions';
|
import * as TrezorConnectActions from '~/js/actions/TrezorConnectActions';
|
||||||
|
|
||||||
import type { State, Dispatch } from '~/flowtype';
|
import type { State, Dispatch } from '~/flowtype';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
acquiring: boolean;
|
acquiring: boolean;
|
||||||
acquireDevice: typeof TrezorConnectActions.acquire
|
acquireDevice: typeof TrezorConnectActions.acquire
|
||||||
}
|
}
|
||||||
|
|
||||||
const Acquire = (props: Props) => {
|
const Acquire = (props: Props) => {
|
||||||
|
|
||||||
const actions = props.acquiring ? [] : [
|
const actions = props.acquiring ? [] : [
|
||||||
{
|
{
|
||||||
label: 'Acquire device',
|
label: 'Acquire device',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
props.acquireDevice()
|
props.acquireDevice();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -35,17 +35,13 @@ const Acquire = (props: Props) => {
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
(state: State) => {
|
(state: State) => ({
|
||||||
return {
|
acquiring: state.connect.acquiring,
|
||||||
acquiring: state.connect.acquiring
|
}),
|
||||||
};
|
(dispatch: Dispatch) => ({
|
||||||
},
|
|
||||||
(dispatch: Dispatch) => {
|
|
||||||
return {
|
|
||||||
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
|
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
|
||||||
};
|
}),
|
||||||
}
|
|
||||||
)(Acquire);
|
)(Acquire);
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
const Bootloader = () => {
|
const Bootloader = () => (
|
||||||
return (
|
|
||||||
<section className="device-settings">
|
<section className="device-settings">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<h2>Your device is in firmware update mode</h2>
|
<h2>Your device is in firmware update mode</h2>
|
||||||
@ -14,6 +13,5 @@ const Bootloader = () => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, null)(Bootloader);
|
export default connect(null, null)(Bootloader);
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import DashboardImg from '~/images/dashboard.png';
|
import DashboardImg from '~/images/dashboard.png';
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => (
|
||||||
return (
|
|
||||||
<section className="dashboard">
|
<section className="dashboard">
|
||||||
<h2>Dashboard</h2>
|
<h2>Dashboard</h2>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
@ -17,6 +16,5 @@ const Dashboard = () => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, null)(Dashboard);
|
export default connect(null, null)(Dashboard);
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
export const DeviceSettings = () => {
|
export const DeviceSettings = () => (
|
||||||
return (
|
|
||||||
<section className="device-settings">
|
<section className="device-settings">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<h2>Device settings is under construction</h2>
|
<h2>Device settings is under construction</h2>
|
||||||
@ -15,6 +14,5 @@ export const DeviceSettings = () => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, null)(DeviceSettings);
|
export default connect(null, null)(DeviceSettings);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
@ -16,7 +16,6 @@ type State = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AccountTabs = (props: any): any => {
|
const AccountTabs = (props: any): any => {
|
||||||
|
|
||||||
const urlParams = props.match.params;
|
const urlParams = props.match.params;
|
||||||
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
|
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;
|
||||||
|
|
||||||
@ -25,6 +24,6 @@ const AccountTabs = (props: any): any => {
|
|||||||
<a>Device settings</a>
|
<a>Device settings</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default AccountTabs;
|
export default AccountTabs;
|
@ -1,12 +1,11 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
const Initialize = () => {
|
const Initialize = () => (
|
||||||
return (
|
|
||||||
<section className="device-settings">
|
<section className="device-settings">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<h2>Your device is in not initialized</h2>
|
<h2>Your device is in not initialized</h2>
|
||||||
@ -15,6 +14,5 @@ const Initialize = () => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, null)(Initialize);
|
export default connect(null, null)(Initialize);
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
export const WalletSettings = () => {
|
export const WalletSettings = () => (
|
||||||
return (
|
|
||||||
<section className="settings">
|
<section className="settings">
|
||||||
Wallet settings
|
Wallet settings
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, null)(WalletSettings);
|
export default connect(null, null)(WalletSettings);
|
||||||
|
30
src/js/config/colors.js
Normal file
30
src/js/config/colors.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export default {
|
||||||
|
color_white: '#ffffff',
|
||||||
|
|
||||||
|
color_header: '#212121',
|
||||||
|
color_body: '#E3E3E3',
|
||||||
|
color_main: '#FBFBFB',
|
||||||
|
color_landing: '#F9F9F9',
|
||||||
|
|
||||||
|
color_text_primary: '#494949',
|
||||||
|
color_text_secondary: '#757575',
|
||||||
|
|
||||||
|
color_gray_light: '#F2F2F2', // hover menu
|
||||||
|
color_divider: '#E3E3E3', //#EBEBEB,
|
||||||
|
|
||||||
|
color_green_primary: '#01B757',
|
||||||
|
color_green_secondary: '#00AB51',
|
||||||
|
color_green_tertiary: '#009546',
|
||||||
|
|
||||||
|
color_info_primary: '#1E7FF0',
|
||||||
|
color_info_secondary: '#E1EFFF',
|
||||||
|
|
||||||
|
color_warning_primary: '#EB8A00',
|
||||||
|
color_warning_secondary: '#FFEFD9',
|
||||||
|
|
||||||
|
color_success_primary: '#01B757',
|
||||||
|
color_success_secondary: '#DFFFEE',
|
||||||
|
|
||||||
|
color_error_primary: '#ED1212',
|
||||||
|
color_error_secondary: '#FFE9E9',
|
||||||
|
};
|
@ -1,6 +1,4 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
@ -15,7 +13,7 @@ if (root) {
|
|||||||
|
|
||||||
window.onbeforeunload = () => {
|
window.onbeforeunload = () => {
|
||||||
store.dispatch(onBeforeUnload());
|
store.dispatch(onBeforeUnload());
|
||||||
}
|
};
|
||||||
|
|
||||||
if (typeof module !== undefined && module.hasOwnProperty('hot')) {
|
if (typeof module !== undefined && module.hasOwnProperty('hot')) {
|
||||||
// $FlowIssue
|
// $FlowIssue
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user