Merge pull request #9 from satoshilabs/styled-components-refactor

Styled components refactor - removed less completely
pull/21/head
Szymon Lesisz 6 years ago committed by GitHub
commit 089aa0e829
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,7 +16,10 @@
"regenerator": true
}],
["module-resolver", {
"root": ["./src"]
"root": ["./src"],
"alias": {
"public": ["./public"]
}
}],
"babel-plugin-styled-components"
],

@ -1,4 +1,5 @@
solidity
public
coverage
images
node_modules
node_modules
src/flowtype/npm

@ -9,6 +9,7 @@
"jest": true
},
"rules": {
"no-plusplus": 0,
"class-methods-use-this": 0,
"react/require-default-props": 0,
"react/forbid-prop-types": 0,

@ -1,7 +1,90 @@
# TREZOR Ethereum Wallet
# TREZOR Wallet
To install dependencies run `npm install` or `yarn`
To start locally run `npm run dev` or `yarn run dev`
To build the project run `npm run build` or `yarn run build`
======================================
## Project structure
The project is divided into two parts - data that are used when compiling the project and data that aren't.
All data that are used during compilation are stored inside the `src/` folder.
### `src/` folder
At the root of the `src/` folder are all files or folders that are shared.
- `src/index.js` - root of the application
- `src/views/` - contains all React `components` and `views`
- `src/store/` - todo
- `src/actions/` - todo
- `src/reducers/` - todo
- todo other folders/files?
## Component
Component is what you'd intuitively think it is. It's a regular React component (doesn't matter whether statefull or stateless).
### **Global components**
All global components are are stored in `src/views/components/` folder.
Global components are such components that are shared across multiple different components or views.
- For example there's a `Button` component that is used in both `ConnectDevice` and `AccountSend`. `ConnectDevice` and `AccountSend` are both placed accross different views so the `Button` component they're both using must be stored in the global `components` folder.
### **Naming & structure convention**
Each component has it's own folder. Name of the folder is same as is the name of the component (camel case and first letter is capitalized, e.g.: *MyComponent*).
If you want to create multiple components of the same type you should put them into a common folder with a lowercase name like this `views/componets/type/MyComponent`.
- For example there are different types of modals like `confirm` or `device`.
Because the `confirm` and `device` modals are subtypes of modal the folder structure looks like this
```
modals/confirm/Address
modals/confirm/SignTx
modals/device/Duplicate
```
Where `Address`, `SignTx` and `Duplicate` are the actual modal components.
Inside each component's folder is `index.js` file containing the actual component's code with following export at the end of the file `export default ComponentName;`
There's only one render function per component's index file. If you need more renders you should probably create new component.
Each component may contain other components in its own `components/` folder. Component's components may contain another components etc.
## View
The difference between `view` and `component` is rather semantical then technical.
From the React's standpoint a view is just another component. So when is component a regular component and when is it a view?
View components basically copy router structure and are composed either from view's own components or global components.
### **Naming & structure convention**
Both naming and structure conventions are similar to components conventions.
Each view has its own folder in `views/` folder. Name of this folder is same as is the view's name (camel case and first letter is capitalized, e.g.: *MyView*).
Inside the view's folder is always an `index.js` file containing view's code itself.
View may contain own components inside view's folder - in the `components/` folder. One of the differences between a component and a view is that view can hav another views. Of course those views may have their own components and views, etc.
```
views/
MyView/
components/
views/
index.js
MyAnotherView/
components/
index.js
```
- For example there's a `Landing` component that is displayed if no device is detected. This view contains its own components in a `Landing/components/` folder. These components are then used exclusively in `Landing/index.js` and together compose different versions of the `Landing` view.
<!-- If you aren't sure whether you should create component or view follow this discussion
- If the route has following structure `/nameA/nameB/...` then `nameA` is probably a view and `nameB` is its subview
- If the route has following structure `/nameA/:parameter/nameB/...` then `nameA` is a view
- If the are some elements -->
npm install / yarn
npm run dev / yarn run dev
npm run build / yarn run build

@ -3,28 +3,28 @@
"version": "1.0.0",
"author": "TREZOR <info@trezor.io>",
"description": "",
"bugs": {
"url": "https://github.com/szymonlesisz/trezor-connect-react-boilerplate/issues"
},
"bin": {
"flow": "./node_modules/flow-bin"
},
"license": "LGPL-3.0+",
"scripts": {
"dev": "webpack-dev-server --config ./webpack/config.dev.babel.js --mode development",
"dev:local": "webpack-dev-server --config ./webpack/config.dev.local.babel.js --mode development",
"build": "rm -rf build && webpack --config ./webpack/config.prod.babel.js --progress",
"dev": "npx webpack-dev-server --config ./webpack/dev.babel.js",
"dev:local": "npx webpack-dev-server --config ./webpack/local.babel.js",
"build:clean": "rm -rf build",
"build:production": "npx webpack --config ./webpack/production.babel.js --progress --bail",
"build": "run-s build:*",
"flow": "flow check src",
"lint": "run-s lint:*",
"lint:js": "eslint ./src ./webpack",
"lint:css": "stylelint './src/**/*.js'",
"lint:js": "npx eslint ./src ./webpack",
"lint:css": "npx stylelint './src/**/*.js'",
"test": "run-s test:*",
"test:unit": "jest",
"test-unit:watch": "jest -o --watch"
"test:unit": "npx jest",
"test-unit:watch": "npx jest -o --watch"
},
"dependencies": {
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"bignumber.js": "2.4.0",
"color-hash": "^1.0.3",
"copy-webpack-plugin": "^4.5.2",
"date-fns": "^1.29.0",
@ -33,14 +33,12 @@
"ethereumjs-util": "^5.1.4",
"hdkey": "^0.8.0",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.4.1",
"npm-run-all": "^4.1.3",
"prop-types": "^15.6.2",
"raf": "^3.4.0",
"raven-js": "^3.22.3",
"rc-tooltip": "^3.7.0",
"react": "^16.2.0",
"react-css-transition": "^0.7.4",
"react": "^16.4.2",
"react-dom": "^16.2.0",
"react-hot-loader": "^4.3.4",
"react-qr-svg": "^2.1.0",
@ -56,10 +54,12 @@
"redux-raven-middleware": "^1.2.0",
"redux-thunk": "^2.2.0",
"styled-components": "^3.3.3",
"styled-media-query": "^2.0.2",
"styled-normalize": "^8.0.0",
"trezor-connect": "5.0.30",
"web3": "^0.19.0",
"webpack": "^4.16.3",
"webpack-bundle-analyzer": "^2.13.1",
"whatwg-fetch": "^2.0.4",
"yarn-run-all": "^3.1.1"
},
@ -80,6 +80,7 @@
"eslint": "^4",
"eslint-config-airbnb": "^17.0.0",
"eslint-import-resolver-babel-module": "^4.0.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-flowtype": "^2.50.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jest": "^21.18.0",
@ -90,10 +91,12 @@
"jest": "^23.4.2",
"less": "^3.0.1",
"less-loader": "4.1.0",
"stylelint": "^9.4.0",
"stylelint": "^8.0.0",
"stylelint-config-standard": "^18.2.0",
"stylelint-config-styled-components": "^0.1.1",
"stylelint-custom-processor-loader": "^0.5.0",
"stylelint-processor-styled-components": "^1.3.2",
"stylelint-webpack-plugin": "^0.10.5",
"webpack-cli": "^2.1.3",
"webpack-dev-server": "^3.1.4",
"yargs": "11.0.0"

@ -4,12 +4,6 @@ import * as ACCOUNT from 'actions/constants/account';
import type { Action, TrezorDevice } from 'flowtype';
import type { State } from 'reducers/AccountsReducer';
export type AccountAction =
AccountFromStorageAction
| AccountCreateAction
| AccountSetBalanceAction
| AccountSetNonceAction;
export type AccountFromStorageAction = {
type: typeof ACCOUNT.FROM_STORAGE,
payload: State
@ -40,6 +34,12 @@ export type AccountSetNonceAction = {
nonce: number
}
export type AccountAction =
AccountFromStorageAction
| AccountCreateAction
| AccountSetBalanceAction
| AccountSetNonceAction;
export const setBalance = (address: string, network: string, deviceState: string, balance: string): Action => ({
type: ACCOUNT.SET_BALANCE,
address,
@ -54,4 +54,4 @@ export const setNonce = (address: string, network: string, deviceState: string,
network,
deviceState,
nonce,
});
});

@ -1,11 +1,9 @@
/* @flow */
import TrezorConnect from 'trezor-connect';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import * as DISCOVERY from 'actions/constants/discovery';
import * as ACCOUNT from 'actions/constants/account';
import * as TOKEN from 'actions/constants/token';
import * as NOTIFICATION from 'actions/constants/notification';
import type {
ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice,
@ -13,18 +11,9 @@ import type {
import type { Discovery, State } from 'reducers/DiscoveryReducer';
import * as AccountsActions from './AccountsActions';
import { getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions';
import { setBalance as setTokenBalance } from './TokenActions';
import { getNonceAsync, getBalanceAsync } from './Web3Actions';
export type DiscoveryAction = {
type: typeof DISCOVERY.FROM_STORAGE,
payload: State
} | DiscoveryStartAction
| DiscoveryWaitingAction
| DiscoveryStopAction
| DiscoveryCompleteAction;
export type DiscoveryStartAction = {
type: typeof DISCOVERY.START,
device: TrezorDevice,
@ -51,144 +40,29 @@ export type DiscoveryCompleteAction = {
network: string
}
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (!selected) {
// TODO: throw error
console.error('Start discovery: no selected device', device);
return;
} if (selected.path !== device.path) {
console.error('Start discovery: requested device is not selected', device, selected);
return;
} if (!selected.state) {
console.warn("Start discovery: Selected device wasn't authenticated yet...");
return;
} if (selected.connected && !selected.available) {
console.warn('Start discovery: Selected device is unavailable...');
return;
}
const web3 = getState().web3.find(w3 => w3.network === network);
if (!web3) {
console.error('Start discovery: Web3 does not exist', network);
return;
}
if (!web3.web3.currentProvider.isConnected()) {
console.error('Start discovery: Web3 is not connected', network);
dispatch({
type: DISCOVERY.WAITING_FOR_BACKEND,
device,
network,
});
return;
}
const discovery: State = getState().discovery;
const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network,
});
return;
}
if (!discoveryProcess) {
dispatch(begin(device, network));
} else if (discoveryProcess.completed && !ignoreCompleted) {
dispatch({
type: DISCOVERY.COMPLETE,
device,
network,
});
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
// discovery cycle was interrupted
// start from beginning
dispatch(begin(device, network));
} else {
dispatch(discoverAccount(device, discoveryProcess));
}
};
const begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.network === network);
if (!coinToDiscover) return;
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network,
});
// get xpub from TREZOR
const response = await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
path: coinToDiscover.bip44,
keepSession: true, // acquire and hold session
useEmptyPassphrase: !device.instance,
});
// handle TREZOR response error
if (!response.success) {
// TODO: check message
console.warn('DISCOVERY ERROR', response);
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Discovery error',
message: response.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, network));
},
},
],
},
});
return;
}
export type DiscoveryAction = {
type: typeof DISCOVERY.FROM_STORAGE,
payload: State
} | DiscoveryStartAction
| DiscoveryWaitingAction
| DiscoveryStopAction
| DiscoveryCompleteAction;
// check for interruption
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
if (discoveryProcess && discoveryProcess.interrupted) return;
const basePath: Array<number> = response.payload.path;
// Because start() is calling begin() and begin() is calling start() one of them must be declared first
// otherwise eslint will start complaining
let begin;
// send data to reducer
dispatch({
type: DISCOVERY.START,
network: coinToDiscover.network,
device,
publicKey: response.payload.publicKey,
chainCode: response.payload.chainCode,
basePath,
});
dispatch(start(device, network));
};
const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const completed: boolean = discoveryProcess.completed;
const { completed } = discoveryProcess;
discoveryProcess.completed = false;
const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`);
const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex);
const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex');
const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress);
const network = discoveryProcess.network;
const { network } = discoveryProcess;
// TODO: check if address was created before
@ -312,6 +186,135 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy
}
};
export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
if (!selected) {
// TODO: throw error
console.error('Start discovery: no selected device', device);
return;
} if (selected.path !== device.path) {
console.error('Start discovery: requested device is not selected', device, selected);
return;
} if (!selected.state) {
console.warn("Start discovery: Selected device wasn't authenticated yet...");
return;
} if (selected.connected && !selected.available) {
console.warn('Start discovery: Selected device is unavailable...');
return;
}
const web3 = getState().web3.find(w3 => w3.network === network);
if (!web3) {
console.error('Start discovery: Web3 does not exist', network);
return;
}
if (!web3.web3.currentProvider.isConnected()) {
console.error('Start discovery: Web3 is not connected', network);
dispatch({
type: DISCOVERY.WAITING_FOR_BACKEND,
device,
network,
});
return;
}
const { discovery }: { discovery: State } = getState();
const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network);
if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) {
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network,
});
return;
}
if (!discoveryProcess) {
dispatch(begin(device, network));
} else if (discoveryProcess.completed && !ignoreCompleted) {
dispatch({
type: DISCOVERY.COMPLETE,
device,
network,
});
} else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice) {
// discovery cycle was interrupted
// start from beginning
dispatch(begin(device, network));
} else {
dispatch(discoverAccount(device, discoveryProcess));
}
};
begin = (device: TrezorDevice, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.network === network);
if (!coinToDiscover) return;
dispatch({
type: DISCOVERY.WAITING_FOR_DEVICE,
device,
network,
});
// get xpub from TREZOR
const response = await TrezorConnect.getPublicKey({
device: {
path: device.path,
instance: device.instance,
state: device.state,
},
path: coinToDiscover.bip44,
keepSession: true, // acquire and hold session
useEmptyPassphrase: !device.instance,
});
// handle TREZOR response error
if (!response.success) {
// TODO: check message
console.warn('DISCOVERY ERROR', response);
dispatch({
type: NOTIFICATION.ADD,
payload: {
type: 'error',
title: 'Discovery error',
message: response.payload.error,
cancelable: true,
actions: [
{
label: 'Try again',
callback: () => {
dispatch(start(device, network));
},
},
],
},
});
return;
}
// check for interruption
const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network);
if (discoveryProcess && discoveryProcess.interrupted) return;
const basePath: Array<number> = response.payload.path;
// send data to reducer
dispatch({
type: DISCOVERY.START,
network: coinToDiscover.network,
device,
publicKey: response.payload.publicKey,
chainCode: response.payload.chainCode,
basePath,
});
dispatch(start(device, network));
};
export const restore = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;

@ -7,15 +7,15 @@ import * as TOKEN from 'actions/constants/token';
import * as DISCOVERY from 'actions/constants/discovery';
import * as STORAGE from 'actions/constants/localStorage';
import * as PENDING from 'actions/constants/pendingTx';
import { JSONRequest, httpRequest } from 'utils/networkUtils';
import { httpRequest } from 'utils/networkUtils';
import type {
ThunkAction, AsyncAction, GetState, Dispatch, TrezorDevice,
ThunkAction, AsyncAction, /* GetState, */ Dispatch,
} from 'flowtype';
import type { Config, Coin, TokensCollection } from 'reducers/LocalStorageReducer';
import AppConfigJSON from 'data/appConfig.json';
import Erc20AbiJSON from 'data/ERC20Abi.json';
import Erc20AbiJSON from 'public/data/ERC20Abi.json';
import AppConfigJSON from 'public/data/appConfig.json';
export type StorageAction = {
type: typeof STORAGE.READY,
@ -30,59 +30,66 @@ export type StorageAction = {
error: string,
};
export const loadData = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
// check if local storage is available
// let available: boolean = true;
// if (typeof window.localStorage === 'undefined') {
// available = false;
// } else {
// try {
// window.localStorage.setItem('ethereum_wallet', true);
// } catch (error) {
// available = false;
// }
// }
dispatch(loadTokensFromJSON());
export const get = (key: string): ?string => {
try {
return window.localStorage.getItem(key);
} catch (error) {
// available = false;
return null;
}
};
// const parseConfig = (json: JSON): Config => {
// if (json['coins']) {
// }
export function update(event: StorageEvent): AsyncAction {
return async (dispatch: Dispatch/* , getState: GetState */): Promise<void> => {
if (!event.newValue) return;
// for (let key in json) {
// if (key === 'coins') {
if (event.key === 'devices') {
// check if device was added/ removed
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
// }
// }
// if (newDevices.length !== myDevices.length) {
// const diff = myDevices.filter(d => newDevices.indexOf(d) < 0)
// console.warn("DEV LIST CHANGED!", newDevices.length, myDevices.length, diff)
// // check if difference is caused by local device which is not saved
// // or device which was saved in other tab
// }
// const coins: Array<Object> = json.coins || [];
// const diff = oldDevices.filter(d => newDevices.indexOf())
}
// if ("coins" in json){
// json.coins
// }
// if (!json.hasOwnProperty("fiatValueTickers")) throw new Error(`Property "fiatValueTickers" is missing in appConfig.json`);
// if (json.config && json.hasOwnProperty('coins') && Array.isArray(json.coins)) {
// json.coins.map(c => {
// return {
if (event.key === 'accounts') {
dispatch({
type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
// }
// })
// } else {
// throw new Error(`Property "coins" is missing in appConfig.json`);
// }
if (event.key === 'tokens') {
dispatch({
type: TOKEN.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
if (event.key === 'pending') {
dispatch({
type: PENDING.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
// return {
// coins: [],
// fiatValueTickers: []
// }
// }
if (event.key === 'discovery') {
dispatch({
type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
};
}
export function loadTokensFromJSON(): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
return async (dispatch: Dispatch): Promise<void> => {
if (typeof window.localStorage === 'undefined') return;
try {
@ -157,57 +164,58 @@ export function loadTokensFromJSON(): AsyncAction {
};
}
export function update(event: StorageEvent): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
if (!event.newValue) return;
export const loadData = (): ThunkAction => (dispatch: Dispatch): void => {
// check if local storage is available
// let available: boolean = true;
// if (typeof window.localStorage === 'undefined') {
// available = false;
// } else {
// try {
// window.localStorage.setItem('ethereum_wallet', true);
// } catch (error) {
// available = false;
// }
// }
if (event.key === 'devices') {
// check if device was added/ removed
// const newDevices: Array<TrezorDevice> = JSON.parse(event.newValue);
// const myDevices: Array<TrezorDevice> = getState().connect.devices.filter(d => d.features);
dispatch(loadTokensFromJSON());
};
// if (newDevices.length !== myDevices.length) {
// const diff = myDevices.filter(d => newDevices.indexOf(d) < 0)
// console.warn("DEV LIST CHANGED!", newDevices.length, myDevices.length, diff)
// // check if difference is caused by local device which is not saved
// // or device which was saved in other tab
// }
// const parseConfig = (json: JSON): Config => {
// const diff = oldDevices.filter(d => newDevices.indexOf())
}
// if (json['coins']) {
if (event.key === 'accounts') {
dispatch({
type: ACCOUNT.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
// }
if (event.key === 'tokens') {
dispatch({
type: TOKEN.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
// for (let key in json) {
// if (key === 'coins') {
if (event.key === 'pending') {
dispatch({
type: PENDING.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
// }
// }
if (event.key === 'discovery') {
dispatch({
type: DISCOVERY.FROM_STORAGE,
payload: JSON.parse(event.newValue),
});
}
};
}
// const coins: Array<Object> = json.coins || [];
// if ("coins" in json){
// json.coins
// }
// if (!json.hasOwnProperty("fiatValueTickers")) throw new Error(`Property "fiatValueTickers" is missing in appConfig.json`);
// if (json.config && json.hasOwnProperty('coins') && Array.isArray(json.coins)) {
// json.coins.map(c => {
// return {
// }
// })
// } else {
// throw new Error(`Property "coins" is missing in appConfig.json`);
// }
// return {
// coins: [],
// fiatValueTickers: []
// }
// }
export const save = (key: string, value: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
export const save = (key: string, value: string): ThunkAction => (): void => {
if (typeof window.localStorage !== 'undefined') {
try {
window.localStorage.setItem(key, value);
@ -216,13 +224,4 @@ export const save = (key: string, value: string): ThunkAction => (dispatch: Disp
console.error(`Local Storage ERROR: ${error}`);
}
}
};
export const get = (key: string): ?string => {
try {
return window.localStorage.getItem(key);
} catch (error) {
// available = false;
return null;
}
};

@ -1,6 +1,6 @@
/* @flow */
import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect';
import TrezorConnect, { UI } from 'trezor-connect';
import type { Device } from 'trezor-connect';
import * as MODAL from 'actions/constants/modal';
import * as CONNECT from 'actions/constants/TrezorConnect';
@ -24,14 +24,14 @@ export const onPinSubmit = (value: string): Action => {
};
};
export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const resp = await TrezorConnect.uiResponse({
type: UI.RECEIVE_PASSPHRASE,
payload: {
value: passphrase,
save: true,
},
});
export const onPassphraseSubmit = (/* passphrase: string */): AsyncAction => async (dispatch: Dispatch): Promise<void> => {
// const resp = await TrezorConnect.uiResponse({
// type: UI.RECEIVE_PASSPHRASE,
// payload: {
// value: passphrase,
// save: true,
// },
// });
dispatch({
type: MODAL.CLOSE,
@ -64,7 +64,7 @@ export const onCancel = (): Action => ({
type: MODAL.CLOSE,
});
export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch): void => {
dispatch(onCancel());
dispatch({

@ -28,7 +28,7 @@ export type ReceiveAction = {
type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS
}
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
const state: State = {
...initialState,
};

@ -9,19 +9,13 @@ import * as PENDING from 'actions/constants/pendingTx';
import * as stateUtils from 'reducers/utils';
import { initialState } from 'reducers/SelectedAccountReducer';
import type {
Coin,
TrezorDevice,
AsyncAction,
ThunkAction,
Action,
GetState,
Dispatch,
State,
} from 'flowtype';
import * as SessionStorageActions from './SessionStorageActions';
import * as SendFormActions from './SendFormActions';
@ -32,13 +26,14 @@ export type SelectedAccountAction = {
payload: $ElementType<State, 'selectedAccount'>
};
export const dispose = (): Action => ({
type: ACCOUNT.DISPOSE,
});
export const updateSelectedValues = (prevState: State, action: Action): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const locationChange: boolean = action.type === LOCATION_CHANGE;
const state: State = getState();
const location = state.router.location;
const prevLocation = prevState.router.location;
const needUpdate: boolean = false;
const { location } = state.router;
// reset form to default
if (action.type === SEND.TX_COMPLETE) {
@ -126,7 +121,3 @@ export const updateSelectedValues = (prevState: State, action: Action): AsyncAct
}
}
};
export const dispose = (): Action => ({
type: ACCOUNT.DISPOSE,
});

@ -5,37 +5,29 @@ import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsUnits from 'ethereumjs-units';
import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect';
import { push } from 'react-router-redux';
import BigNumber from 'bignumber.js';
import { strip } from 'utils/ethUtils';
import * as NOTIFICATION from 'actions/constants/notification';
import * as SEND from 'actions/constants/send';
import { initialState } from 'reducers/SendFormReducer';
import { findAccount } from 'reducers/AccountsReducer';
import { findToken } from 'reducers/TokensReducer';
import { findDevice } from 'reducers/utils';
import * as stateUtils from 'reducers/utils';
import { findDevice, getPendingAmount, getPendingNonce } from 'reducers/utils';
import type {
PendingTx,
Dispatch,
GetState,
Action,
ThunkAction,
AsyncAction,
RouterLocationState,
TrezorDevice,
} from 'flowtype';
import type { State as AccountState } from 'reducers/SelectedAccountReducer';
import type { Web3Instance } from 'reducers/Web3Reducer';
import type { Config, Coin } from 'reducers/LocalStorageReducer';
import type { Coin } from 'reducers/LocalStorageReducer';
import type { Token } from 'reducers/TokensReducer';
import type { State, FeeLevel } from 'reducers/SendFormReducer';
import type { Account } from 'reducers/AccountsReducer';
import type { Props } from 'views/Wallet/views/AccountSend/Container';
import * as SessionStorageActions from './SessionStorageActions';
import { estimateGas, getGasPrice, pushTx } from './Web3Actions';
import { estimateGas, pushTx } from './Web3Actions';
export type SendTxAction = {
type: typeof SEND.TX_COMPLETE,
@ -155,7 +147,6 @@ export const calculate = (prevProps: Props, props: Props) => {
} = props.selectedAccount;
if (!account) return;
const prevState = prevProps.sendForm;
const state = props.sendForm;
const isToken: boolean = state.currency !== state.networkSymbol;
@ -169,7 +160,7 @@ export const calculate = (prevProps: Props, props: Props) => {
if (state.setMax) {
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, isToken);
const pendingAmount: BigNumber = getPendingAmount(pending, state.currency, isToken);
if (isToken) {
const token: ?Token = findToken(tokens, account.address, state.currency, account.deviceState);
@ -277,7 +268,7 @@ export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState):
});
};
export const toggleAdvanced = (address: string): Action => ({
export const toggleAdvanced = (/* address: string */): Action => ({
type: SEND.TOGGLE_ADVANCED,
});
@ -286,7 +277,6 @@ const addressValidation = (): ThunkAction => (dispatch: Dispatch, getState: GetS
const {
account,
network,
tokens,
} = getState().selectedAccount;
if (!account || !network) return;
@ -310,7 +300,7 @@ const addressValidation = (): ThunkAction => (dispatch: Dispatch, getState: GetS
} else {
const otherNetworkAccount = savedAccounts[0];
const device: ?TrezorDevice = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState);
const coins = getState().localStorage.config.coins;
const { coins } = getState().localStorage.config;
const otherNetwork: ?Coin = coins.find(c => c.network === otherNetworkAccount.network);
if (device && otherNetwork) {
warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`;
@ -351,7 +341,7 @@ export const validation = (props: Props): void => {
if (state.untouched) return;
// valid address
if (state.touched.address) {
if (state.address.length < 1) {
/* if (state.address.length < 1) {
errors.address = 'Address is not set';
} else if (!EthereumjsUtil.isValidAddress(state.address)) {
errors.address = 'Address is not valid';
@ -363,6 +353,20 @@ export const validation = (props: Props): void => {
} else if (state.infos.address) {
infos.address = state.infos.address;
}
} */
/* eslint (no-lonely-if) */
if (state.address.length < 1) {
errors.address = 'Address is not set';
} else if (!EthereumjsUtil.isValidAddress(state.address)) {
errors.address = 'Address is not valid';
} else if (state.warnings.address) {
// address warning or info are set in addressValidation ThunkAction
// do not override this
warnings.address = state.warnings.address;
if (state.infos.address) {
infos.address = state.infos.address;
}
}
}
@ -376,7 +380,7 @@ export const validation = (props: Props): void => {
errors.amount = 'Amount is not a number';
} else {
let decimalRegExp: RegExp;
const pendingAmount: BigNumber = stateUtils.getPendingAmount(pending, state.currency, state.currency !== state.networkSymbol);
const pendingAmount: BigNumber = getPendingAmount(pending, state.currency, state.currency !== state.networkSymbol);
if (state.currency !== state.networkSymbol) {
const token = findToken(tokens, account.address, state.currency, account.deviceState);
@ -612,7 +616,7 @@ export const updateFeeLevels = (): ThunkAction => (dispatch: Dispatch, getState:
// update only gasPrice
currentState.selectedFeeLevel.gasPrice = currentState.recommendedGasPrice;
// leave gas limit as it was
gasLimit = currentState.gasLimit;
({ gasLimit } = currentState);
}
const feeLevels: Array<FeeLevel> = getFeeLevels(network.symbol, currentState.recommendedGasPrice, gasLimit, currentState.selectedFeeLevel);
@ -659,9 +663,8 @@ export const onGasPriceChange = (gasPrice: string): ThunkAction => (dispatch: Di
});
};
export const onGasLimitChange = (gasLimit: string, updateFeeLevels: boolean = false): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
export const onGasLimitChange = (gasLimit: string/* , shouldUpdateFeeLevels: boolean = false */): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const currentState: State = getState().sendForm;
const isToken: boolean = currentState.currency !== currentState.networkSymbol;
const touched = { ...currentState.touched };
touched.gasLimit = true;
@ -704,34 +707,6 @@ export const onNonceChange = (nonce: string): AsyncAction => async (dispatch: Di
});
};
export const onDataChange = (data: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const currentState: State = getState().sendForm;
const touched = { ...currentState.touched };
touched.data = true;
const state: State = {
...currentState,
calculatingGasLimit: true,
untouched: false,
touched,
data,
};
if (currentState.selectedFeeLevel.value !== 'Custom') {
const customLevel = currentState.feeLevels.find(f => f.value === 'Custom');
if (!customLevel) return;
state.selectedFeeLevel = customLevel;
}
dispatch({
type: SEND.DATA_CHANGE,
state,
});
dispatch(estimateGasPrice());
};
const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const {
web3,
@ -771,6 +746,33 @@ const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState:
}
};
export const onDataChange = (data: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const currentState: State = getState().sendForm;
const touched = { ...currentState.touched };
touched.data = true;
const state: State = {
...currentState,
calculatingGasLimit: true,
untouched: false,
touched,
data,
};
if (currentState.selectedFeeLevel.value !== 'Custom') {
const customLevel = currentState.feeLevels.find(f => f.value === 'Custom');
if (!customLevel) return;
state.selectedFeeLevel = customLevel;
}
dispatch({
type: SEND.DATA_CHANGE,
state,
});
dispatch(estimateGasPrice());
};
export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const {
account,
@ -806,7 +808,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge
txAddress = token.address;
}
const pendingNonce: number = stateUtils.getPendingNonce(pending);
const pendingNonce: number = getPendingNonce(pending);
const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce;
console.warn('NONCE', nonce, account.nonce, pendingNonce);

@ -1,17 +1,11 @@
/* @flow */
import EthereumjsUtil from 'ethereumjs-util';
import * as SUMMARY from 'actions/constants/summary';
import * as TOKEN from 'actions/constants/token';
import { resolveAfter } from 'utils/promiseUtils';
import { initialState } from 'reducers/SummaryReducer';
import type {
ThunkAction, AsyncAction, Action, GetState, Dispatch,
ThunkAction, Action, Dispatch,
} from 'flowtype';
import type { State } from 'reducers/SummaryReducer';
import type { Token } from 'reducers/TokensReducer';
export type SummaryAction = {
type: typeof SUMMARY.INIT,
@ -22,7 +16,7 @@ export type SummaryAction = {
type: typeof SUMMARY.DETAILS_TOGGLE
}
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
const state: State = {
...initialState,
};

@ -25,10 +25,6 @@ export type TokenAction = {
payload: State
}
type SelectOptions = {
options?: Array<NetworkToken>
}
// action from component <reactSelect>
export const load = (input: string, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<any> => {
@ -57,6 +53,20 @@ export const load = (input: string, network: string): AsyncAction => async (disp
//await resolveAfter(3000);
};
export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const newState: Array<Token> = [...getState().tokens];
const token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress);
if (token) {
token.loaded = true;
token.balance = balance;
}
dispatch({
type: TOKEN.SET_BALANCE,
payload: newState,
});
};
export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.find(w3 => w3.network === account.network);
if (!web3instance) return;
@ -82,20 +92,6 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => async
dispatch(setBalance(token.address, account.address, tokenBalance));
};
export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const newState: Array<Token> = [...getState().tokens];
const token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress);
if (token) {
token.loaded = true;
token.balance = balance;
}
dispatch({
type: TOKEN.SET_BALANCE,
payload: newState,
});
};
export const remove = (token: Token): Action => ({
type: TOKEN.REMOVE,
token,

@ -1,10 +1,7 @@
/* @flow */
import TrezorConnect, {
UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT,
DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT,
} from 'trezor-connect';
import * as TOKEN from 'actions/constants/token';
import * as CONNECT from 'actions/constants/TrezorConnect';
import * as NOTIFICATION from 'actions/constants/notification';
import * as WALLET from 'actions/constants/wallet';
@ -82,6 +79,13 @@ export type TrezorConnectAction = {
};
const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => devices.sort((a, b) => {
if (!a.ts || !b.ts) {
return -1;
}
return a.ts > b.ts ? -1 : 1;
});
export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// set listeners
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
@ -112,7 +116,7 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS
});
// $FlowIssue LOCAL not declared
window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.trezor.io/connect/';
// window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.trezor.io/connect/';
// window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://connect.trezor.io/5/';
//window.__TREZOR_CONNECT_SRC = 'https://sisyfos.trezor.io/connect/';
// window.__TREZOR_CONNECT_SRC = 'https://localhost:8088/';
@ -133,71 +137,6 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS
}
};
// called after backend was initialized
// set listeners for connect/disconnect
export const postInit = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const handleDeviceConnect = (device: Device) => {
dispatch(initConnectedDevice(device));
};
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
const { devices } = getState();
const { initialPathname, initialParams } = getState().wallet;
if (initialPathname) {
dispatch({
type: WALLET.SET_INITIAL_URL,
// pathname: null,
// params: null
});
}
if (devices.length > 0) {
const unacquired: ?TrezorDevice = devices.find(d => d.features);
if (unacquired) {
dispatch(onSelectDevice(unacquired));
} else {
const latest: Array<TrezorDevice> = sortDevices(devices);
const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
dispatch(onSelectDevice(firstConnected || latest[0]));
// TODO
if (initialParams) {
if (!initialParams.hasOwnProperty('network') && initialPathname !== getState().router.location.pathname) {
// dispatch( push(initialPathname) );
} else {
}
}
}
}
};
const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => devices.sort((a, b) => {
if (!a.ts || !b.ts) {
return -1;
}
return a.ts > b.ts ? -1 : 1;
});
export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const selected = getState().wallet.selectedDevice;
// if (!selected || (selected && selected.state)) {
dispatch(onSelectDevice(device));
// }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
// dispatch( onSelectDevice(device) );
// } else if (!selected) {
// dispatch( onSelectDevice(device) );
// }
};
// selection from Aside dropdown button
// after device_connect event
// or after acquiring device
@ -242,6 +181,62 @@ export const onSelectDevice = (device: TrezorDevice | Device): ThunkAction => (d
}
};
export const initConnectedDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch/* , getState: GetState */): void => {
// const selected = getState().wallet.selectedDevice;
// if (!selected || (selected && selected.state)) {
dispatch(onSelectDevice(device));
// }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) {
// dispatch( onSelectDevice(device) );
// } else if (!selected) {
// dispatch( onSelectDevice(device) );
// }
};
// called after backend was initialized
// set listeners for connect/disconnect
export const postInit = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const handleDeviceConnect = (device: Device) => {
dispatch(initConnectedDevice(device));
};
TrezorConnect.off(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.off(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT, handleDeviceConnect);
TrezorConnect.on(DEVICE.CONNECT_UNACQUIRED, handleDeviceConnect);
const { devices } = getState();
const { initialPathname, initialParams } = getState().wallet;
if (initialPathname) {
dispatch({
type: WALLET.SET_INITIAL_URL,
// pathname: null,
// params: null
});
}
if (devices.length > 0) {
const unacquired: ?TrezorDevice = devices.find(d => d.features);
if (unacquired) {
dispatch(onSelectDevice(unacquired));
} else {
const latest: Array<TrezorDevice> = sortDevices(devices);
const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
dispatch(onSelectDevice(firstConnected || latest[0]));
// TODO
if (initialParams) {
if (!initialParams.hasOwnProperty('network') && initialPathname !== getState().router.location.pathname) {
// dispatch( push(initialPathname) );
}
}
}
}
};
export const switchToFirstAvailableDevice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { devices } = getState();
if (devices.length > 0) {
@ -388,7 +383,7 @@ export function acquire(): AsyncAction {
};
}
export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch): void => {
if (device.features) {
const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`;
dispatch(push(`/device/${devUrl}/settings`));

@ -3,16 +3,10 @@
import { LOCATION_CHANGE } from 'react-router-redux';
import * as WALLET from 'actions/constants/wallet';
import * as CONNECT from 'actions/constants/TrezorConnect';
import * as stateUtils from 'reducers/utils';
import type
{
Account,
Coin,
Discovery,
Token,
Web3Instance,
Device,
TrezorDevice,
RouterLocationState,
@ -47,8 +41,8 @@ export type WalletAction = {
devices: Array<TrezorDevice>
}
export const init = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
const updateOnlineStatus = (event) => {
export const init = (): ThunkAction => (dispatch: Dispatch): void => {
const updateOnlineStatus = () => {
dispatch({
type: WALLET.ONLINE_STATUS,
online: navigator.onLine,
@ -72,7 +66,7 @@ export const toggleDeviceDropdown = (opened: boolean): WalletAction => ({
// all saved instances will be removed immediately inside DevicesReducer
// This method will clear leftovers associated with removed instances from reducers.
// (DiscoveryReducer, AccountReducer, TokensReducer)
export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => {
export const clearUnavailableDevicesData = (prevState: State, device: Device): ThunkAction => (dispatch: Dispatch): void => {
if (!device.features) return;
const affectedDevices = prevState.devices.filter(d => d.features && device.features

@ -1,23 +1,19 @@
/* @flow */
import Web3 from 'web3';
import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect';
import type { ContractFactory, EstimateGasOptions } from 'web3';
import type {
ContractFactory,
EstimateGasOptions,
TransactionStatus,
TransactionReceipt,
} from 'web3';
import type BigNumber from 'bignumber.js';
import type { TransactionStatus, TransactionReceipt } from 'web3';
import { strip } from 'utils/ethUtils';
import * as WEB3 from 'actions/constants/web3';
import * as PENDING from 'actions/constants/pendingTx';
import type {
Dispatch,
GetState,
Action,
AsyncAction,
} from 'flowtype';
@ -29,15 +25,6 @@ import type { NetworkToken } from 'reducers/LocalStorageReducer';
import * as TokenActions from './TokenActions';
import * as AccountsActions from './AccountsActions';
export type Web3Action = {
type: typeof WEB3.READY,
} | {
type: typeof WEB3.CREATE,
instance: Web3Instance
}
| Web3UpdateBlockAction
| Web3UpdateGasPriceAction;
export type Web3UpdateBlockAction = {
type: typeof WEB3.BLOCK_UPDATED,
network: string,
@ -50,6 +37,14 @@ export type Web3UpdateGasPriceAction = {
gasPrice: string
};
export type Web3Action = {
type: typeof WEB3.READY,
} | {
type: typeof WEB3.CREATE,
instance: Web3Instance
}
| Web3UpdateBlockAction
| Web3UpdateGasPriceAction;
export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
@ -64,7 +59,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
return;
}
const network = coin.network;
const { network } = coin;
const urls = coin.backends[0].urls;
let web3host: string = urls[0];
@ -150,7 +145,7 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
//const shh = instance.shh.newIdentity();
const latestBlockFilter = web3.eth.filter('latest');
// const latestBlockFilter = web3.eth.filter('latest');
const onBlockMined = async (error: ?Error, blockHash: ?string) => {
if (error) {
@ -186,16 +181,12 @@ export function init(instance: ?Web3, coinIndex: number = 0): AsyncAction {
}
const tokens = getState().tokens.filter(t => t.network === network);
for (const token of tokens) {
dispatch(getTokenBalance(token));
}
tokens.forEach(token => dispatch(getTokenBalance(token)));
dispatch(getGasPrice(network));
const pending = getState().pending.filter(p => p.network === network);
for (const tx of pending) {
dispatch(getTransactionReceipt(tx));
}
pending.forEach(pendingTx => dispatch(getTransactionReceipt(pendingTx)));
};
// latestBlockFilter.watch(onBlockMined);
@ -220,7 +211,7 @@ export function getGasPrice(network: string): AsyncAction {
const index: number = getState().web3.findIndex(w3 => w3.network === network);
const web3instance = getState().web3[index];
const web3 = web3instance.web3;
const { web3 } = web3instance;
web3.eth.getGasPrice((error, gasPrice) => {
if (!error) {
if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) {
@ -238,7 +229,7 @@ export function getGasPrice(network: string): AsyncAction {
export function getBalance(account: Account): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
const web3: Web3 = web3instance.web3;
const { web3 } = web3instance;
web3.eth.getBalance(account.address, (error: Error, balance: BigNumber) => {
if (!error) {
@ -261,7 +252,6 @@ export function getBalance(account: Account): AsyncAction {
export function getTokenBalance(token: Token): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === token.network)[0];
const web3 = web3instance.web3;
const contract = web3instance.erc20.at(token.address);
contract.balanceOf(token.ethAddress, (error: Error, balance: BigNumber) => {
@ -282,7 +272,7 @@ export function getTokenBalance(token: Token): AsyncAction {
export function getNonce(account: Account): AsyncAction {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0];
const web3 = web3instance.web3;
const { web3 } = web3instance;
web3.eth.getTransactionCount(account.address, (error: Error, result: number) => {
if (!error) {
@ -296,7 +286,7 @@ export function getNonce(account: Account): AsyncAction {
export const getTransactionReceipt = (tx: PendingTx): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0];
const web3 = web3instance.web3;
const { web3 } = web3instance;
web3.eth.getTransaction(tx.id, (error: Error, status: TransactionStatus) => {
if (!error && !status) {
@ -367,8 +357,8 @@ export const getNonceAsync = (web3: Web3, address: string): Promise<number> => n
});
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => new Promise((resolve, reject) => {
const contract = erc20.at(address, (error, res) => {
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => new Promise((resolve) => {
const contract = erc20.at(address, (error/* , res */) => {
// console.warn("callback", error, res)
});

@ -1,7 +1,6 @@
import React from 'react';
import styled, { css } from 'styled-components';
import PropTypes from 'prop-types';
import Icon from 'components/Icon';
import colors from 'config/colors';
import { TRANSITION } from 'config/variables';
@ -82,7 +81,7 @@ const Wrapper = styled.button`
left: 18px;
}
&:after {
&:after {
width: 2px;
height: 12px;
left: 23px;
@ -107,66 +106,47 @@ const Wrapper = styled.button`
`}
`;
const IconWrapper = styled.span`
${props => ((props.hasChildren && !props.isRight) ? 'margin: 0 8px 0 -4px;' : '')};
${props => ((props.hasChildren && props.isRight) ? 'margin: 0 -4px 0 8px;' : '')};
`;
const Button = ({
children,
className,
icon,
onClick = () => { },
onMouseEnter,
onMouseLeave,
onFocus,
isDisabled = false,
isWhite = false,
isWebUsb = false,
isTransparent = false,
hasIconRight = false,
}) => (
<Wrapper
className={className}
icon={icon}
onClick={onClick}
isDisabled={isDisabled}
isWhite={isWhite}
isWebUsb={isWebUsb}
isTransparent={isTransparent}
>
{hasIconRight && children}
{icon && (
<IconWrapper
hasChildren={!!children}
isRight={hasIconRight}
>
<Icon
icon={icon.type}
color={icon.color}
size={icon.size}
isActive={icon.isActive}
canAnimate={icon.canAnimate}
/>
</IconWrapper>
)}
{!hasIconRight && children}
</Wrapper>
);
}) => {
const newClassName = isWebUsb ? `${className} trezor-webusb-button` : className;
return (
<Wrapper
className={newClassName}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onFocus={onFocus}
isDisabled={isDisabled}
isWhite={isWhite}
isWebUsb={isWebUsb}
isTransparent={isTransparent}
>
{children}
</Wrapper>
);
};
Button.propTypes = {
children: PropTypes.element.isRequired,
children: PropTypes.node.isRequired,
className: PropTypes.string,
onClick: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onFocus: PropTypes.func,
isDisabled: PropTypes.bool,
isWhite: PropTypes.bool,
isWebUsb: PropTypes.bool,
isTransparent: PropTypes.bool,
icon: PropTypes.shape({
type: PropTypes.arrayOf(PropTypes.string).isRequired,
color: PropTypes.string,
size: PropTypes.number,
isActive: PropTypes.bool,
canAnimate: PropTypes.bool,
}),
hasIconRight: PropTypes.bool,
};
export default Button;

@ -31,7 +31,7 @@ const Footer = ({ toggle }) => (
<Wrapper>
<Copy>&copy; {getYear(new Date())}</Copy>
<StyledLink href="http://satoshilabs.com" target="_blank" rel="noreferrer noopener" isGreen>SatoshiLabs</StyledLink>
<StyledLink href="tos.pdf" target="_blank" rel="noreferrer noopener" isGreen>Terms</StyledLink>
<StyledLink href="/assets/tos.pdf" target="_blank" rel="noreferrer noopener" isGreen>Terms</StyledLink>
<StyledLink onClick={toggle} isGreen>Show Log</StyledLink>
</Wrapper>
);

@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import colors from 'config/colors';
import styled, { keyframes } from 'styled-components';
// TODO: make animation of icons better
@ -23,16 +24,25 @@ const rotate180down = keyframes`
const SvgWrapper = styled.svg`
animation: ${props => (props.canAnimate ? (props.isActive ? rotate180up : rotate180down) : null)} 0.2s linear 1 forwards;
:hover {
path {
fill: ${props => props.hoverColor || colors.TEXT_SECONDARY}
}
}
`;
const Path = styled.path``;
const Path = styled.path`
fill: ${props => props.color};
`;
const Icon = ({
icon,
size = 32,
color = 'black',
color = colors.TEXT_SECONDARY,
isActive,
canAnimate,
hoverColor,
className,
onMouseEnter,
onMouseLeave,
@ -42,6 +52,7 @@ const Icon = ({
<SvgWrapper
className={className}
canAnimate={canAnimate}
hoverColor={hoverColor}
isActive={isActive}
style={{
display: 'inline-block',
@ -59,7 +70,7 @@ const Icon = ({
<Path
key={path}
isActive={isActive}
style={{ fill: color }}
color={color}
d={path}
/>
))}
@ -68,6 +79,7 @@ const Icon = ({
Icon.propTypes = {
className: PropTypes.string,
hoverColor: PropTypes.string,
canAnimate: PropTypes.bool,
icon: PropTypes.arrayOf(PropTypes.string).isRequired,
size: PropTypes.number,

@ -84,7 +84,7 @@ const NotificationButton = ({
);
NotificationButton.propTypes = {
children: PropTypes.element.isRequired,
children: PropTypes.node.isRequired,
type: PropTypes.string.isRequired,
className: PropTypes.string,
onClick: PropTypes.func,

@ -3,14 +3,13 @@ import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import styled, { css } from 'styled-components';
import colors from 'config/colors';
import NotificationButton from 'components/buttons/NotificationButton';
import Icon from 'components/Icon';
import icons from 'config/icons';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
import * as NOTIFICATION from 'actions/constants/notification';
import * as NotificationActions from 'actions/NotificationActions';
import Loader from 'components/Loader';
import NotificationButton from './components/NotificationButton';
const Wrapper = styled.div`
position: relative;
@ -45,7 +44,6 @@ const Wrapper = styled.div`
const Body = styled.div`
display: flex;
margin-right: 40px;
flex: 1;
`;
const Title = styled.div`
@ -53,8 +51,6 @@ const Title = styled.div`
font-weight: ${FONT_WEIGHT.BIGGER};
`;
const ActionContent = styled.div``;
const CloseClick = styled.div`
position: absolute;
right: 0;
@ -81,7 +77,17 @@ const Texts = styled.div`
flex-direction: column;
`;
const AdditionalContent = styled.div``;
const AdditionalContent = styled.div`
flex: 1;
display: flex;
justify-content: flex-end;
align-items: flex-end;
`;
const ActionContent = styled.div`
justify-content: right;
align-items: flex-end;
`;
export const Notification = (props: NProps): React$Element<string> => {
const close: Function = typeof props.close === 'function' ? props.close : () => {}; // TODO: add default close action
@ -109,12 +115,12 @@ export const Notification = (props: NProps): React$Element<string> => {
};
return (
<Wrapper type={props.className}>
<Wrapper type={props.type}>
{props.loading && <Loader size={50} /> }
{props.cancelable && (
<CloseClick onClick={() => close()}>
<Icon
color={getIconColor(props.className)}
color={getIconColor(props.type)}
icon={icons.CLOSE}
size={20}
/>
@ -123,8 +129,8 @@ export const Notification = (props: NProps): React$Element<string> => {
<Body>
<MessageContent>
<StyledIcon
color={getIconColor(props.className)}
icon={icons[props.className.toUpperCase()]}
color={getIconColor(props.type)}
icon={icons[props.type.toUpperCase()]}
/>
<Texts>
<Title>{ props.title }</Title>
@ -142,7 +148,7 @@ export const Notification = (props: NProps): React$Element<string> => {
{props.actions.map(action => (
<NotificationButton
key={action.label}
type={props.className}
type={props.type}
text={action.label}
onClick={() => { close(); action.callback(); }}
>{action.label}
@ -157,10 +163,10 @@ export const Notification = (props: NProps): React$Element<string> => {
export const NotificationGroup = (props) => {
const { notifications, close } = props;
return notifications.map((n, i) => (
return notifications.map(n => (
<Notification
key={i}
className={n.type}
key={n.title}
type={n.type}
title={n.title}
message={n.message}
cancelable={n.cancelable}

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import ReactSelect from 'react-select';
import ReactAsyncSelect from 'react-select/lib/Async';
import colors from 'config/colors';
import { FONT_FAMILY } from 'config/variables';
const styles = isSearchable => ({
singleValue: base => ({
@ -12,6 +13,7 @@ const styles = isSearchable => ({
}),
control: (base, { isDisabled }) => ({
...base,
fontFamily: FONT_FAMILY.MONOSPACE,
minHeight: 'initial',
height: '100%',
borderRadius: '2px',
@ -43,6 +45,7 @@ const styles = isSearchable => ({
menuList: base => ({
...base,
padding: 0,
fontFamily: FONT_FAMILY.MONOSPACE,
boxShadow: 'none',
background: colors.WHITE,
borderLeft: `1px solid ${colors.DIVIDER}`,

@ -2,23 +2,29 @@ import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import colors from 'config/colors';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
import { FONT_SIZE, FONT_WEIGHT, FONT_FAMILY } from 'config/variables';
const Wrapper = styled.div`
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
`;
const disabledColor = colors.TEXT_PRIMARY;
const TextArea = styled.textarea`
width: 100%;
padding: 5px 10px;
padding: 6px 12px;
box-sizing: border-box;
min-height: 25px;
border: ${props => (props.isError ? `1px solid ${colors.ERROR_PRIMARY}` : `1px solid ${colors.TEXT_PRIMARY}`)};
border: ${props => (props.isError ? `1px solid ${colors.ERROR_PRIMARY}` : `1px solid ${colors.DIVIDER}`)};
border-radius: 2px;
resize: none;
outline: none;
background: transparent;
font-family: ${FONT_FAMILY.MONOSPACE};
color: ${colors.TEXT_PRIMARY};
background: ${colors.WHITE};
font-weight: ${FONT_WEIGHT.BASE};
font-size: ${FONT_SIZE.BASE};
white-space: pre-wrap; /* css-3 */
@ -73,34 +79,38 @@ const TextArea = styled.textarea`
opacity: 1;
}
}
`;
&:hover:not(:disabled),
&:focus:not(:disabled) {
border: 1px solid ${colors.TEXT_SECONDARY};
}
const TopLabel = styled.span`
padding-bottom: 4px;
color: ${colors.TEXT_SECONDARY};
`;
const Textarea = ({
className,
placeholder = '',
value = '',
value,
customStyle = {},
onFocus,
onBlur,
disabled,
isDisabled,
onChange,
isError,
topLabel,
}) => (
<Wrapper>
{topLabel && (
<TopLabel>{topLabel}</TopLabel>
)}
<TextArea
className={className}
disabled={disabled}
disabled={isDisabled}
style={customStyle}
onFocus={onFocus}
onBlur={onBlur}
value={value}
placeholder={placeholder}
onChange={e => onChange(e.target.value)}
onChange={onChange}
isError={isError}
/>
</Wrapper>
@ -115,7 +125,8 @@ Textarea.propTypes = {
customStyle: PropTypes.string,
placeholder: PropTypes.string,
value: PropTypes.string,
disabled: PropTypes.bool,
isDisabled: PropTypes.bool,
topLabel: PropTypes.node,
};
export default Textarea;

@ -0,0 +1,213 @@
import React, { Component } from 'react';
import RcTooltip from 'rc-tooltip';
import colors from 'config/colors';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { FONT_SIZE } from 'config/variables';
const TooltipContent = styled.div`
width: ${props => (props.isAside ? '260px' : '320px')};
font-size: ${FONT_SIZE.SMALLEST};
`;
const Wrapper = styled.div`
.rc-tooltip {
position: absolute;
z-index: 1070;
display: block;
background: red;
visibility: visible;
border: 1px solid ${colors.DIVIDER};
border-radius: 3px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.06);
}
.rc-tooltip-hidden {
display: none;
}
.rc-tooltip-inner {
padding: 8px 10px;
color: ${colors.TEXT_SECONDARY};
font-size: 12px;
line-height: 1.5;
text-align: left;
text-decoration: none;
background-color: ${colors.WHITE};
border-radius: 3px;
min-height: 34px;
border: 1px solid ${colors.WHITE};
}
.rc-tooltip-arrow,
.rc-tooltip-arrow-inner {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
.rc-tooltip-placement-top .rc-tooltip-arrow,
.rc-tooltip-placement-topLeft .rc-tooltip-arrow,
.rc-tooltip-placement-topRight .rc-tooltip-arrow {
bottom: -6px;
margin-left: -6px;
border-width: 6px 6px 0;
border-top-color: ${colors.DIVIDER};
}
.rc-tooltip-placement-top .rc-tooltip-arrow-inner,
.rc-tooltip-placement-topLeft .rc-tooltip-arrow-inner,
.rc-tooltip-placement-topRight .rc-tooltip-arrow-inner {
bottom: 2px;
margin-left: -6px;
border-width: 6px 6px 0;
border-top-color: ${colors.WHITE};
}
.rc-tooltip-placement-top .rc-tooltip-arrow {
left: 50%;
}
.rc-tooltip-placement-topLeft .rc-tooltip-arrow {
left: 15%;
}
.rc-tooltip-placement-topRight .rc-tooltip-arrow {
right: 15%;
}
.rc-tooltip-placement-right .rc-tooltip-arrow,
.rc-tooltip-placement-rightTop .rc-tooltip-arrow,
.rc-tooltip-placement-rightBottom .rc-tooltip-arrow {
left: -5px;
margin-top: -6px;
border-width: 6px 6px 6px 0;
border-right-color: ${colors.DIVIDER};
}
.rc-tooltip-placement-right .rc-tooltip-arrow-inner,
.rc-tooltip-placement-rightTop .rc-tooltip-arrow-inner,
.rc-tooltip-placement-rightBottom .rc-tooltip-arrow-inner {
left: 1px;
margin-top: -6px;
border-width: 6px 6px 6px 0;
border-right-color: ${colors.WHITE};
}
.rc-tooltip-placement-right .rc-tooltip-arrow {
top: 50%;
}
.rc-tooltip-placement-rightTop .rc-tooltip-arrow {
top: 15%;
margin-top: 0;
}
.rc-tooltip-placement-rightBottom .rc-tooltip-arrow {
bottom: 15%;
}
.rc-tooltip-placement-left .rc-tooltip-arrow,
.rc-tooltip-placement-leftTop .rc-tooltip-arrow,
.rc-tooltip-placement-leftBottom .rc-tooltip-arrow {
right: -5px;
margin-top: -6px;
border-width: 6px 0 6px 6px;
border-left-color: ${colors.DIVIDER};
}
.rc-tooltip-placement-left .rc-tooltip-arrow-inner,
.rc-tooltip-placement-leftTop .rc-tooltip-arrow-inner,
.rc-tooltip-placement-leftBottom .rc-tooltip-arrow-inner {
right: 1px;
margin-top: -6px;
border-width: 6px 0 6px 6px;
border-left-color: ${colors.WHITE};
}
.rc-tooltip-placement-left .rc-tooltip-arrow {
top: 50%;
}
.rc-tooltip-placement-leftTop .rc-tooltip-arrow {
top: 15%;
margin-top: 0;
}
.rc-tooltip-placement-leftBottom .rc-tooltip-arrow {
bottom: 15%;
}
.rc-tooltip-placement-bottom .rc-tooltip-arrow,
.rc-tooltip-placement-bottomLeft .rc-tooltip-arrow,
.rc-tooltip-placement-bottomRight .rc-tooltip-arrow {
top: -5px;
margin-left: -6px;
border-width: 0 6px 6px;
border-bottom-color: ${colors.DIVIDER};
}
.rc-tooltip-placement-bottom .rc-tooltip-arrow-inner,
.rc-tooltip-placement-bottomLeft .rc-tooltip-arrow-inner,
.rc-tooltip-placement-bottomRight .rc-tooltip-arrow-inner {
top: 1px;
margin-left: -6px;
border-width: 0 6px 6px;
border-bottom-color: ${colors.WHITE};
}
.rc-tooltip-placement-bottom .rc-tooltip-arrow {
left: 50%;
}
.rc-tooltip-placement-bottomLeft .rc-tooltip-arrow {
left: 15%;
}
.rc-tooltip-placement-bottomRight .rc-tooltip-arrow {
right: 15%;
}
`;
class Tooltip extends Component {
render() {
const {
className,
placement,
content,
children,
} = this.props;
return (
<Wrapper
className={className}
innerRef={(node) => { this.tooltipContainerRef = node; }}
>
<RcTooltip
getTooltipContainer={() => this.tooltipContainerRef}
arrowContent={<div className="rc-tooltip-arrow-inner" />}
placement={placement}
overlay={<TooltipContent>{content}</TooltipContent>}
>
{children}
</RcTooltip>
</Wrapper>
);
}
}
Tooltip.propTypes = {
className: PropTypes.string,
placement: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
]),
content: PropTypes.oneOfType([
PropTypes.element,
PropTypes.string,
]),
};
export default Tooltip;

@ -1,26 +0,0 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { FONT_SIZE } from 'config/variables';
const Wrapper = styled.div`
width: ${props => (props.isAside ? '260px' : '320px')};
font-size: ${FONT_SIZE.SMALLEST};
`;
const TooltipContent = ({
children, isAside = false,
}) => (
<Wrapper
isAside={isAside}
>
{children}
</Wrapper>
);
TooltipContent.propTypes = {
children: PropTypes.node,
isAside: PropTypes.bool,
};
export default TooltipContent;

@ -4,7 +4,12 @@ import styled, { css } from 'styled-components';
import colors from 'config/colors';
import ICONS from 'config/icons';
import Icon from 'components/Icon';
import { FONT_SIZE, FONT_WEIGHT, TRANSITION } from 'config/variables';
import {
FONT_SIZE,
FONT_WEIGHT,
TRANSITION,
FONT_FAMILY,
} from 'config/variables';
const Wrapper = styled.div`
width: 100%;
@ -23,7 +28,7 @@ const InputIconWrapper = styled.div`
display: inline-block;
`;
const InputLabel = styled.span`
const TopLabel = styled.span`
padding-bottom: 4px;
color: ${colors.TEXT_SECONDARY};
`;
@ -33,8 +38,9 @@ const StyledInput = styled.input`
padding: 6px 12px;
line-height: 1.42857143;
font-family: ${FONT_FAMILY.MONOSPACE};
font-size: ${FONT_SIZE.SMALL};
font-weight: ${FONT_WEIGHT.SMALLEST};
font-weight: ${FONT_WEIGHT.BASE};
color: ${colors.TEXT_PRIMARY};
border-radius: 2px;
@ -96,8 +102,8 @@ class Input extends Component {
<Wrapper
className={this.props.className}
>
{this.props.inputLabel && (
<InputLabel>{this.props.inputLabel}</InputLabel>
{this.props.topLabel && (
<TopLabel>{this.props.topLabel}</TopLabel>
)}
<InputWrapper>
<InputIconWrapper>
@ -147,7 +153,7 @@ Input.propTypes = {
onChange: PropTypes.func,
state: PropTypes.string,
bottomText: PropTypes.string,
inputLabel: PropTypes.string,
topLabel: PropTypes.node,
sideAddons: PropTypes.arrayOf(PropTypes.node),
isDisabled: PropTypes.bool,
};

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components';
import styled from 'styled-components';
import colors from 'config/colors';
import Icon from 'components/Icon';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';

@ -6,11 +6,10 @@ import styled from 'styled-components';
import Icon from 'components/Icon';
import colors from 'config/colors';
import icons from 'config/icons';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
import Link from 'components/Link';
import { findAccount } from 'reducers/AccountsReducer';
import type { Props } from './index';
import type { Props } from '../../index';
const StyledLink = styled(Link)`
position: absolute;
@ -38,15 +37,6 @@ const StyledButton = styled(Button)`
`;
class ConfirmUnverifiedAddress extends Component<Props> {
keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) {
event.preventDefault();
this.verifyAddress();
}
}
componentDidMount(): void {
this.keyboardHandler = this.keyboardHandler.bind(this);
window.addEventListener('keydown', this.keyboardHandler, false);
@ -56,6 +46,15 @@ class ConfirmUnverifiedAddress extends Component<Props> {
window.removeEventListener('keydown', this.keyboardHandler, false);
}
keyboardHandler: (event: KeyboardEvent) => void;
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) {
event.preventDefault();
this.verifyAddress();
}
}
verifyAddress() {
if (!this.props.modal.opened) return;
const {

@ -3,7 +3,7 @@ import React, { Component } from 'react';
import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
import Input from 'components/inputs/Input';
import { getDuplicateInstanceNumber } from 'reducers/utils';
import { FONT_SIZE } from 'config/variables';
@ -63,12 +63,12 @@ const ErrorMessage = styled.div`
`;
export default class DuplicateDevice extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
input: ?HTMLInputElement;
keyboardHandler: (event: KeyboardEvent) => void;
constructor(props: Props) {
super(props);
@ -85,13 +85,6 @@ export default class DuplicateDevice extends Component<Props, State> {
};
}
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13 && !this.state.isUsed) {
event.preventDefault();
this.submit();
}
}
componentDidMount(): void {
// one time autofocus
if (this.input) this.input.focus();
@ -115,6 +108,13 @@ export default class DuplicateDevice extends Component<Props, State> {
});
}
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13 && !this.state.isUsed) {
event.preventDefault();
this.submit();
}
}
submit() {
if (!this.props.modal.opened) return;
const extended: Object = { instanceName: this.state.instanceName, instance: this.state.instance };
@ -125,7 +125,7 @@ export default class DuplicateDevice extends Component<Props, State> {
if (!this.props.modal.opened) return null;
const { device } = this.props.modal;
const { onCancel, onDuplicateDevice } = this.props.modalActions;
const { onCancel } = this.props.modalActions;
const {
defaultName,
instanceName,

@ -2,7 +2,7 @@ import React, { Component } from 'react';
import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
const Wrapper = styled.div`
width: 360px;

@ -4,9 +4,9 @@ import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import Loader from 'components/Loader';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
import type { Props } from './index';
import type { Props } from '../../index';
type State = {
countdown: number;
@ -77,9 +77,9 @@ export default class RememberDevice extends Component<Props, State> {
this.props.modalActions.onForgetDevice(this.props.modal.device);
}
} else {
this.setState({
countdown: this.state.countdown - 1,
});
this.setState(previousState => ({
countdown: previousState.countdown - 1,
}));
}
};
@ -108,16 +108,16 @@ export default class RememberDevice extends Component<Props, State> {
render() {
if (!this.props.modal.opened) return null;
const { device, instances } = this.props.modal;
const { onForgetDevice, onRememberDevice } = this.props.modalActions;
const { onRememberDevice } = this.props.modalActions;
let label = device.label;
let { label } = device;
const devicePlural: string = instances && instances.length > 1 ? 'devices or to remember them' : 'device or to remember it';
if (instances && instances.length > 0) {
label = instances.map((instance, index) => {
let comma: string = '';
if (index > 0) comma = ', ';
return (
<span key={index}>{ comma }{ instance.instanceLabel }</span>
<span key={instance.instanceLabel}>{ comma }{ instance.instanceLabel }</span>
);
});
}

@ -9,8 +9,8 @@ import { CSSTransition } from 'react-transition-group';
import { UI } from 'trezor-connect';
import { default as ModalActions } from 'actions/ModalActions';
import { default as ReceiveActions } from 'actions/ReceiveActions';
import ModalActions from 'actions/ModalActions';
import ReceiveActions from 'actions/ReceiveActions';
import * as RECEIVE from 'actions/constants/receive';
import * as CONNECT from 'actions/constants/TrezorConnect';
@ -51,8 +51,6 @@ type DispatchProps = {
export type Props = StateProps & DispatchProps;
const duration = 300;
const Fade = ({ children, ...props }) => (
<CSSTransition
{...props}
@ -75,7 +73,7 @@ const ModalContainer = styled.div`
flex-direction: column;
align-items: center;
overflow: auto;
padding: 20px;
padding: 20px;
`;
const ModalWindow = styled.div`
@ -125,7 +123,8 @@ class Modal extends Component<Props> {
component = (<DuplicateDevice {...this.props} />);
break;
default: null;
default:
component = null;
}
let ch = null;
@ -145,7 +144,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): StateProps => ({
modal: state.modal,
accounts: state.accounts,
devices: state.devices,

@ -4,14 +4,13 @@ import raf from 'raf';
import colors from 'config/colors';
import P from 'components/Paragraph';
import { FONT_SIZE } from 'config/variables';
import { H2 } from 'components/Heading';
import Link from 'components/Link';
import Checkbox from 'components/Checkbox';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
import Input from 'components/inputs/Input';
import styled from 'styled-components';
import type { Props } from './index';
import type { Props } from '../../index';
const Wrapper = styled.div`
padding: 24px 48px;
@ -55,14 +54,6 @@ type State = {
}
export default class PinModal extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
passphraseInput: ?HTMLInputElement;
passphraseRevisionInput: ?HTMLInputElement;
constructor(props: Props) {
super(props);
@ -91,6 +82,15 @@ export default class PinModal extends Component<Props, State> {
};
}
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
passphraseInput: ?HTMLInputElement;
passphraseRevisionInput: ?HTMLInputElement;
keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) {
event.preventDefault();
@ -108,6 +108,7 @@ export default class PinModal extends Component<Props, State> {
}
}
componentDidMount(): void {
// one time autofocus
if (this.passphraseInput) this.passphraseInput.focus();
@ -124,15 +125,6 @@ export default class PinModal extends Component<Props, State> {
// };
}
componentWillUnmount(): void {
window.removeEventListener('keydown', this.keyboardHandler, false);
// this.passphraseInput.type = 'text';
// this.passphraseInput.style.display = 'none';
// this.passphraseRevisionInput.type = 'text';
// this.passphraseRevisionInput.style.display = 'none';
}
// we don't want to keep password inside "value" attribute,
// so we need to replace it thru javascript
componentDidUpdate() {
@ -164,21 +156,30 @@ export default class PinModal extends Component<Props, State> {
}
}
componentWillUnmount(): void {
window.removeEventListener('keydown', this.keyboardHandler, false);
// this.passphraseInput.type = 'text';
// this.passphraseInput.style.display = 'none';
// this.passphraseRevisionInput.type = 'text';
// this.passphraseRevisionInput.style.display = 'none';
}
onPassphraseChange = (input: string, value: string): void => {
// https://codepen.io/MiDri/pen/PGqvrO
// or
// https://github.com/zakangelle/react-password-mask/blob/master/src/index.js
if (input === 'passphrase') {
this.setState({
match: this.state.singleInput || this.state.passphraseRevision === value,
this.setState(previousState => ({
match: previousState.singleInput || previousState.passphraseRevision === value,
passphrase: value,
});
}));
} else {
this.setState({
match: this.state.passphrase === value,
this.setState(previousState => ({
match: previousState.passphrase === value,
passphraseRevision: value,
passphraseRevisionTouched: true,
});
}));
}
}
@ -307,7 +308,7 @@ export default class PinModal extends Component<Props, State> {
onBlur={() => this.onPassphraseBlur('revision')}
tabIndex="2"
/>
{!match && passphraseRevisionTouched && <PassphraseError className="error">Passphrases do not match</PassphraseError> }
{!match && passphraseRevisionTouched && <PassphraseError>Passphrases do not match</PassphraseError> }
</Row>
) }
<Row>

@ -3,7 +3,7 @@ import styled from 'styled-components';
import { H3 } from 'components/Heading';
import P from 'components/Paragraph';
import type { Props } from './index';
import type { Props } from '../../index';
const Wrapper = styled.div`
padding: 24px 48px;

@ -3,12 +3,11 @@ import P from 'components/Paragraph';
import { H2 } from 'components/Heading';
import React, { Component } from 'react';
import Link from 'components/Link';
import colors from 'config/colors';
import styled from 'styled-components';
import PinInput from 'components/inputs/PinInput';
import PinButton from 'components/buttons/PinButton';
import Button from 'components/buttons/Button';
import type { Props } from './index';
import Button from 'components/Button';
import PinButton from './components/PinButton';
import type { Props } from '../../index';
type State = {
pin: string;
@ -53,7 +52,7 @@ class Pin extends Component<Props, State> {
}
onPinAdd = (input: number): void => {
let pin: string = this.state.pin;
let { pin } = this.state;
if (pin.length < 9) {
pin += input;
this.setState({
@ -63,9 +62,9 @@ class Pin extends Component<Props, State> {
}
onPinBackspace = (): void => {
this.setState({
pin: this.state.pin.substring(0, this.state.pin.length - 1),
});
this.setState(previousState => ({
pin: previousState.pin.substring(0, previousState.pin.length - 1),
}));
}
keyboardHandler(event: KeyboardEvent): void {

@ -1,4 +1,14 @@
export default {
TOP: [
'M677.44 613.76c-3.255 1.423-7.047 2.252-11.033 2.252-0.284 0-0.566-0.004-0.848-0.013l0.041 0.001c-8.323-0.531-15.657-4.371-20.77-10.206l-0.030-0.034-93.44-109.44c-0.378-0.735-1.131-1.229-1.999-1.229-1.237 0-2.24 1.003-2.24 2.24 0 0.209 0.029 0.412 0.083 0.605l-0.004-0.016v233.28c0.102 0.987 0.16 2.132 0.16 3.291 0 18.733-15.187 33.92-33.92 33.92s-33.92-15.187-33.92-33.92c0-1.159 0.058-2.304 0.172-3.433l-0.012 0.142v-236.16c0.050-0.177 0.079-0.379 0.079-0.589 0-1.237-1.003-2.24-2.24-2.24-0.868 0-1.621 0.494-1.993 1.216l-0.006 0.013-88.32 104.32c-5.204 6.343-13.042 10.358-21.819 10.358-7.711 0-14.699-3.099-19.784-8.121l0.003 0.003c-6.16-5.845-9.993-14.090-9.993-23.231 0-8.17 3.062-15.625 8.101-21.28l-0.028 0.032 146.56-173.44c5.311-6.15 13.061-10.069 21.731-10.24h0.029c8.727 0.036 16.523 3.991 21.724 10.196l0.036 0.044 152 178.56c5.441 6.124 8.764 14.234 8.764 23.121 0 12.698-6.785 23.81-16.927 29.911l-0.157 0.088z',
'M329.28 292.8c-0.024-0.488-0.038-1.060-0.038-1.635 0-18.891 14.881-34.306 33.561-35.163l0.077-0.003h292.48c18.795 1.81 33.372 17.523 33.372 36.64s-14.577 34.83-33.222 36.628l-0.15 0.012h-292.48c-18.751-0.866-33.625-16.278-33.625-35.165 0-0.463 0.009-0.923 0.027-1.381l-0.002 0.066z',
],
EYE_CROSSED: [
'M768 456.64c-20.16-34.88-44.48-63.68-71.68-86.72l-64.64 64.64c0.64 4.16 0.96 8.64 0.96 12.8 0 60.16-51.84 108.8-115.84 108.8-2.24 0-4.16 0-6.4-0.32l-33.92 33.92c12.16 1.6 24 2.24 36.16 2.24 98.88 0 197.44-45.12 255.36-135.36zM348.8 646.080c-8.96 8.96-23.68 8.96-32.64 0l-3.52-3.52c-8.96-8.96-8.96-23.68 0-32.64l53.76-53.76c-43.52-22.4-81.6-56.32-110.4-100.8 83.84-130.56 226.88-177.6 348.48-137.28l57.6-57.6c9.28-9.28 23.68-9.28 32.96 0l3.2 3.2c8.96 8.96 8.96 23.68 0 32.64l-349.44 349.76zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 21.12 6.080 40.64 17.28 57.28l42.56-42.56c-1.92-5.76-2.56-12.16-1.92-18.88 2.88-30.080 30.72-52.16 62.72-49.92 2.24 0.32 4.8 0.64 7.040 0.96l42.56-42.56c-16.32-8.32-34.56-12.8-54.4-12.8z',
],
EYE: [
'M512.64 592c-99.2 0-198.4-45.76-256.64-136.64 128.64-200 394.56-203.84 512 1.28-57.92 90.24-156.48 135.36-255.36 135.36zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 60.16 51.52 109.12 115.84 109.12 64 0 115.84-48.64 115.84-108.8 0.32-60.16-51.52-108.8-115.84-108.8zM574.72 451.84c2.56-30.080-21.12-56.32-53.12-58.88-32-2.24-59.84 19.84-62.72 49.92-2.56 30.080 21.44 56.32 53.12 58.56 32 2.56 59.84-19.84 62.72-49.6z',
],
CHECKED: [
'M692.8 313.92l-1.92-1.92c-6.246-7.057-15.326-11.484-25.44-11.484s-19.194 4.427-25.409 11.448l-0.031 0.036-196.48 224-3.84 1.6-3.84-1.92-48.64-57.28c-7.010-7.905-17.193-12.862-28.533-12.862-21.031 0-38.080 17.049-38.080 38.080 0 7.495 2.165 14.485 5.905 20.377l-0.092-0.155 100.8 148.16c5.391 8.036 14.386 13.292 24.618 13.44h8.662c17.251-0.146 32.385-9.075 41.163-22.529l0.117-0.191 195.2-296.32c4.473-6.632 7.141-14.803 7.141-23.597 0-11.162-4.297-21.32-11.326-28.911l0.025 0.028z',
],

@ -14,6 +14,12 @@ export const FONT_WEIGHT = {
BIGGER: '600',
};
export const FONT_FAMILY = {
DEFAULT: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif',
MONOSPACE: '"Roboto Mono", Menlo, Monaco, Consolas, "Courier New", monospace',
MONOSPACE_NUMBERS: '"Roboto Zero", "Roboto Mono", Menlo, Monaco, Consolas, "Courier New", monospace',
};
export const ICON_SIZE = {
BASE: '20px',
};

@ -7,11 +7,10 @@ import type {
Middleware as ReduxMiddleware,
ThunkAction as ReduxThunkAction,
AsyncAction as ReduxAsyncAction,
ThunkDispatch as ReduxThunkDispatch,
PlainDispatch as ReduxPlainDispatch,
} from 'redux';
import type { Reducers, ReducersState } from 'reducers';
import type { ReducersState } from 'reducers';
// Actions
import type { SelectedAccountAction } from 'actions/SelectedAccountActions';

Binary file not shown.

@ -1,42 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="icon-unlocked" d="M381.088 483.648v89.696c0 72.16 53.696 130.656 119.904 130.656h35.104c66.208 0 119.904-58.496 119.904-130.656v-7.616h-69.312c-4.416 36.704-33.248 65.056-68.128 65.056-37.952 0-68.736-33.536-68.736-74.88v-72.288h221.184c18.208 0 32.992-15.552 32.992-34.72v-222.24c0-19.104-14.784-34.656-32.992-34.656h-318.016c-18.208 0-32.992 15.552-32.992 34.72v222.24c0 19.168 14.784 34.72 32.992 34.72h28.096z" />
<glyph unicode="&#xe901;" glyph-name="icon-locked" d="M671.008 483.648h-14.976v89.632c0 72.256-53.76 130.72-120.064 130.72h-35.136c-66.304 0-119.744-58.464-119.744-130.72v-89.632h-28.096c-18.336 0-32.992-15.424-32.992-34.688v-222.272c0-18.944 14.656-34.688 32.992-34.688h318.016c18.336 0 32.992 15.744 32.992 34.688v222.272c0 19.264-14.656 34.688-32.992 34.688zM449.824 555.936c0 41.44 30.56 74.848 68.736 74.848 34.816 0 63.552-33.728 68.128-70.336v-76.768h-136.864v72.256z" />
<glyph unicode="&#xe902;" glyph-name="icon-eject" d="M276 192h471.968c11.072 0 20.032 9.76 20.032 21.824v75.968c0 12.064-8.96 21.824-20 21.824h-472c-11.040 0-20-9.76-20-21.824v-75.968c0-12.064 8.96-21.824 20-21.824zM503.552 699.808l-231.232-288.128c-6.368-7.904-1.184-20.32 8.448-20.32h462.496c9.664 0 14.816 12.384 8.448 20.32l-231.232 288.128c-4.512 5.6-12.448 5.6-16.928 0z" />
<glyph unicode="&#xe903;" glyph-name="icon-refresh" d="M347.392 486.688c17.28 82.24 90.4 142.336 173.92 142.336 31.648 0 61.92-8.704 88.576-24.416 6.656-3.936 12.8-8.672 18.944-13.504l-68.832-68.736 192-33.056-32 198.272-38.4-42.016c-5.92 5.024-12.064 9.728-18.336 14.144-40.928 28.672-89.92 44.288-141.92 44.288-121.664 0-225.12-89.312-245.984-210.368l-3.36-20.896h72.672l2.72 13.952zM676.608 409.312c-17.28-82.24-90.4-142.336-173.92-142.336-31.648 0-61.92 8.704-88.576 24.416-6.624 3.936-12.8 8.672-18.944 13.504l68.832 68.736-192 33.056 32-198.272 38.4 42.016c5.92-5.024 12.032-9.696 18.336-14.144 40.928-28.672 89.92-44.288 141.952-44.288 121.664 0 225.12 89.312 245.984 210.368l3.328 20.864h-72.672l-2.72-13.92z" />
<glyph unicode="&#xe904;" glyph-name="icon-info" d="M693.024 629.056c-99.968 99.936-262.080 99.936-362.048 0s-99.968-262.112 0-362.080c99.968-100 262.144-99.936 362.048 0 99.968 99.904 99.968 262.176 0 362.080zM507.904 659.808c27.008 0 48.992-21.984 48.992-49.088 0-27.296-21.984-49.472-48.992-49.472-27.264 0-49.536 22.176-49.536 49.472 0 27.552 21.728 49.088 49.536 49.088zM586.656 299.2c0-10.304-4.96-15.328-15.264-15.328h-126.464c-10.304 0-15.328 5.024-15.328 15.328v32.256c0 10.304 5.024 15.264 15.328 15.264h23.36v136.064h-23.872c-10.304 0-15.264 5.024-15.264 15.328v32.224c0 10.304 4.96 15.264 15.264 15.264h88.288c10.304 0 15.264-4.96 15.264-15.264v-183.648h23.424c10.304 0 15.264-4.96 15.264-15.264v-32.224z" />
<glyph unicode="&#xe905;" glyph-name="icon-chat" d="M580.992 704h-137.984c-103.296 0-187.008-85.952-187.008-192 0-96.608 69.536-176.32 160-189.792v-130.208l128 128h36.992c103.296 0 187.008 85.952 187.008 192s-83.712 192-187.008 192z" />
<glyph unicode="&#xe906;" glyph-name="icon-skip" d="M512 704c-141.376 0-256-114.656-256-256 0-141.408 114.624-256 256-256s256 114.592 256 256c0 141.344-114.624 256-256 256zM529.056 328.544v68.256c-102.4 34.144-136.544 0-170.656-68.256 0 170.656 102.4 204.8 170.656 204.8v68.256l136.544-136.544-136.544-136.512z" />
<glyph unicode="&#xe907;" glyph-name="icon-cog" d="M739.552 497.856h-71.328c-4.256 13.664-10.208 26.56-17.472 38.56l47.264 47.424c11.2 11.008 11.2 29.056 0 40.192l-20.064 20.032c-11.136 11.104-29.152 11.040-40.192 0l-48.128-48.032c-12.992 7.392-27.072 13.152-42.080 16.992v62.496c0 15.68-12.672 28.48-28.448 28.48h-28.448c-15.68 0-28.416-12.8-28.416-28.48v-62.464c-16.352-4.128-31.68-10.656-45.728-19.2l-40.288 40.224c-11.072 11.040-29.184 11.104-40.288 0l-20.096-20.096c-11.104-11.072-10.976-29.152 0.064-40.288l40.992-40.992c-8.672-15.136-15.168-31.648-18.88-49.152h-53.504c-15.776 0-28.544-12.736-28.544-28.48v-28.416c0-15.68 12.768-28.416 28.544-28.416h57.152c5.184-17.152 12.992-32.928 23.008-47.328l-38.656-38.656c-11.136-11.136-11.136-29.216-0.064-40.288l20.064-20.096c11.2-11.040 29.248-11.040 40.32 0.032l43.232 43.2c14.528-7.232 30.336-12.48 46.944-15.2v-59.488c0-15.68 12.736-28.448 28.448-28.48h28.448c15.68 0.032 28.448 12.8 28.448 28.48v66.816c14.336 5.088 27.904 11.872 40.224 20.544l45.76-45.888c11.104-11.072 29.12-11.072 40.224 0l20.096 20.128c11.168 11.072 11.168 29.056-0.096 40.288l-50.144 50.24c6.144 12.512 10.944 25.792 13.92 39.904h67.776c15.744 0 28.448 12.672 28.48 28.448v28.448c-0.096 15.68-12.8 28.512-28.544 28.512zM504.928 376.928c-39.264 0-71.072 31.776-71.072 71.104 0 39.264 31.808 71.040 71.072 71.040 39.296 0 71.136-31.776 71.136-71.040 0-39.328-31.84-71.104-71.136-71.104z" />
<glyph unicode="&#xe908;" glyph-name="icon-warning" d="M795.616 224.992l-264.896 465.44c-10.272 18.080-27.168 18.080-37.504 0l-264.864-465.44c-10.272-18.176-1.696-32.992 19.040-32.992h529.184c20.8 0 29.376 14.816 19.040 32.992zM549.76 286.88c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v37.952c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-37.952zM549.76 400.736c0-10.464-8.48-18.976-18.912-18.976h-37.792c-10.336 0-18.912 8.512-18.912 18.976v113.856c0 10.464 8.576 18.976 18.912 18.976h37.792c10.4 0 18.912-8.544 18.912-18.976v-113.856z" />
<glyph unicode="&#xe909;" glyph-name="icon-arrow-down" d="M757.216 539.072l-219.616-237.44c-8.128-8.576-19.296-13.632-31.040-13.632-11.744 0.288-23.2 5.056-31.040 13.664l-208.768 227.040c-15.36 16.928-14.176 43.040 3.008 58.176 16.864 15.424 43.392 13.952 59.040-2.656l177.76-193.504 188.608 203.904c7.52 8 18.080 12.768 29.216 13.344 11.456 0.608 21.696-3.264 30.112-10.688 16.896-15.456 18.080-41.568 2.72-58.208z" />
<glyph unicode="&#xe90a;" glyph-name="icon-close" d="M754.816 270.080c17.6-17.6 17.6-46.72 0-64.64-8.96-8.64-20.48-13.44-32.64-13.44s-23.68 4.8-32.32 13.44l-177.888 177.92-177.888-177.92c-16.32-16.96-47.040-17.6-64.64 0-17.92 17.92-17.92 47.040 0 64.64l178.208 177.92-178.208 177.92c-17.92 17.92-17.92 46.72 0 64.64 17.28 17.28 47.36 17.28 64.64 0l177.888-177.92 177.888 177.92c17.92 17.92 47.040 17.92 64.96 0 17.6-17.92 17.6-46.72 0-64.64l-178.24-177.92 178.24-177.92z" />
<glyph unicode="&#xe90b;" glyph-name="icon-arrow-up" d="M757.216 356.928l-219.616 237.44c-8.128 8.576-19.296 13.632-31.040 13.632-11.744-0.288-23.2-5.056-31.040-13.664l-208.768-227.040c-15.36-16.928-14.176-43.040 3.008-58.176 16.864-15.424 43.392-13.952 59.040 2.656l177.76 193.504 188.608-203.904c7.52-8 18.080-12.768 29.216-13.344 11.456-0.608 21.696 3.264 30.112 10.688 16.896 15.456 18.080 41.568 2.72 58.208z" />
<glyph unicode="&#xe90c;" glyph-name="icon-arrow-right2" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" />
<glyph unicode="&#xe90d;" glyph-name="icon-plus" d="M768 448c0-22.080-17.92-40-40-40h-176v-176c0-22.080-17.92-40-40-40s-40 17.92-40 40v176h-176c-22.080 0-40 17.92-40 40s17.92 40 40 40h176v176c0 22.080 17.92 40 40 40s40-17.92 40-40v-176h176c22.080 0 40-17.92 40-40z" />
<glyph unicode="&#xe90e;" glyph-name="icon-arrow-right" d="M420.928 202.784l237.44 219.616c8.576 8.128 13.632 19.296 13.632 31.040-0.288 11.744-5.056 23.2-13.664 31.040l-227.040 208.768c-16.928 15.36-43.040 14.176-58.176-3.008-15.424-16.864-13.952-43.392 2.656-59.040l193.504-177.76-203.904-188.608c-8-7.52-12.768-18.080-13.344-29.216-0.608-11.456 3.264-21.696 10.688-30.112 15.456-16.896 41.568-18.080 58.208-2.72z" />
<glyph unicode="&#xe90f;" glyph-name="icon-help" d="M693.024 629.056c-99.968 99.936-262.080 99.936-362.048 0s-99.968-262.112 0-362.080c99.968-100 262.144-99.936 362.048 0 99.968 99.904 99.968 262.176 0 362.080zM501.216 242.048c-27.808 0-50.496 22.464-50.496 50.048 0 28.32 22.176 50.528 50.496 50.528 27.616 0 50.048-22.656 50.048-50.528 0.032-27.168-22.88-50.048-50.048-50.048zM536.416 417.536v-27.744c0-13.504-5.28-18.784-18.784-18.784h-36.224c-13.504 0-18.72 5.28-18.72 18.784v61.984c0 15.68 16.064 20.352 30.208 24.48 3.456 1.056 7.040 2.080 10.496 3.264 18.336 6.592 29.696 14.816 29.696 35.296 0 6.656 0 26.816-32.832 26.816-20.224 0-38.624-7.776-49.6-12.416-6.208-2.624-9.28-3.904-12.384-3.904-6.336 0-12.32 5.088-13.248 10.304l-12.608 32.96c-1.824 3.776-1.824 6.784-1.824 9.216 0 24.288 75.552 37.664 100.608 37.664 63.104 0 105.504-40.672 105.504-101.152 0.032-65.44-49.12-85.952-80.288-96.768z" />
<glyph unicode="&#xe910;" glyph-name="icon-dashboard" d="M768 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM480 672v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32zM768 384v-160c0-17.6-14.4-32-32-32h-160c-17.6 0-32 14.4-32 32v160c0 17.6 14.4 32 32 32h160c17.6 0 32-14.4 32-32z" />
<glyph unicode="&#xe911;" glyph-name="icon-eye-crossed" d="M768 456.64c-20.16-34.88-44.48-63.68-71.68-86.72l-64.64 64.64c0.64 4.16 0.96 8.64 0.96 12.8 0 60.16-51.84 108.8-115.84 108.8-2.24 0-4.16 0-6.4-0.32l-33.92 33.92c12.16 1.6 24 2.24 36.16 2.24 98.88 0 197.44-45.12 255.36-135.36zM348.8 646.080c-8.96 8.96-23.68 8.96-32.64 0l-3.52-3.52c-8.96-8.96-8.96-23.68 0-32.64l53.76-53.76c-43.52-22.4-81.6-56.32-110.4-100.8 83.84-130.56 226.88-177.6 348.48-137.28l57.6-57.6c9.28-9.28 23.68-9.28 32.96 0l3.2 3.2c8.96 8.96 8.96 23.68 0 32.64l-349.44 349.76zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 21.12 6.080 40.64 17.28 57.28l42.56-42.56c-1.92-5.76-2.56-12.16-1.92-18.88 2.88-30.080 30.72-52.16 62.72-49.92 2.24 0.32 4.8 0.64 7.040 0.96l42.56-42.56c-16.32-8.32-34.56-12.8-54.4-12.8z" />
<glyph unicode="&#xe912;" glyph-name="icon-T1" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.232 31.447 29.027 69.173 29.12 109.413v232.987c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-0.073 0-0.159-0.001-0.246-0.001-14.427 0-27.041 7.762-33.894 19.338l-0.1 0.183-34.88 59.84c-16.656 28.155-26.515 62.042-26.56 98.227v235.853c0.133 19.025 13.742 34.833 31.751 38.359l0.249 0.041h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.258-3.568 31.867-19.375 32-38.386v-0.014zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4z" />
<glyph unicode="&#xe913;" glyph-name="icon-T1-buttons" d="M603.2 694.4h-6.4c-25.494 5.341-54.79 8.398-84.8 8.398s-59.305-3.058-87.592-8.879l2.792 0.48h-6.72c-30.053-5.643-52.489-31.68-52.489-62.956 0-0.367 0.003-0.733 0.009-1.099l-0.001 0.055v-234.88c0.075-40.921 11.238-79.22 30.643-112.071l-0.563 1.031 35.2-60.48c11.655-19.297 32.515-32.001 56.342-32.001 0.105 0 0.209 0 0.314 0.001h44.144c0.359-0.007 0.783-0.011 1.208-0.011 23.569 0 44.162 12.74 55.269 31.709l0.164 0.302 36.16 64c18.152 31.468 28.933 69.175 29.12 109.385v233.015c0.005 0.293 0.008 0.639 0.008 0.986 0 31.391-22.599 57.503-52.416 62.954l-0.392 0.059zM629.76 396.8c-0.193-35.364-9.792-68.446-26.418-96.923l0.498 0.923-35.84-64c-6.868-11.865-19.463-19.742-33.906-19.84h-44.174c-14.469 0.112-27.111 7.827-34.139 19.343l-34.981 61.297c-16.687 28.041-26.553 61.827-26.56 97.918v234.882c0 19.072 13.676 34.95 31.757 38.362l0.243 0.038h6.72c24.050 5.126 51.682 8.062 80 8.062s55.949-2.936 82.608-8.519l-2.608 0.457h6.72c18.324-3.45 32-19.328 32-38.4v0zM422.4 606.080h179.2c3.535 0 6.4-2.865 6.4-6.4v-99.2c0-3.535-2.865-6.4-6.4-6.4h-179.2c-3.535 0-6.4 2.865-6.4 6.4v99.2c0 3.535 2.865 6.4 6.4 6.4zM531.2 469.76h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4zM422.4 468.8h70.4c3.535 0 6.4-2.865 6.4-6.4v-28.8c0-3.535-2.865-6.4-6.4-6.4h-70.4c-3.535 0-6.4 2.865-6.4 6.4v28.8c0 3.535 2.865 6.4 6.4 6.4z" />
<glyph unicode="&#xe914;" glyph-name="icon-T1-connect" d="M526.72 345.92h-29.44c-16.229 0.18-30.307 9.217-37.647 22.496l-0.113 0.224-23.36 41.92c-12.685 22.404-20.16 49.203-20.16 77.748 0 0.004 0 0.008 0 0.012v-0.001 164.16c-0.002 0.138-0.002 0.301-0.002 0.463 0 21.403 14.903 39.326 34.898 43.957l0.304 0.059h4.48c16.978 3.729 36.479 5.865 56.48 5.865s39.502-2.136 58.289-6.193l-1.809 0.327h4.48c20.14-4.81 34.883-22.655 34.883-43.943 0-0.189-0.001-0.377-0.003-0.565v0.029-163.52c0-0.064 0-0.139 0-0.214 0-28.079-7.233-54.468-19.937-77.407l0.417 0.821-24-43.84c-7.529-13.375-21.591-22.288-37.744-22.4h-0.016zM512 685.76c-0.097 0-0.212 0-0.327 0-18.693 0-36.923-1.981-54.492-5.745l1.699 0.305h-4.48c-12.188-2.865-21.122-13.645-21.122-26.512 0-0.129 0.001-0.258 0.003-0.387v0.020-164.48c0-0.094 0-0.204 0-0.315 0-25.018 6.525-48.512 17.966-68.873l-0.365 0.708 23.36-41.92c4.497-8.071 12.981-13.44 22.718-13.44 0.001 0 0.002 0 0.002 0h29.44c9.799 0.067 18.299 5.56 22.652 13.623l0.068 0.137 24 43.84c10.854 19.495 17.254 42.757 17.28 67.512v162.248c0.002 0.109 0.002 0.238 0.002 0.368 0 12.867-8.935 23.648-20.938 26.476l-0.184 0.037h-4.48c-15.736 3.739-33.906 6.053-52.557 6.396l-0.243 0.004zM452.48 635.52h119.040c2.474 0 4.48-2.006 4.48-4.48v-69.44c0-2.474-2.006-4.48-4.48-4.48h-119.040c-2.474 0-4.48 2.006-4.48 4.48v69.44c0 2.474 2.006 4.48 4.48 4.48zM540.48 303.68v-52.16h-18.24v-59.52h-19.84v59.52h-18.88v52.16h56.96z" />
<glyph unicode="&#xe915;" d="M551.36 178.24h-78.4c-0.225-0.006-0.489-0.010-0.754-0.010-10.242 0-19.207 5.468-24.135 13.643l-0.071 0.127-64 96c-29.426 43.619-46.99 97.353-47.040 155.187v233.613c0.36 22.875 18.988 41.281 41.915 41.281 0.114 0 0.228 0 0.342-0.001h263.342c0.097 0.001 0.211 0.001 0.325 0.001 22.927 0 41.555-18.406 41.915-41.248v-239.394c-0.289-59.994-19.423-115.463-51.779-160.855l0.579 0.855-60.48-86.080c-4.695-7.399-12.575-12.414-21.664-13.114l-0.096-0.006zM380.48 690.24c-0.1 0.003-0.217 0.004-0.334 0.004-7.552 0-13.716-5.946-14.064-13.413l-0.001-0.031v-233.92c0-0.010 0-0.023 0-0.036 0-52.093 15.777-100.502 42.813-140.708l-0.573 0.904 64-96h79.040l60.16 86.080c28.821 40.408 46.080 90.794 46.080 145.211 0 0.024 0 0.049 0 0.073v-0.004 238.4c-0.35 7.498-6.513 13.444-14.066 13.444-0.118 0-0.235-0.001-0.352-0.004h0.017z" />
<glyph unicode="&#xe916;" d="M407.040 624.32h209.92c4.595 0 8.32-3.725 8.32-8.32v-201.92c0-4.595-3.725-8.32-8.32-8.32h-209.92c-4.595 0-8.32 3.725-8.32 8.32v201.92c0 4.595 3.725 8.32 8.32 8.32z" />
<glyph unicode="&#xe917;" d="M539.52 352h-54.72c-6.578 0.052-12.387 3.298-15.96 8.261l-0.040 0.059-45.12 64c-20.029 28.726-32.002 64.366-32.002 102.802 0 0.309 0.001 0.617 0.002 0.925v-0.047 157.76c0.349 15.459 12.963 27.856 28.473 27.856 0.34 0 0.679-0.006 1.016-0.018l-0.049 0.001h184.32c0.288 0.010 0.627 0.016 0.967 0.016 15.51 0 28.124-12.398 28.473-27.824l0.001-0.032v-160c-0.074-40.641-13.522-78.128-36.176-108.308l0.336 0.468-42.24-57.28c-3.622-5.258-9.609-8.66-16.39-8.66-0.313 0-0.624 0.007-0.934 0.022l0.044-0.002zM419.84 694.4c-0.205 0.016-0.444 0.025-0.686 0.025-4.973 0-9.062-3.781-9.551-8.624l-0.003-0.040v-155.84c0.066-34.769 11.081-66.953 29.778-93.302l-0.338 0.502 45.12-64h56l42.24 57.28c19.839 26.468 31.828 59.817 32 95.96v160.040c-0.492 4.884-4.582 8.665-9.554 8.665-0.241 0-0.481-0.009-0.717-0.026l0.032 0.002z" />
<glyph unicode="&#xe918;" d="M438.4 650.88h146.88c3.181 0 5.76-2.579 5.76-5.76v-134.4c0-3.181-2.579-5.76-5.76-5.76h-146.88c-3.181 0-5.76 2.579-5.76 5.76v134.4c0 3.181 2.579 5.76 5.76 5.76z" />
<glyph unicode="&#xe919;" d="M541.12 300.48v-50.56h-18.56v-57.92h-20.48v57.92h-18.88v50.56h57.92z" />
<glyph unicode="&#xe91a;" glyph-name="icon-arrow-left" d="M603.072 202.784l-237.44 219.616c-8.576 8.128-13.632 19.296-13.632 31.040 0.288 11.744 5.056 23.2 13.664 31.040l227.040 208.768c16.928 15.36 43.040 14.176 58.176-3.008 15.424-16.864 13.952-43.392-2.656-59.040l-193.504-177.76 203.904-188.608c8-7.52 12.768-18.080 13.344-29.216 0.608-11.456-3.264-21.696-10.688-30.112-15.456-16.896-41.568-18.080-58.208-2.72z" />
<glyph unicode="&#xe91b;" glyph-name="icon-top" d="M677.44 346.24c-3.255-1.423-7.047-2.252-11.033-2.252-0.284 0-0.566 0.004-0.848 0.013l0.041-0.001c-8.323 0.531-15.657 4.371-20.77 10.206l-0.030 0.034-93.44 109.44c-0.378 0.735-1.131 1.229-1.999 1.229-1.237 0-2.24-1.003-2.24-2.24 0-0.209 0.029-0.412 0.083-0.605l-0.004 0.016v-233.28c0.102-0.987 0.16-2.132 0.16-3.291 0-18.733-15.187-33.92-33.92-33.92s-33.92 15.187-33.92 33.92c0 1.159 0.058 2.304 0.172 3.433l-0.012-0.142v236.16c0.050 0.177 0.079 0.379 0.079 0.589 0 1.237-1.003 2.24-2.24 2.24-0.868 0-1.621-0.494-1.993-1.216l-0.006-0.013-88.32-104.32c-5.204-6.343-13.042-10.358-21.819-10.358-7.711 0-14.699 3.099-19.784 8.121l0.003-0.003c-6.16 5.845-9.993 14.090-9.993 23.231 0 8.17 3.062 15.625 8.101 21.28l-0.028-0.032 146.56 173.44c5.311 6.15 13.061 10.069 21.731 10.24h0.029c8.727-0.036 16.523-3.991 21.724-10.196l0.036-0.044 152-178.56c5.441-6.124 8.764-14.234 8.764-23.121 0-12.698-6.785-23.81-16.927-29.911l-0.157-0.088zM329.28 667.2c-0.024 0.488-0.038 1.060-0.038 1.635 0 18.891 14.881 34.306 33.561 35.163l0.077 0.003h292.48c18.795-1.81 33.372-17.523 33.372-36.64s-14.577-34.83-33.222-36.628l-0.15-0.012h-292.48c-18.751 0.866-33.625 16.278-33.625 35.165 0 0.463 0.009 0.923 0.027 1.381l-0.002-0.066z" />
<glyph unicode="&#xe91c;" glyph-name="icon-check" d="M692.8 646.080l-1.92 1.92c-6.246 7.057-15.326 11.484-25.44 11.484s-19.194-4.427-25.409-11.448l-0.031-0.036-196.48-224-3.84-1.6-3.84 1.92-48.64 57.28c-7.010 7.905-17.193 12.862-28.533 12.862-21.031 0-38.080-17.049-38.080-38.080 0-7.495 2.165-14.485 5.905-20.377l-0.092 0.155 100.8-148.16c5.391-8.036 14.386-13.292 24.618-13.44h8.662c17.251 0.146 32.385 9.075 41.163 22.529l0.117 0.191 195.2 296.32c4.473 6.632 7.141 14.803 7.141 23.597 0 11.162-4.297 21.32-11.326 28.911l0.025-0.028z" />
<glyph unicode="&#xe91d;" glyph-name="icon-error" d="M693.12 629.12c-46.317 46.267-110.276 74.88-180.919 74.88-141.385 0-256-114.615-256-256s114.615-256 256-256c70.642 0 134.602 28.613 180.921 74.882l-0.002-0.002c46.387 46.337 75.081 110.377 75.081 181.12s-28.694 134.783-75.079 181.118l-0.002 0.002zM494.080 615.68h53.12c16 0 18.24-9.28 18.24-14.72v-10.24l-10.88-194.56c0-14.4-8-17.28-18.88-17.28h-28.16c-10.56 0-17.28 2.88-18.88 17.92l-10.88 193.92v10.56c-1.28 4.8 2.24 14.080 16.32 14.080zM521.28 242.24c-0.095-0.001-0.207-0.001-0.319-0.001-27.747 0-50.24 22.493-50.24 50.24s22.493 50.24 50.24 50.24c27.747 0 50.24-22.493 50.24-50.24 0-0.112 0-0.224-0.001-0.336v0.017c0 0 0-0.001 0-0.001 0-27.634-22.311-50.057-49.903-50.239h-0.017z" />
<glyph unicode="&#xe91e;" glyph-name="icon-eye" d="M512.64 592c-99.2 0-198.4-45.76-256.64-136.64 128.64-200 394.56-203.84 512 1.28-57.92 90.24-156.48 135.36-255.36 135.36zM516.8 338.56c-64-0.32-115.84 48.64-115.84 108.48-0.32 60.16 51.52 109.12 115.84 109.12 64 0 115.84-48.64 115.84-108.8 0.32-60.16-51.52-108.8-115.84-108.8zM574.72 451.84c2.56-30.080-21.12-56.32-53.12-58.88-32-2.24-59.84 19.84-62.72 49.92-2.56 30.080 21.44 56.32 53.12 58.56 32 2.56 59.84-19.84 62.72-49.6z" />
<glyph unicode="&#xe91f;" glyph-name="icon-back" d="M656.224 557.696l-66.848-66.176-66.848 66.176-50.144-49.6 66.912-66.176-66.912-66.176 50.176-49.632 66.848 66.176 66.848-66.176 50.112 49.632-66.816 66.176 66.816 66.176-50.144 49.6zM337.824 704h540.928c27.2 0 49.248-21.824 49.248-48.768v-414.464c0-26.944-22.048-48.768-49.248-48.768h-540.608c-13.856 0-27.072 5.792-36.416 15.936l-192.896 209.664c-17.248 18.752-17.088 47.488 0.352 66.048l192.576 204.8c9.344 9.92 22.4 15.552 36.064 15.552z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Binary file not shown.

@ -5,12 +5,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Ethereum Wallet | TREZOR</title>
<meta name="title" content="TrezorConnect" />
<meta name="description" content="TrezorConnect" />
<meta name="keywords" content="TrezorConnect" />
<meta name="author" content="" />
<link rel="icon" type="image/png" href="favicon.png">
<!--[if IE]><link rel="shortcut icon" href="favicon.ico"/><![endif]-->
<meta name="title" content="Trezor Wallet" />
<meta name="description" content="Trezor Wallet" />
<meta name="keywords" content="trezor wallet" />
<meta name="author" content="satoshilabs s.r.o." />
<meta name="viewport" content="width=1024, initial-scale=1">
</head>
<body>

@ -3,9 +3,8 @@ import React from 'react';
import { render } from 'react-dom';
import baseStyles from 'support/BaseStyles';
import { onBeforeUnload } from 'actions/WalletActions';
import 'styles/index.less';
import App from 'views/index';
import store from 'support/store';
import store from 'store';
const root: ?HTMLElement = document.getElementById('root');
if (root) {

@ -22,7 +22,7 @@ const mergeDevices = (current: TrezorDevice, upcoming: Device | TrezorDevice): T
// }
// }
let instanceLabel = current.instanceLabel;
let { instanceLabel } = current;
if (upcoming.label !== current.label) {
instanceLabel = upcoming.label;
if (typeof current.instance === 'number') {

@ -62,9 +62,10 @@ const closeNotification = (state: State, payload: any): State => {
export default function notification(state: State = initialState, action: Action): State {
switch (action.type) {
case DEVICE.DISCONNECT:
const path: string = action.device.path; // Flow warning
case DEVICE.DISCONNECT: {
const { path } = action.device; // Flow warning
return state.filter(entry => entry.devicePath !== path);
}
case NOTIFICATION.ADD:
return addNotification(state, action.payload);

@ -1,9 +1,6 @@
/* @flow */
import * as PENDING from 'actions/constants/pendingTx';
import * as SEND from 'actions/constants/send';
import * as WEB3 from 'actions/constants/web3';
import type { Action } from 'flowtype';
import type { SendTxAction } from 'actions/SendFormActions';

@ -2,11 +2,9 @@
import EthereumjsUnits from 'ethereumjs-units';
import BigNumber from 'bignumber.js';
import * as SEND from 'actions/constants/send';
import * as WEB3 from 'actions/constants/web3';
import * as ACCOUNT from 'actions/constants/account';
import * as WALLET from 'actions/constants/wallet';
import { getFeeLevels } from 'actions/SendFormActions';
@ -15,6 +13,12 @@ import type {
Web3UpdateGasPriceAction,
} from 'actions/Web3Actions';
export type FeeLevel = {
label: string;
gasPrice: string;
value: string;
}
export type State = {
+networkName: string;
+networkSymbol: string;
@ -45,12 +49,6 @@ export type State = {
sending: boolean;
}
export type FeeLevel = {
label: string;
gasPrice: string;
value: string;
}
export const initialState: State = {
networkName: '',

@ -1,9 +1,6 @@
/* @flow */
import { TRANSPORT, DEVICE, UI } from 'trezor-connect';
import { TRANSPORT, UI } from 'trezor-connect';
import * as CONNECT from 'actions/constants/TrezorConnect';
import * as WALLET from 'actions/constants/wallet';
import type { Action } from 'flowtype';

@ -4,7 +4,6 @@
import Web3 from 'web3';
import type { ContractFactory } from 'web3';
import * as STORAGE from 'actions/constants/localStorage';
import * as WEB3 from 'actions/constants/web3';
import type { Action } from 'flowtype';

@ -1,20 +1,8 @@
/* @flow */
import * as LogActions from 'actions/LogActions';
import * as STORAGE from 'actions/constants/localStorage';
import * as WALLET from 'actions/constants/wallet';
import BigNumber from 'bignumber.js';
import type {
Middleware,
MiddlewareAPI,
MiddlewareDispatch,
State,
Dispatch,
Action,
AsyncAction,
GetState,
Device,
TrezorDevice,
Account,
@ -29,7 +17,7 @@ export const getSelectedDevice = (state: State): ?TrezorDevice => {
const locationState = state.router.location.state;
if (!locationState.device) return undefined;
const instance: ?number = locationState.deviceInstance ? parseInt(locationState.deviceInstance) : undefined;
const instance: ?number = locationState.deviceInstance ? parseInt(locationState.deviceInstance, 10) : undefined;
return state.devices.find((d) => {
if (!d.features && d.path === locationState.device) {
return true;
@ -65,7 +53,7 @@ export const getDuplicateInstanceNumber = (devices: Array<TrezorDevice>, device:
}
return !b.instance || a.instance > b.instance ? 1 : -1;
});
// calculate new instance number
const instance: number = affectedDevices.reduce((inst, dev) => (dev.instance ? dev.instance + 1 : inst + 1), 0);
return instance;
@ -76,14 +64,14 @@ export const getSelectedAccount = (state: State): ?Account => {
const locationState = state.router.location.state;
if (!device || !locationState.network || !locationState.account) return null;
const index: number = parseInt(locationState.account);
const index: number = parseInt(locationState.account, 10);
return state.accounts.find(a => a.deviceState === device.state && a.index === index && a.network === locationState.network);
};
export const getSelectedNetwork = (state: State): ?Coin => {
const device = state.wallet.selectedDevice;
const coins = state.localStorage.config.coins;
const { coins } = state.localStorage.config;
const locationState = state.router.location.state;
if (!device || !locationState.network) return null;

@ -1,6 +1,6 @@
/* @flow */
import { JSONRequest, httpRequest } from 'utils/networkUtils';
import { httpRequest } from 'utils/networkUtils';
import { resolveAfter } from 'utils/promiseUtils';
import { READY } from 'actions/constants/localStorage';
@ -8,7 +8,6 @@ import type {
Middleware,
MiddlewareAPI,
MiddlewareDispatch,
State,
Dispatch,
Action,
AsyncAction,

@ -1,18 +1,13 @@
/* @flow */
import { DEVICE } from 'trezor-connect';
import { LOCATION_CHANGE } from 'react-router-redux';
import * as LocalStorageActions from 'actions/LocalStorageActions';
import * as WalletActions from 'actions/WalletActions';
// import * as WalletActions from 'actions/WalletActions';
import * as CONNECT from 'actions/constants/TrezorConnect';
import * as MODAL from 'actions/constants/modal';
import * as TOKEN from 'actions/constants/token';
import * as ACCOUNT from 'actions/constants/account';
import * as DISCOVERY from 'actions/constants/discovery';
import * as SEND from 'actions/constants/send';
import * as WEB3 from 'actions/constants/web3';
import * as PENDING from 'actions/constants/pendingTx';
import { findAccountTokens } from 'reducers/TokensReducer';
@ -20,14 +15,12 @@ import type {
Middleware,
MiddlewareAPI,
MiddlewareDispatch,
State,
Dispatch,
Action,
AsyncAction,
GetState,
TrezorDevice,
} from 'flowtype';
import type { TrezorDevice } from 'flowtype';
import type { Account } from 'reducers/AccountsReducer';
import type { Token } from 'reducers/TokensReducer';
import type { PendingTx } from 'reducers/PendingTxReducer';

@ -1,29 +1,23 @@
/* @flow */
import * as LogActions from 'actions/LogActions';
import * as STORAGE from 'actions/constants/localStorage';
import * as SEND from 'actions/constants/send';
import { OPEN, CLOSE, ADD } from 'actions/constants/log';
// import * as STORAGE from 'actions/constants/localStorage';
// import * as SEND from 'actions/constants/send';
// import { OPEN, CLOSE, ADD } from 'actions/constants/log';
import { TRANSPORT, DEVICE } from 'trezor-connect';
import type {
Middleware,
MiddlewareAPI,
MiddlewareDispatch,
State,
Dispatch,
Action,
AsyncAction,
GetState,
} from 'flowtype';
const exclude: Array<string> = [
ADD, OPEN, CLOSE,
STORAGE.READY,
SEND.TX_COMPLETE,
'web3__create',
];
// const exclude: Array<string> = [
// ADD, OPEN, CLOSE,
// STORAGE.READY,
// SEND.TX_COMPLETE,
// 'web3__create',
// ];
const include: Array<string> = [
TRANSPORT.START,

@ -1,8 +1,5 @@
/* @flow */
import { DEVICE } from 'trezor-connect';
import { LOCATION_CHANGE, push, replace } from 'react-router-redux';
import { LOCATION_CHANGE/* , replace */ } from 'react-router-redux';
import * as CONNECT from 'actions/constants/TrezorConnect';
import * as WALLET from 'actions/constants/wallet';
import * as NotificationActions from 'actions/NotificationActions';
@ -11,12 +8,7 @@ import type {
Middleware,
MiddlewareAPI,
MiddlewareDispatch,
State,
Dispatch,
Action,
ThunkAction,
AsyncAction,
GetState,
RouterLocationState,
TrezorDevice,
} from 'flowtype';
@ -51,7 +43,7 @@ const validation = (api: MiddlewareAPI, params: RouterLocationState): boolean =>
let device: ?TrezorDevice;
if (params.hasOwnProperty('deviceInstance')) {
device = devices.find(d => d.features && d.features.device_id === params.device && d.instance === parseInt(params.deviceInstance));
device = devices.find(d => d.features && d.features.device_id === params.device && d.instance === parseInt(params.deviceInstance, 10));
} else {
device = devices.find(d => d.path === params.device || (d.features && d.features.device_id === params.device));
}
@ -61,25 +53,20 @@ const validation = (api: MiddlewareAPI, params: RouterLocationState): boolean =>
if (params.hasOwnProperty('network')) {
const { config } = api.getState().localStorage;
const coin = config.coins.find(coin => coin.network === params.network);
const coin = config.coins.find(c => c.network === params.network);
if (!coin) return false;
if (!params.account) return false;
}
if (params.account) {
// if (params.account) {
}
// }
return true;
};
let __unloading: boolean = false;
const LandingURLS: Array<string> = [
'/',
'/bridge',
];
const RouterService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => (action: Action): Action => {
if (action.type === WALLET.ON_BEFORE_UNLOAD) {
__unloading = true;

@ -1,10 +1,8 @@
/* @flow */
import { push } from 'react-router-redux';
import TrezorConnect, {
TRANSPORT, DEVICE_EVENT, UI_EVENT, UI, DEVICE,
import {
TRANSPORT, DEVICE,
} from 'trezor-connect';
import * as TrezorConnectActions from 'actions/TrezorConnectActions';
import * as DiscoveryActions from 'actions/DiscoveryActions';
@ -13,25 +11,19 @@ import { init as initWeb3 } from 'actions/Web3Actions';
import * as WEB3 from 'actions/constants/web3';
import * as STORAGE from 'actions/constants/localStorage';
import * as CONNECT from 'actions/constants/TrezorConnect';
import * as NOTIFICATION from 'actions/constants/notification';
import * as MODAL from 'actions/constants/modal';
import type {
Middleware,
MiddlewareAPI,
MiddlewareDispatch,
State,
Dispatch,
Action,
AsyncAction,
GetState,
RouterLocationState,
} from 'flowtype';
const TrezorConnectService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => (action: Action): Action => {
const prevState: $ElementType<State, 'connect'> = api.getState().connect;
// const prevState: $ElementType<State, 'connect'> = api.getState().connect;
const prevModalState: $ElementType<State, 'modal'> = api.getState().modal;
const prevRouterState: $ElementType<State, 'router'> = api.getState().router;
// const prevRouterState: $ElementType<State, 'router'> = api.getState().router;
next(action);

@ -1,20 +1,18 @@
/* @flow */
import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware, push } from 'react-router-redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
// import createHistory from 'history/createBrowserHistory';
// import { useRouterHistory } from 'react-router';
import createHistory from 'history/createHashHistory';
import { createLogger } from 'redux-logger';
import reducers from 'reducers';
import services from 'services';
import Raven from 'raven-js';
import RavenMiddleware from 'redux-raven-middleware';
import type { Action, GetState, Store } from 'flowtype';
// import type { Action, GetState, Store } from 'flowtype';
export const history: History = createHistory({ queryKey: false });
@ -32,12 +30,12 @@ const middleware = [
let composedEnhancers: any;
if (process.env.NODE_ENV === 'development') {
const excludeLogger = (getState: GetState, action: Action): boolean => {
//'@@router/LOCATION_CHANGE'
const excluded: Array<string> = ['LOG_TO_EXCLUDE', 'log__add'];
const pass: Array<string> = excluded.filter(act => action.type === act);
return pass.length === 0;
};
// const excludeLogger = (getState: GetState, action: Action): boolean => {
// //'@@router/LOCATION_CHANGE'
// const excluded: Array<string> = ['LOG_TO_EXCLUDE', 'log__add'];
// const pass: Array<string> = excluded.filter(act => action.type === act);
// return pass.length === 0;
// };
composedEnhancers = compose(
applyMiddleware(...middleware, ...services),

@ -1,28 +0,0 @@
@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,58 +0,0 @@
article {
nav {
.account-tabs {
position: relative;
display: flex;
flex: 1;
align-items: center;
justify-content: space-between;
padding: 0px 28px; // 20px padding arround links
max-width: 600px;
a {
font-weight: 500;
font-size: 14px;
color: @color_text_secondary;
margin: 0px 4px;
padding: 20px;
.hover();
&.active,
&:hover {
color: @color_text_primary;
}
&:first-child {
margin-left: 0px;
}
&:last-child {
margin-right: 0px;
}
}
.indicator {
position: absolute;
bottom: -1px;
left: 0;
width: 100px;
height: 2px;
background: @color_green_primary;
transition: all 0.3s ease-in-out;
}
}
}
section {
flex: 1;
display: flex;
flex-direction: column;
background: @color_white;
p {
padding: 0px 48px;
color: @color_text_secondary;
}
}
}

@ -1,174 +0,0 @@
// custom Roboto with Zero without the thing inside, so it's more readable as number
// since 0 doesn't look too similar to 8
@font-face {
font-family: 'Roboto Zero';
src: url('../fonts/roboto/RobotoZero.eot') format('embedded-opentype'),
url('../fonts/roboto/RobotoZero.eot?#iefix') format('embedded-opentype'),
url('../fonts/roboto/RobotoZero.woff') format('woff'),
url('../fonts/roboto/RobotoZero.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
src: url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.eot') format('embedded-opentype'), /* IE9 Compat Modes */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.woff') format('woff'), /* Modern Browsers */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
}
@font-face {
font-family: 'icomoon';
src: url('../fonts/icomoon.eot') format('embedded-opentype'),
url('../fonts/icomoon.eot?#iefix') format('embedded-opentype'),
url('../fonts/icomoon.woff') format('woff'),
url('../fonts/icomoon.ttf') format('truetype'),
url('../fonts/icomoon.svg#icomoon') format('svg');
}
@font-default: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
@font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif;
@font-family-monospace: "Roboto Mono", Menlo, Monaco, Consolas, "Courier New", monospace;
@font-family-monospace-numbers: "Roboto Zero", "Roboto Mono", Menlo, Monaco, Consolas, "Courier New", monospace;
// ::selection,
// ::-moz-selection {
// background: @color_info_secondary;
// }
.icomoon-base() {
display: inline-block;
font-family: 'icomoon';
font-style: normal;
font-weight: normal;
line-height: 1;
font-size: 24px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
}
.icomoon-eject {
.icomoon-base();
content: "\e902";
}
.icomoon-refresh {
.icomoon-base();
content: "\e903";
}
.icomoon-info {
.icomoon-base();
content: "\e904";
}
.icomoon-chat {
.icomoon-base();
content: "\e905";
}
.icomoon-redirect {
.icomoon-base();
content: "\e906";
}
.icomoon-settings {
.icomoon-base();
content: "\e907";
}
.icomoon-warning {
.icomoon-base();
content: "\e908";
}
.icomoon-arrow-down {
.icomoon-base();
content: "\e909";
}
.icomoon-close {
.icomoon-base();
content: "\e90a";
}
.icomoon-arrow-up {
.icomoon-base();
content: "\e90b";
}
.icomoon-arrow-right2 {
.icomoon-base();
content: "\e90c";
}
.icomoon-plus {
.icomoon-base();
content: "\e90d";
}
.icomoon-arrow-right {
.icomoon-base();
content: "\e90e";
}
.icomoon-help {
.icomoon-base();
content: "\e90f";
}
.icomoon-eye-error {
.icomoon-base();
content: "\e911";
}
.icomoon-T1 {
.icomoon-base();
content: "\e912";
}
.icomoon-arrow-left {
.icomoon-base();
content: "\e91a";
}
.icomoon-setmax {
.icomoon-base();
content: "\e91b";
}
.icomoon-checked {
.icomoon-base();
content: "\e91c";
}
.icomoon-error {
.icomoon-base();
content: "\e91d";
}
.icomoon-eye {
.icomoon-base();
content: "\e91e";
}
.icomoon-back {
.icomoon-base();
content: "\e91f";
}
.icomoon-download {
.icomoon-base();
content: "\e91b";
transform: rotate(180deg)
}

@ -1,13 +0,0 @@
@import './fonts.less';
@import './colors.less';
@import './mixins.less';
@import './content.less';
@import './reactSelect.less';
@import './rcTooltip.less';
@import './send.less';
@import './receive.less';
@import './summary.less';
@import './inputs.less';

@ -1,300 +0,0 @@
input, textarea {
font-size: 14px;
font-weight: 300;
line-height: 1.42857143;
font-family: @font-family-monospace;
color: @color_text_primary;
background-color: @color_white;
border: 1px solid @color_divider;
border-radius: 2px;
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
padding: 6px 12px;
&:focus {
box-shadow: 0 1px 2px 0 rgba(169, 169, 169, 0.25);
}
&:disabled {
background: @color_gray_light;
color: @color_text_secondary;
}
}
input {
&.valid {
border-color: @color_success_primary;
&:focus {
box-shadow: 0 1px 4px 0 rgba(1, 183, 87, 0.25);
}
}
&.warning {
border-color: @color_warning_primary;
&:focus {
box-shadow: 0 1px 4px 0 rgba(235, 138, 0, 0.25);
}
}
&.not-valid {
border-color: @color_error_primary;
&:focus {
box-shadow: 0 1px 4px 0 rgba(255, 111, 109, 0.25);
}
}
}
a.button,
button {
padding: 12px 24px;
border-radius: 3px;
font-size: 14px;
font-weight: 300;
cursor: pointer;
background: @color_green_primary;
color: @color_white;
border: 0px;
.hover();
&:hover {
background: @color_green_secondary;
}
&:active {
background: @color_green_tertiary;
}
&:disabled {
pointer-events: none;
color: @color_text_secondary;
background: @color_gray_light;
}
&.blue {
background: transparent;
border: 1px solid @color_info_primary;
color: @color_info_primary;
padding: 12px 58px;
&:hover {
color: @color_white;
background: @color_info_primary;
}
}
&.white {
background: @color_white;
color: @color_text_secondary;
border: 1px solid @color_divider;
&:hover {
//color: @color_text_primary;
//border-color: @color_text_primary;
background: @color_divider;
}
&:active {
color: @color_text_primary;
background: @color_divider;
}
}
&.transparent {
background: transparent;
border: 0px;
color: @color_text_secondary;
.hover();
&:hover,
&:active {
color: @color_text_primary;
background: transparent;
}
}
}
// a copy of webusb.less from trezor-connect
.trezor-webusb-button {
position: relative;
padding: 12px 24px 12px 40px;
background: transparent;
color: @color_green_primary;
border: 1px solid @color_green_primary;
.hover();
&:before,
&:after {
content: '';
position: absolute;
background: @color_green_primary;
top: 0;
bottom: 0;
margin: auto;
.hover();
}
&:before {
width: 12px;
height: 2px;
left: 18px;
}
&:after {
width: 2px;
height: 12px;
left: 23px;
}
&:hover {
background: @color_green_primary;
color: @color_white;
&:before,
&:after {
background: @color_white;
}
}
iframe {
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
}
.custom-checkbox {
position: relative;
display: flex;
align-items: center;
cursor: pointer;
color: @color_text_secondary;
input {
position: absolute;
left: -9999px;
z-index: -1;
opacity: 0;
&:checked + .indicator:after {
background-color: @color_green_primary;
border-color: @color_green_primary;
}
&:disabled + .indicator:after {
background-color: @color_text_secondary;
}
}
.indicator {
position: relative;
height: 24px;
width: 24px;
margin-right: 12px;
&:after {
.icomoon-checked;
.hover();
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
color: @color_white;
background-color: @color_white;
border: 1px solid @color_divider;
border-radius: 2px;
}
&:hover {
border-color: @color_text_secondary;
}
}
&.radio {
.indicator {
&:after {
border-radius: 50%;
}
}
input:checked + .indicator:after {
content: '';
background: white;
border: 4px solid @color_green_primary;
}
}
&.align-left {
padding-left: 20px;
padding-right: 10px;
.indicator {
position: absolute;
left: 0;
top: 2px;
margin: 0;
&:after {
top: 0px;
}
}
}
}
a.green,
a.green:visited {
position: relative;
color: @color_green_primary;
.hover();
&:after {
content: '';
position: absolute;
width: 100%;
border-top: 1px solid @color_green_primary;
line-height: 1px;
left: 0px;
bottom: -1px;
transition: border-color 0.3s;
}
&:hover {
color: @color_green_secondary;
}
&:active {
color: @color_green_tertiary;
}
&:hover,
&:active {
&:after {
border-color: transparent;
}
}
}
a.gray,
a.gray:visited {
position: relative;
color: @color_text_secondary;
.hover();
&:after {
content: '';
position: absolute;
width: 100%;
border-top: 1px solid @color_text_secondary;
line-height: 1px;
left: 0px;
bottom: -1px;
transition: border-color 0.3s;
}
&:hover,
&:active {
color: @color_text_primary;
&:after {
border-color: transparent;
}
}
}

@ -1,31 +0,0 @@
.antialised() {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.no-outlines() {
outline: none !important;
border-color: inherit !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.hover() {
transition: background-color 0.3s ease-in-out, color 0.3s ease-in-out, border-color 0.3s ease-in-out;
}
.placeholder(@rules) {
&::-webkit-input-placeholder {
@rules();
}
&:-moz-placeholder {
@rules();
}
&::-moz-placeholder {
@rules();
}
&:-ms-input-placeholder {
@rules();
}
}

@ -1,152 +0,0 @@
.tooltip-wrapper {
width: 320px;
font-size: 10px;
span {
color: @color_green_primary;
}
}
.aside-tooltip-wrapper {
width: 260px;
font-size: 10px;
}
.rc-tooltip {
position: absolute;
z-index: 1070;
display: block;
visibility: visible;
border: 1px solid @color_divider;
border-radius: 3px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.06);
}
.rc-tooltip-hidden {
display: none;
}
.rc-tooltip-inner {
padding: 8px 10px;
color: @color_text_secondary;
font-size: 12px;
line-height: 1.5;
text-align: left;
text-decoration: none;
background-color: @color_white;
border-radius: 3px;
min-height: 34px;
border: 1px solid @color_white;
}
.rc-tooltip-arrow,
.rc-tooltip-arrow-inner {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
.rc-tooltip-placement-top .rc-tooltip-arrow,
.rc-tooltip-placement-topLeft .rc-tooltip-arrow,
.rc-tooltip-placement-topRight .rc-tooltip-arrow {
bottom: -6px;
margin-left: -6px;
border-width: 6px 6px 0;
border-top-color: @color_divider;
}
.rc-tooltip-placement-top .rc-tooltip-arrow-inner,
.rc-tooltip-placement-topLeft .rc-tooltip-arrow-inner,
.rc-tooltip-placement-topRight .rc-tooltip-arrow-inner {
//bottom: 1px;
bottom: 2px;
margin-left: -6px;
border-width: 6px 6px 0;
border-top-color: @color_white;
}
.rc-tooltip-placement-top .rc-tooltip-arrow {
left: 50%;
}
.rc-tooltip-placement-topLeft .rc-tooltip-arrow {
left: 15%;
}
.rc-tooltip-placement-topRight .rc-tooltip-arrow {
right: 15%;
}
.rc-tooltip-placement-right .rc-tooltip-arrow,
.rc-tooltip-placement-rightTop .rc-tooltip-arrow,
.rc-tooltip-placement-rightBottom .rc-tooltip-arrow {
left: -5px;
margin-top: -6px;
border-width: 6px 6px 6px 0;
border-right-color: @color_divider;
}
.rc-tooltip-placement-right .rc-tooltip-arrow-inner,
.rc-tooltip-placement-rightTop .rc-tooltip-arrow-inner,
.rc-tooltip-placement-rightBottom .rc-tooltip-arrow-inner {
left: 1px;
margin-top: -6px;
border-width: 6px 6px 6px 0;
border-right-color: @color_white;
}
.rc-tooltip-placement-right .rc-tooltip-arrow {
top: 50%;
}
.rc-tooltip-placement-rightTop .rc-tooltip-arrow {
top: 15%;
margin-top: 0;
}
.rc-tooltip-placement-rightBottom .rc-tooltip-arrow {
bottom: 15%;
}
.rc-tooltip-placement-left .rc-tooltip-arrow,
.rc-tooltip-placement-leftTop .rc-tooltip-arrow,
.rc-tooltip-placement-leftBottom .rc-tooltip-arrow {
right: -5px;
margin-top: -6px;
border-width: 6px 0 6px 6px;
border-left-color: @color_divider;
}
.rc-tooltip-placement-left .rc-tooltip-arrow-inner,
.rc-tooltip-placement-leftTop .rc-tooltip-arrow-inner,
.rc-tooltip-placement-leftBottom .rc-tooltip-arrow-inner {
right: 1px;
margin-top: -6px;
border-width: 6px 0 6px 6px;
border-left-color: @color_white;
}
.rc-tooltip-placement-left .rc-tooltip-arrow {
top: 50%;
}
.rc-tooltip-placement-leftTop .rc-tooltip-arrow {
top: 15%;
margin-top: 0;
}
.rc-tooltip-placement-leftBottom .rc-tooltip-arrow {
bottom: 15%;
}
.rc-tooltip-placement-bottom .rc-tooltip-arrow,
.rc-tooltip-placement-bottomLeft .rc-tooltip-arrow,
.rc-tooltip-placement-bottomRight .rc-tooltip-arrow {
top: -5px;
margin-left: -6px;
border-width: 0 6px 6px;
border-bottom-color: @color_divider;
}
.rc-tooltip-placement-bottom .rc-tooltip-arrow-inner,
.rc-tooltip-placement-bottomLeft .rc-tooltip-arrow-inner,
.rc-tooltip-placement-bottomRight .rc-tooltip-arrow-inner {
top: 1px;
margin-left: -6px;
border-width: 0 6px 6px;
border-bottom-color: @color_white;
}
.rc-tooltip-placement-bottom .rc-tooltip-arrow {
left: 50%;
}
.rc-tooltip-placement-bottomLeft .rc-tooltip-arrow {
left: 15%;
}
.rc-tooltip-placement-bottomRight .rc-tooltip-arrow {
right: 15%;
}

@ -1,100 +0,0 @@
// https://github.com/JedWatson/react-select/blob/master/less/select.less
// @import '~react-select/less/select.less';
// // override predefined colors
// @select-primary-color: @color_white;
// @select-input-hover-box-shadow: none;
// @select-input-box-shadow-focus: transparent;
// @select-input-border-radius: 0px;
// @select-item-border-radius: 0px;
// @select-input-border-color: transparent;
// @select-input-border-focus: @color_divider;
// // @select-input-bg: transparent;
// // @select-input-bg-focus: transparent;
// .Select-focus-state(@color) {
// // do nothing
// background: transparent;
// box-shadow: none;
// }
// .Select-focus-state-classic() {
// background: transparent;
// box-shadow: none;
// }
// .Select-arrow-zone {
// position: absolute;
// top: 0;
// bottom: 0;
// margin: auto 0;
// padding-right: 0px !important;
// width: 24px;
// height: 24px;
// right: 8px;
// .Select-arrow {
// top: 0px;
// border: 0px;
// width: 24px;
// &:after {
// .icomoon-arrow-down;
// transition: transform 0.3s, color 0.3s;
// color: @color_text_secondary;
// transform-origin: 50% 50%;
// font-size: 24px;
// }
// }
// }
// .Select {
// .Select-control {
// cursor: pointer;
// .Select-input {
// background: transparent;
// position: absolute;
// top: 0;
// // display: none !important; // uncomment for disable auto closing
// }
// &:hover {
// .Select-arrow:after {
// color: @color_text_primary;
// }
// }
// }
// .Select-noresults {
// color: @color_text_secondary;
// font-family: @font-default;
// }
// .Select-placeholder {
// color: @color_text_secondary;
// font-family: @font-default;
// }
// .Select-value-label {
// color: @color_text_primary;
// }
// .Select-menu-outer {
// border-radius: 0px;
// border: 1px solid @color_divider;
// box-shadow: none;
// }
// &.is-open {
// .Select-arrow {
// top: 0px !important;
// border: 0px;
// &:after {
// transform: rotate(180deg);
// }
// }
// }
// }

@ -1,126 +0,0 @@
.receive {
.address {
position: relative;
padding: 0px 48px;
display: flex;
flex-wrap: wrap;
.verify {
position: absolute;
background: @color_white;
border: 2px solid @color_green_primary;
left: 0;
right: 0;
top: 0;
margin: 0px 48px;
z-index: 10001; // bigger than modal container
}
.value {
// same as input (inputs.less)
font-size: 14px;
font-weight: 300;
line-height: 1.42857143;
font-family: @font-family-monospace;
color: @color_text_primary;
border: 1px solid @color_divider;
border-radius: 3px;
padding: 6px 12px;
padding-right: 38px; // eye icon
position: relative;
flex: 1;
user-select: all; /* Chrome and Opera */
}
button {
padding: 6px 24px;
&.white {
padding: 0px;
border: 0px;
position: absolute;
height: 100%;
background: transparent;
right: 48px;
}
span {
display: flex;
align-items: center;
white-space: nowrap;
&:before {
.icomoon-eye;
font-size: 32px;
line-height: 14px;
// padding-top: 2px;
padding-right: 4px;
}
}
}
&.hidden {
.value {
padding-right: 6px; // no eye icon
user-select: none;
border-radius: 3px 0px 0px 3px;
&:after {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background: linear-gradient(to right,
rgba(255,255,255, 0) 0%,
rgba(255,255,255, 1) 220px
);
pointer-events: none; /* so the text is still selectable */
}
}
button {
border-radius: 0px 3px 3px 0px;
}
}
&.unverified {
button span:before {
.icomoon-eye-error;
color: @color_error_primary;
font-size: 32px;
line-height: 14px;
padding-top: 0px;
padding-right: 4px;
}
}
&.verifying {
.value {
background: @color_white;
z-index: 10001; // bigger than modal container
border-color: @color_green_primary;
border-width: 2px;
transform: translate(-1px, -1px);
}
.confirm {
display: block;
position: relative;
background: @color_white;
z-index: 10001;
width: 100%;
padding: 6px 12px;
transform: translate(-1px, -1px);
margin: 0px 2px;
}
}
}
.qr {
margin: 24px 48px;
}
}

@ -1,367 +0,0 @@
.send-form {
.Select {
width: 98px;
height: 34px;
font-family: @font-family-monospace;
&.fee {
width: 100%;
}
.Select-control {
height: 34px;
border: 1px solid @color_divider;
border-radius: 0px 2px 2px 0px;
}
.Select-option {
.hover();
&.is-focused {
background: @color_gray_light;
}
&.is-selected {
background: @color_divider;
}
}
&.is-disabled {
.Select-control {
cursor: default;
}
.Select-arrow {
visibility: hidden;
}
}
.fee-option {
display: flex;
align-items: center;
.fee-value {
flex: 1;
color: @color_text_primary;
}
.fee-label {
color: @color_text_secondary;
font-size: 12px;
font-weight: 400;
padding-right: 36px;
}
}
}
.row {
position: relative;
display: block;
padding: 0px 48px;
padding-bottom: 24px;
.error,
.warning:not(input),
.info {
position: absolute;
left: 48px;
bottom: 6px;
font-size: 12px;
color: @color_error_primary;
white-space: nowrap;
}
.error {
color: @color_error_primary;
}
.warning:not(input) {
color: @color_warning_primary;
}
.info {
color: @color_green_primary;
}
}
.input-icon {
position: absolute;
top: 0;
bottom: 0;
right: 54px;
margin: auto 0;
height: 26px;
color: @color_green_primary;
}
.address-input {
input.valid + .input-icon:before {
.icomoon-checked;
}
input.not-valid + .input-icon:before {
.icomoon-error;
color: @color_error_primary;
}
input.warning + .input-icon:before {
.icomoon-warning;
color: @color_warning_primary;
}
}
.amount-input {
position: relative;
display: flex;
flex-direction: row;
input {
flex: 1;
border-radius: 2px 0px 0px 2px;
}
.set-max {
position: relative;
height: 34px;
line-height: 34px;
font-size: 12px;
font-weight: 300; // different
color: @color_text_secondary;
border: 1px solid @color_divider;
border-right: 0px;
border-left: 0px;
background: @color_white;
padding: 0px 10px 0px 32px;
cursor: pointer;
.hover();
&:before {
.icomoon-setmax;
width: 24px;
height: 24px;
font-size: 24px;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
left: 4px;
}
&:hover {
background: @color_gray_light;
}
&:active {
background: @color_divider;
}
&.enabled {
color: @color_white;
background: @color_green_primary;
border-color: @color_green_primary;
&:before {
.icomoon-checked;
}
&:hover {
background: @color_green_secondary;
}
&:active {
background: @color_green_tertiary;
}
}
}
}
.advanced {
font-weight: 500;
line-height: 40px; // button height
color: @color_text_secondary;
.hover();
&:hover,
&:active {
color: @color_text_primary;
}
&:after {
.icomoon-arrow-down;
transition: transform 0.3s;
transform-origin: 50% 50%;
font-size: 24px;
position: relative;
top: 6px;
left: 8px;
}
}
.advanced-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0px 48px;
padding-bottom: 24px;
button {
min-width: 50%;
white-space: nowrap;
}
&.opened {
flex-direction: column;
padding: 0px;
button {
position: relative;
// left: 50%;
// width: 50%;
float: right; // TODO: better
}
.advanced {
display: inline-block;
margin: 0px 48px 12px 48px;
&:after {
transform: rotate(180deg);
top: 5px;
}
}
}
.what-is-it {
&:before {
.icomoon-help;
.hover();
transform-origin: 50% 50%;
font-size: 24px;
position: relative;
top: 5px;
cursor: pointer;
}
&:hover {
&:before {
color: @color_text_primary;
}
}
}
// @media screen and (max-width: 900px) {
// :not(.opened) {
// border: 1px solid red;
// }
// }
}
.gas-row {
display: flex;
flex-direction: row;
border-top: 1px solid @color_divider;
padding-top: 24px;
.column {
position: relative;
flex: 1;
padding-right: 20px;
&.nonce {
width: 100px;
flex: none;
}
&:last-child {
padding-right: 0px;
}
.error,
.warning,
.info {
left: 0;
bottom: -17px;
white-space: nowrap;
}
}
}
.update-fee-levels {
position: relative;
font-size: 12px;
color: @color_warning_primary;
padding-left: 24px;
margin-left: 8px;
a {
text-decoration: underline;
color: @color_green_primary;
margin-left: 4px;
}
&:before {
.icomoon-warning;
position: absolute;
top: -4px;
left: 0;
}
}
label {
display: block;
font-size: 14px;
color: @color_text_secondary;
padding-bottom: 4px;
}
input,
textarea {
width: 100%;
}
textarea {
resize: none;
height: 80px;
}
.pending-transactions {
border-top: 1px solid @color_divider;
.tx {
border-bottom: 1px solid @color_divider;
padding: 14px 48px;
display: flex;
flex-direction: row;
align-items: center;
&:last-child {
border-bottom: 0px;
}
.icon {
width: 36px;
height: 36px;
//border: 8px solid white;
border-radius: 50%;
margin-right: 10px;
line-height: 30px;
text-transform: uppercase;
user-select: none;
text-align: center;
padding: 6px;
p {
line-height: 24px;
padding: 0px;
color: inherit;
}
}
.name {
flex: 1;
a, a:visited {
color: @color_text_secondary;
.hover();
&:hover, &:active {
color: @color_text_primary;
}
}
}
.balance {
color: @color_text_primary;
}
}
}
}

@ -1,227 +0,0 @@
.summary {
.summary-header {
display: flex;
align-items: center;
&:before {
content: '';
display: inline-block;
width: 32px;
height: 22px;
background-repeat: no-repeat;
background-size: auto 20px;
}
&.ethereum:before {
background-image: url('../images/eth-logo.png');
}
&.ethereum-classic:before {
background-image: url('../images/etc-logo.png');
}
&.ropsten:before {
background-image: url('../images/ropsten-logo.png');
}
&.rinkeby:before {
background-image: url('../images/rinkeby-logo.png');
}
a {
font-size: 12px;
margin-left: auto;
white-space: nowrap;
}
}
.token-select {
width: 100%;
height: 34px;
font-family: @font-family-monospace;
.Select-control {
height: 34px;
border: 1px solid @color_divider;
}
.Select-input {
}
.Select-arrow-zone {
display: none;
}
}
.identicon {
display: inline-block;
vertical-align: middle;
position: relative;
top: -4px;
margin-right: 10px;
border-radius: 50%;
}
.summary-details {
position: relative;
padding: 0px 48px;
border-bottom: 1px solid @color_divider;
.content {
display: flex;
padding-bottom: 32px;
.column {
margin-right: 48px;
.label, .value {
color: @color_text_secondary;
font-size: 12px;
}
.fiat-value {
font-weight: 500;
font-size: 18px;
margin: 7px 0px 7px 0px;
color: @color_text_primary;
}
}
}
.toggle {
display: block;
position: absolute;
left: 50%;
margin-left: -20px;
bottom: -20px;
width: 40px;
height: 40px;
//line-height: 30px;
background: @color_white;
color: @color_text_secondary;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04);
border-radius: 50%;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: @color_divider;
color: @color_text_primary;
}
&:before {
.icomoon-arrow-down;
transition: transform 0.3s;
transform-origin: 50% 50%;
position: absolute;
left: 8px;
top: 10px;
}
}
&.opened {
.toggle:before {
transform: rotate(180deg);
top: 8px;
}
}
}
.what-is-it {
&:before {
.icomoon-help;
.hover();
transform-origin: 50% 50%;
font-size: 24px;
position: relative;
top: 5px;
cursor: pointer;
color: @color_text_secondary;
}
&:hover {
&:before {
color: @color_text_primary;
}
}
}
.filter {
//background: @color_main;
padding: 0px 48px 32px 48px;
// text-align: right;
input {
box-shadow: none;
}
}
.token-select {
.Select-control {
cursor: text;
}
.Select-option {
.hover();
&.is-focused {
background: @color_gray_light;
}
&.is-selected {
background: @color_divider;
}
}
}
.token {
border-top: 1px solid @color_divider;
padding: 14px 48px;
display: flex;
flex-direction: row;
align-items: center;
position: relative;
.icon {
width: 36px;
height: 36px;
//border: 8px solid white;
border-radius: 50%;
margin-right: 10px;
line-height: 30px;
text-transform: uppercase;
user-select: none;
text-align: center;
padding: 6px;
p {
line-height: 24px;
padding: 0px;
color: inherit;
}
}
.name {
flex: 1;
color: @color_text_secondary;
}
button {
position: absolute;
right: 12px;
padding: 0px;
padding-top: 3px;
&:after {
.icomoon-close;
}
}
&:last-child {
// border-bottom: 1px solid @color_divider;
}
}
}

@ -1,243 +0,0 @@
nav {
display: flex;
width: 100%;
max-width: 1170px;
height: 64px;
margin: 0 auto;
margin-top: 32px;
z-index: 1;
background: @color_white;
border-radius: 4px 4px 0px 0px;
border-bottom: 1px solid @color_divider;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04);
@media screen and (max-width: 1170px) {
border-radius: 0px;
margin-top: 0px;
}
// .layout-wrapper {
// height: 100%;
// background: @color_white;
// border-radius: 4px 4px 0px 0px;
// border-bottom: 1px solid rgba(218, 218, 218, 0.5);
// box-shadow: 0 3px 8px rgba(0, 0, 0, 0.04);
// }
// override styles for react-select
.device-select {
width: 320px;
height: 64px;
// display: inline-block;
// vertical-align: middle;
box-shadow: none;
&.is-focused:not(.is-open) > .Select-control {
border-color: @color_divider;
box-shadow: none;
}
.Select-control {
height: 63px;
border: 0px;
border-radius: 4px 0px 0px 0px;
border-right: 1px solid @color_divider;
cursor: pointer;
transition: color 0.2s ease-in-out;
.Select-input {
background: transparent;
position: absolute;
top: 0;
//display: none !important;
}
&:hover {
background: transparent;
// border: 0px;
border-right: 1px solid @color_divider;
box-shadow: none;
.Select-arrow {
&:after {
color: @color_text_primary;
}
}
}
}
.Select-arrow-zone {
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
padding: 0px;
width: 24px;
height: 24px;
right: 24px;
.Select-arrow {
top: 0px;
border: 0px;
width: 24px;
&:after {
.icomoon-arrow-down;
transition: transform 0.3s;
color: @color_text_secondary;
transform-origin: 50% 50%;
font-size: 24px;
}
}
}
.Select-option {
&:hover {
background: red;
}
&.is-selected {
background: yellow;
}
}
&.is-open {
.Select-control {
border-color: @color_divider;
}
.Select-arrow {
top: 0px !important;
border: 0px;
&:after {
// .icomoon-arrow-up;
transform: rotate(180deg);
}
}
}
&.is-disabled {
.Select-control {
background: transparent;
cursor: default;
}
.Select-arrow {
visibility: hidden;
&:after {
content: ''
}
}
.device {
.device-menu {
padding-right: 24px;
}
}
}
.Select-menu-outer {
border-radius: 0px;
border: 1px solid @color_divider;
box-shadow: none;
visibility: hidden;
}
}
.device {
height: 63px;
width: 319px;
display: flex;
align-items: center;
padding-left: 80px;
&:before {
content: '';
position: absolute;
display: block;
width: 13px;
height: 25px;
z-index: 2;
left: 33px;
top: 17px;
background-repeat: no-repeat;
background-position: center;
background-size: 13px 25px;
background-image: url('../images/icontrezor.png');
}
.label-container {
flex: 1;
overflow: hidden;
span {
display: block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
&.label {
font-weight: 500;
font-size: 14px;
color: @color_text_primary;
}
&.status {
font-size: 12px;
color: @color_text_secondary;
}
}
}
.device-menu {
display: flex;
justify-content: flex-end;
padding-right: 48px;
padding-left: 4px;
div {
display: inline-block;
}
.forget,
.settings,
.acquire {
cursor: pointer;
&:before {
.icomoon-refresh;
color: @color_text_secondary;
position: relative;
font-size: 24px;
.hover();
}
&:hover {
&:before {
color: @color_text_primary;
}
}
}
.forget {
&:before {
.icomoon-eject;
}
}
.settings {
&:before {
.icomoon-settings;
}
}
}
}
}

@ -1,5 +1,4 @@
import { injectGlobal } from 'styled-components';
import normalize from 'styled-normalize';
const baseStyles = () => injectGlobal`
@ -40,23 +39,28 @@ const baseStyles = () => injectGlobal`
outline: 0;
}
/*
custom Roboto with Zero without the thing inside, so it's more readable as number
since 0 doesn't look too similar to 8
*/
@font-face {
font-family: 'Roboto Zero';
src: url('../fonts/roboto/RobotoZero.eot') format('embedded-opentype'),
url('../fonts/roboto/RobotoZero.eot?#iefix') format('embedded-opentype'),
url('../fonts/roboto/RobotoZero.woff') format('woff'),
url('../fonts/roboto/RobotoZero.ttf') format('truetype');
src: url('./fonts/roboto/RobotoZero.eot') format('embedded-opentype'),
url('./fonts/roboto/RobotoZero.eot?#iefix') format('embedded-opentype'),
url('./fonts/roboto/RobotoZero.woff') format('woff'),
url('./fonts/roboto/RobotoZero.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto Mono';
font-style: normal;
src: url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.eot') format('embedded-opentype'), /* IE9 Compat Modes */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.woff') format('woff'), /* Modern Browsers */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
src: url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.eot') format('embedded-opentype'), /* IE9 Compat Modes */
url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.woff') format('woff'), /* Modern Browsers */
url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.ttf') format('truetype'), /* Safari, Android, iOS */
url('./fonts/roboto/roboto-mono-v4-greek_cyrillic-ext_greek-ext_latin_cyrillic_vietnamese_latin-ext-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
}
`;

@ -1,4 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import RedBox from 'redbox-react';
class ErrorBoundary extends Component {
@ -7,7 +8,7 @@ class ErrorBoundary extends Component {
this.state = { hasError: false, error: false };
}
componentDidCatch(error, info) {
componentDidCatch(error) {
this.setState({ hasError: true, error });
}
@ -19,4 +20,8 @@ class ErrorBoundary extends Component {
}
}
ErrorBoundary.propTypes = {
children: PropTypes.node,
};
export default ErrorBoundary;

@ -0,0 +1,7 @@
import PropTypes from 'prop-types';
export const DEVICE_PROP_TYPES = PropTypes.shape({
id: PropTypes.number.isRequired,
text: PropTypes.string.isRequired,
author: authorPropType.isRequired,
});

@ -4,9 +4,9 @@ import BigNumber from 'bignumber.js';
export const decimalToHex = (dec: number): string => new BigNumber(dec).toString(16);
export const hexToDecimal = (hex: number): string => {
const sanitized: ?string = sanitizeHex(hex);
return !sanitized ? 'null' : new BigNumber(sanitized).toString();
export const padLeftEven = (hex: string): string => {
hex = hex.length % 2 != 0 ? `0${hex}` : hex;
return hex;
};
export const sanitizeHex = (hex: number | string): ?string => {
@ -16,9 +16,9 @@ export const sanitizeHex = (hex: number | string): ?string => {
return `0x${padLeftEven(hex)}`;
};
export const padLeftEven = (hex: string): string => {
hex = hex.length % 2 != 0 ? `0${hex}` : hex;
return hex;
export const hexToDecimal = (hex: number): string => {
const sanitized: ?string = sanitizeHex(hex);
return !sanitized ? 'null' : new BigNumber(sanitized).toString();
};
export const strip = (str: string): string => {

@ -21,14 +21,14 @@ export const formatTime = (n: number): string => {
if (!n) return 'No time estimate';
let res = '';
if (hours != 0) {
if (hours !== 0) {
res += `${hours} hour`;
if (hours > 1) {
res += 's';
}
res += ' ';
}
if (minutes != 0) {
if (minutes !== 0) {
res += `${minutes} minutes`;
}
return res;

@ -1,7 +1,4 @@
/* @flow */
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
@ -39,7 +36,7 @@ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: St
devices: state.devices,
});
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (/* dispatch: Dispatch */): DispatchProps => ({
});

@ -1,7 +1,7 @@
import React from 'react';
import styled, { css } from 'styled-components';
import Link from 'components/Link';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
import P from 'components/Paragraph';
import { H2 } from 'components/Heading';

@ -4,7 +4,7 @@ import React, { Component } from 'react';
import styled, { keyframes } from 'styled-components';
import TrezorConnect from 'trezor-connect';
import P from 'components/Paragraph';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
import { PULSATE } from 'config/animations';
import colors from 'config/colors';
import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
@ -89,10 +89,8 @@ class ConnectDevice extends Component<Props> {
{this.props.showWebUsb && (
<React.Fragment>
<P>and</P>
<Button
className="trezor-webusb-button"
isWebUsb
>Check for devices
<Button isWebUsb>
Check for devices
</Button>
</React.Fragment>
)}

@ -7,23 +7,24 @@ import { FONT_SIZE, FONT_WEIGHT } from 'config/variables';
import installers from 'constants/bridge';
import { Select } from 'components/Select';
import Link from 'components/Link';
import Button from 'components/buttons/Button';
import Button from 'components/Button';
import Loader from 'components/Loader';
import P from 'components/Paragraph';
import Icon from 'components/Icon';
import ICONS from 'config/icons';
type State = {
version: string;
target: ?InstallTarget;
url: string;
}
type InstallTarget = {
id: string;
value: string;
label: string;
}
type State = {
version: string;
target: ?InstallTarget;
url: string;
}
// import type { Props } from './index';
type Props = {
@ -73,6 +74,13 @@ const DownloadBridgeWrapper = styled.div`
align-items: center;
`;
const DownloadBridgeButton = styled(Button)`
padding-top: 5px;
padding-bottom: 5px;
display: flex;
align-items: center;
`;
export default class InstallBridge extends Component<Props, State> {
constructor(props: Props) {
super(props);
@ -121,14 +129,14 @@ export default class InstallBridge extends Component<Props, State> {
options={installers}
/>
<Link href={url}>
<Button
icon={{
type: ICONS.DOWNLOAD,
color: colors.WHITE,
size: 30,
}}
>Download for {label}
</Button>
<DownloadBridgeButton>
<Icon
icon={ICONS.DOWNLOAD}
color={colors.WHITE}
size={30}
/>
Download for {label}
</DownloadBridgeButton>
</Link>
</DownloadBridgeWrapper>
<P>

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

Loading…
Cancel
Save