Merge branch 'master' into fix-menu-underline

pull/15/head
Szymon Lesisz 6 years ago committed by GitHub
commit 397b757d1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,54 @@
TREZOR REFERENCE SOURCE LICENSE (T-RSL)
=======================================
This license governs use of the accompanying software. If you use the software,
you accept this license. If you do not accept the license, do not use the
software.
1. Definitions
--------------
The terms "reproduce," "reproduction" and "distribution" have the same meaning
here as under U.S. copyright law.
"You" means the licensee of the software.
"Your company" means the company you worked for when you downloaded the
software.
"Reference use" means use of the software within your company as a reference,
in read only form, for the sole purposes of debugging your products,
maintaining your products, or enhancing the interoperability of your products
with the software, and specifically excludes the right to distribute the
software outside of your company.
"Licensed patents" means any Licensor patent claims which read directly on the
software as distributed by the Licensor under this license.
2. Grant of Rights
------------------
(A) Copyright Grant - Subject to the terms of this license, the Licensor grants
you a non-transferable, non-exclusive, worldwide, royalty-free copyright
license to reproduce the software for reference use.
(B) Patent Grant - Subject to the terms of this license, the Licensor grants
you a non-transferable, non-exclusive, worldwide, royalty-free patent license
under licensed patents for reference use.
3. Limitations
--------------
(A) No Trademark License - This license does not grant you any rights to use
the Licensor's name, logo, or trademarks.
(B) If you begin patent litigation against the Licensor over patents that you
think may apply to the software (including a cross-claim or counterclaim in
a lawsuit), your license to the software ends automatically.
(C) The software is licensed "as-is." You bear the risk of using it. The
Licensor gives no express warranties, guarantees or conditions. You may have
additional consumer rights under your local laws which this license cannot
change. To the extent permitted under your local laws, the Licensor excludes
the implied warranties of merchantability, fitness for a particular purpose and
non-infringement.

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

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

@ -25,10 +25,6 @@ export type TokenAction = {
payload: State payload: State
} }
type SelectOptions = {
options?: Array<NetworkToken>
}
// action from component <reactSelect> // action from component <reactSelect>
export const load = (input: string, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<any> => { 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); //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> => { 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); const web3instance = getState().web3.find(w3 => w3.network === account.network);
if (!web3instance) return; if (!web3instance) return;
@ -82,20 +92,6 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => async
dispatch(setBalance(token.address, account.address, tokenBalance)); 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 => ({ export const remove = (token: Token): Action => ({
type: TOKEN.REMOVE, type: TOKEN.REMOVE,
token, token,

@ -1,10 +1,7 @@
/* @flow */ /* @flow */
import TrezorConnect, { import TrezorConnect, {
UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT,
} from 'trezor-connect'; } from 'trezor-connect';
import * as TOKEN from 'actions/constants/token';
import * as CONNECT from 'actions/constants/TrezorConnect'; import * as CONNECT from 'actions/constants/TrezorConnect';
import * as NOTIFICATION from 'actions/constants/notification'; import * as NOTIFICATION from 'actions/constants/notification';
import * as WALLET from 'actions/constants/wallet'; 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> => { export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
// set listeners // set listeners
TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => { TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => {
@ -112,7 +116,7 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS
}); });
// $FlowIssue LOCAL not declared // $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 = typeof LOCAL === 'string' ? LOCAL : 'https://connect.trezor.io/5/';
//window.__TREZOR_CONNECT_SRC = 'https://sisyfos.trezor.io/connect/'; //window.__TREZOR_CONNECT_SRC = 'https://sisyfos.trezor.io/connect/';
// window.__TREZOR_CONNECT_SRC = 'https://localhost:8088/'; // 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 // selection from Aside dropdown button
// after device_connect event // after device_connect event
// or after acquiring device // 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> => { export const switchToFirstAvailableDevice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const { devices } = getState(); const { devices } = getState();
if (devices.length > 0) { if (devices.length > 0) {
@ -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) { if (device.features) {
const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`; const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`;
dispatch(push(`/device/${devUrl}/settings`)); dispatch(push(`/device/${devUrl}/settings`));

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

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

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Icon from 'components/Icon';
import colors from 'config/colors'; import colors from 'config/colors';
import { TRANSITION } from 'config/variables'; import { TRANSITION } from 'config/variables';
@ -118,21 +117,24 @@ const Button = ({
isWhite = false, isWhite = false,
isWebUsb = false, isWebUsb = false,
isTransparent = false, isTransparent = false,
}) => ( }) => {
<Wrapper const newClassName = isWebUsb ? `${className} trezor-webusb-button` : className;
className={className} return (
onClick={onClick} <Wrapper
onMouseEnter={onMouseEnter} className={newClassName}
onMouseLeave={onMouseLeave} onClick={onClick}
onFocus={onFocus} onMouseEnter={onMouseEnter}
isDisabled={isDisabled} onMouseLeave={onMouseLeave}
isWhite={isWhite} onFocus={onFocus}
isWebUsb={isWebUsb} isDisabled={isDisabled}
isTransparent={isTransparent} isWhite={isWhite}
> isWebUsb={isWebUsb}
{children} isTransparent={isTransparent}
</Wrapper> >
); {children}
</Wrapper>
);
};
Button.propTypes = { Button.propTypes = {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,

@ -1,4 +1,3 @@
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -164,9 +163,9 @@ export const Notification = (props: NProps): React$Element<string> => {
export const NotificationGroup = (props) => { export const NotificationGroup = (props) => {
const { notifications, close } = props; const { notifications, close } = props;
return notifications.map((n, i) => ( return notifications.map(n => (
<Notification <Notification
key={i} key={n.title}
type={n.type} type={n.type}
title={n.title} title={n.title}
message={n.message} message={n.message}

@ -11,11 +11,6 @@ const Wrapper = styled.div`
justify-content: flex-start; justify-content: flex-start;
`; `;
const Label = styled.span`
padding-bottom: 4px;
color: ${colors.TEXT_SECONDARY};
`;
const disabledColor = colors.TEXT_PRIMARY; const disabledColor = colors.TEXT_PRIMARY;
const TextArea = styled.textarea` const TextArea = styled.textarea`

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

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

@ -63,12 +63,12 @@ const ErrorMessage = styled.div`
`; `;
export default class DuplicateDevice extends Component<Props, State> { export default class DuplicateDevice extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State; state: State;
input: ?HTMLInputElement; input: ?HTMLInputElement;
keyboardHandler: (event: KeyboardEvent) => void;
constructor(props: Props) { constructor(props: Props) {
super(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 { componentDidMount(): void {
// one time autofocus // one time autofocus
if (this.input) this.input.focus(); 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() { submit() {
if (!this.props.modal.opened) return; if (!this.props.modal.opened) return;
const extended: Object = { instanceName: this.state.instanceName, instance: this.state.instance }; 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; if (!this.props.modal.opened) return null;
const { device } = this.props.modal; const { device } = this.props.modal;
const { onCancel, onDuplicateDevice } = this.props.modalActions; const { onCancel } = this.props.modalActions;
const { const {
defaultName, defaultName,
instanceName, instanceName,

@ -6,7 +6,7 @@ import P from 'components/Paragraph';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import Button from 'components/Button'; import Button from 'components/Button';
import type { Props } from './index'; import type { Props } from '../../index';
type State = { type State = {
countdown: number; countdown: number;
@ -77,9 +77,9 @@ export default class RememberDevice extends Component<Props, State> {
this.props.modalActions.onForgetDevice(this.props.modal.device); this.props.modalActions.onForgetDevice(this.props.modal.device);
} }
} else { } else {
this.setState({ this.setState(previousState => ({
countdown: this.state.countdown - 1, countdown: previousState.countdown - 1,
}); }));
} }
}; };
@ -108,16 +108,16 @@ export default class RememberDevice extends Component<Props, State> {
render() { render() {
if (!this.props.modal.opened) return null; if (!this.props.modal.opened) return null;
const { device, instances } = this.props.modal; 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'; const devicePlural: string = instances && instances.length > 1 ? 'devices or to remember them' : 'device or to remember it';
if (instances && instances.length > 0) { if (instances && instances.length > 0) {
label = instances.map((instance, index) => { label = instances.map((instance, index) => {
let comma: string = ''; let comma: string = '';
if (index > 0) comma = ', '; if (index > 0) comma = ', ';
return ( 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 { UI } from 'trezor-connect';
import { default as ModalActions } from 'actions/ModalActions'; import ModalActions from 'actions/ModalActions';
import { default as ReceiveActions } from 'actions/ReceiveActions'; import ReceiveActions from 'actions/ReceiveActions';
import * as RECEIVE from 'actions/constants/receive'; import * as RECEIVE from 'actions/constants/receive';
import * as CONNECT from 'actions/constants/TrezorConnect'; import * as CONNECT from 'actions/constants/TrezorConnect';
@ -51,8 +51,6 @@ type DispatchProps = {
export type Props = StateProps & DispatchProps; export type Props = StateProps & DispatchProps;
const duration = 300;
const Fade = ({ children, ...props }) => ( const Fade = ({ children, ...props }) => (
<CSSTransition <CSSTransition
{...props} {...props}
@ -75,7 +73,7 @@ const ModalContainer = styled.div`
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
overflow: auto; overflow: auto;
padding: 20px; padding: 20px;
`; `;
const ModalWindow = styled.div` const ModalWindow = styled.div`
@ -125,7 +123,8 @@ class Modal extends Component<Props> {
component = (<DuplicateDevice {...this.props} />); component = (<DuplicateDevice {...this.props} />);
break; break;
default: null; default:
component = null;
} }
let ch = 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, modal: state.modal,
accounts: state.accounts, accounts: state.accounts,
devices: state.devices, devices: state.devices,

@ -4,14 +4,13 @@ import raf from 'raf';
import colors from 'config/colors'; import colors from 'config/colors';
import P from 'components/Paragraph'; import P from 'components/Paragraph';
import { FONT_SIZE } from 'config/variables'; import { FONT_SIZE } from 'config/variables';
import { H2 } from 'components/Heading';
import Link from 'components/Link'; import Link from 'components/Link';
import Checkbox from 'components/Checkbox'; import Checkbox from 'components/Checkbox';
import Button from 'components/Button'; import Button from 'components/Button';
import Input from 'components/inputs/Input'; import Input from 'components/inputs/Input';
import styled from 'styled-components'; import styled from 'styled-components';
import type { Props } from './index'; import type { Props } from '../../index';
const Wrapper = styled.div` const Wrapper = styled.div`
padding: 24px 48px; padding: 24px 48px;
@ -55,14 +54,6 @@ type State = {
} }
export default class PinModal extends Component<Props, State> { export default class PinModal extends Component<Props, State> {
keyboardHandler: (event: KeyboardEvent) => void;
state: State;
passphraseInput: ?HTMLInputElement;
passphraseRevisionInput: ?HTMLInputElement;
constructor(props: Props) { constructor(props: Props) {
super(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 { keyboardHandler(event: KeyboardEvent): void {
if (event.keyCode === 13) { if (event.keyCode === 13) {
event.preventDefault(); event.preventDefault();
@ -108,6 +108,7 @@ export default class PinModal extends Component<Props, State> {
} }
} }
componentDidMount(): void { componentDidMount(): void {
// one time autofocus // one time autofocus
if (this.passphraseInput) this.passphraseInput.focus(); 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, // we don't want to keep password inside "value" attribute,
// so we need to replace it thru javascript // so we need to replace it thru javascript
componentDidUpdate() { 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 => { onPassphraseChange = (input: string, value: string): void => {
// https://codepen.io/MiDri/pen/PGqvrO // https://codepen.io/MiDri/pen/PGqvrO
// or // or
// https://github.com/zakangelle/react-password-mask/blob/master/src/index.js // https://github.com/zakangelle/react-password-mask/blob/master/src/index.js
if (input === 'passphrase') { if (input === 'passphrase') {
this.setState({ this.setState(previousState => ({
match: this.state.singleInput || this.state.passphraseRevision === value, match: previousState.singleInput || previousState.passphraseRevision === value,
passphrase: value, passphrase: value,
}); }));
} else { } else {
this.setState({ this.setState(previousState => ({
match: this.state.passphrase === value, match: previousState.passphrase === value,
passphraseRevision: value, passphraseRevision: value,
passphraseRevisionTouched: true, passphraseRevisionTouched: true,
}); }));
} }
} }

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

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

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

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

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

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

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

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

@ -17,7 +17,7 @@ export const getSelectedDevice = (state: State): ?TrezorDevice => {
const locationState = state.router.location.state; const locationState = state.router.location.state;
if (!locationState.device) return undefined; 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) => { return state.devices.find((d) => {
if (!d.features && d.path === locationState.device) { if (!d.features && d.path === locationState.device) {
return true; return true;
@ -64,14 +64,14 @@ export const getSelectedAccount = (state: State): ?Account => {
const locationState = state.router.location.state; const locationState = state.router.location.state;
if (!device || !locationState.network || !locationState.account) return null; 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); return state.accounts.find(a => a.deviceState === device.state && a.index === index && a.network === locationState.network);
}; };
export const getSelectedNetwork = (state: State): ?Coin => { export const getSelectedNetwork = (state: State): ?Coin => {
const device = state.wallet.selectedDevice; const device = state.wallet.selectedDevice;
const coins = state.localStorage.config.coins; const { coins } = state.localStorage.config;
const locationState = state.router.location.state; const locationState = state.router.location.state;
if (!device || !locationState.network) return null; if (!device || !locationState.network) return null;

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

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

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

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

@ -1,10 +1,8 @@
/* @flow */ /* @flow */
import { push } from 'react-router-redux'; import { push } from 'react-router-redux';
import TrezorConnect, { import {
TRANSPORT, DEVICE_EVENT, UI_EVENT, UI, DEVICE, TRANSPORT, DEVICE,
} from 'trezor-connect'; } from 'trezor-connect';
import * as TrezorConnectActions from 'actions/TrezorConnectActions'; import * as TrezorConnectActions from 'actions/TrezorConnectActions';
import * as DiscoveryActions from 'actions/DiscoveryActions'; 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 WEB3 from 'actions/constants/web3';
import * as STORAGE from 'actions/constants/localStorage'; import * as STORAGE from 'actions/constants/localStorage';
import * as CONNECT from 'actions/constants/TrezorConnect'; import * as CONNECT from 'actions/constants/TrezorConnect';
import * as NOTIFICATION from 'actions/constants/notification';
import * as MODAL from 'actions/constants/modal';
import type { import type {
Middleware, Middleware,
MiddlewareAPI, MiddlewareAPI,
MiddlewareDispatch, MiddlewareDispatch,
State, State,
Dispatch,
Action, Action,
AsyncAction,
GetState,
RouterLocationState,
} from 'flowtype'; } from 'flowtype';
const TrezorConnectService: Middleware = (api: MiddlewareAPI) => (next: MiddlewareDispatch) => (action: Action): Action => { 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 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); next(action);

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

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

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

@ -28,7 +28,7 @@ export const formatTime = (n: number): string => {
} }
res += ' '; res += ' ';
} }
if (minutes != 0) { if (minutes !== 0) {
res += `${minutes} minutes`; res += `${minutes} minutes`;
} }
return res; return res;

@ -1,7 +1,4 @@
/* @flow */ /* @flow */
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import type { MapStateToProps, MapDispatchToProps } 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, devices: state.devices,
}); });
const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (dispatch: Dispatch): DispatchProps => ({ const mapDispatchToProps: MapDispatchToProps<Dispatch, OwnProps, DispatchProps> = (/* dispatch: Dispatch */): DispatchProps => ({
}); });

@ -89,7 +89,9 @@ class ConnectDevice extends Component<Props> {
{this.props.showWebUsb && ( {this.props.showWebUsb && (
<React.Fragment> <React.Fragment>
<P>and</P> <P>and</P>
<Button isWebUsb>Check for devices</Button> <Button isWebUsb>
Check for devices
</Button>
</React.Fragment> </React.Fragment>
)} )}
</Wrapper> </Wrapper>

@ -13,18 +13,18 @@ import P from 'components/Paragraph';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import ICONS from 'config/icons'; import ICONS from 'config/icons';
type State = {
version: string;
target: ?InstallTarget;
url: string;
}
type InstallTarget = { type InstallTarget = {
id: string; id: string;
value: string; value: string;
label: string; label: string;
} }
type State = {
version: string;
target: ?InstallTarget;
url: string;
}
// import type { Props } from './index'; // import type { Props } from './index';
type Props = { type Props = {

@ -138,7 +138,7 @@ export default (props: Props) => {
)} )}
<P> <P>
<LandingFooterTextWrapper> <LandingFooterTextWrapper>
Don't have TREZOR? Don&amp;t have TREZOR?
</LandingFooterTextWrapper> </LandingFooterTextWrapper>
<StyledLink <StyledLink
href="https://trezor.io/" href="https://trezor.io/"

@ -16,7 +16,7 @@ type OwnProps = {
} }
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State/* , own: OwnProps */): StateProps => ({
connect: state.connect, connect: state.connect,
accounts: state.accounts, accounts: state.accounts,
router: state.router, router: state.router,

@ -4,7 +4,6 @@ import BigNumber from 'bignumber.js';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import colors from 'config/colors'; import colors from 'config/colors';
import Loader from 'components/Loader'; import Loader from 'components/Loader';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
import * as stateUtils from 'reducers/utils'; import * as stateUtils from 'reducers/utils';
import Tooltip from 'components/Tooltip'; import Tooltip from 'components/Tooltip';

@ -24,7 +24,7 @@ class DeviceList extends Component {
device !== selectedDevice && ( device !== selectedDevice && (
<DeviceHeader <DeviceHeader
key={`${device.instanceLabel}`} key={`${device.instanceLabel}`}
onClickWrapper={() => this.props.onSelectDevice(device)} onClickWrapper={() => onSelectDevice(device)}
onClickIcon={() => this.onDeviceMenuClick({ type: 'forget', label: '' }, device)} onClickIcon={() => this.onDeviceMenuClick({ type: 'forget', label: '' }, device)}
icon={( icon={(
<IconClick onClick={(event) => { <IconClick onClick={(event) => {

@ -3,7 +3,6 @@ import React, { Component } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import TrezorConnect from 'trezor-connect'; import TrezorConnect from 'trezor-connect';
import type { TrezorDevice } from 'flowtype'; import type { TrezorDevice } from 'flowtype';
import DeviceHeader from 'components/DeviceHeader';
import Button from 'components/Button'; import Button from 'components/Button';
import { isWebUSB } from 'utils/device'; import { isWebUSB } from 'utils/device';
import MenuItems from './components/MenuItems'; import MenuItems from './components/MenuItems';
@ -39,6 +38,13 @@ class DeviceMenu extends Component<Props> {
this.blurHandler = this.blurHandler.bind(this); this.blurHandler = this.blurHandler.bind(this);
} }
componentDidMount(): void {
window.addEventListener('mousedown', this.mouseDownHandler, false);
// window.addEventListener('blur', this.blurHandler, false);
const { transport } = this.props.connect;
if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
}
componentDidUpdate() { componentDidUpdate() {
const { transport } = this.props.connect; const { transport } = this.props.connect;
if (isWebUSB(transport)) TrezorConnect.renderWebUSBButton(); if (isWebUSB(transport)) TrezorConnect.renderWebUSBButton();
@ -61,17 +67,10 @@ class DeviceMenu extends Component<Props> {
} }
} }
blurHandler(event: FocusEvent): void { blurHandler(): void {
this.props.toggleDeviceDropdown(false); this.props.toggleDeviceDropdown(false);
} }
componentDidMount(): void {
window.addEventListener('mousedown', this.mouseDownHandler, false);
// window.addEventListener('blur', this.blurHandler, false);
const { transport } = this.props.connect;
if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton();
}
onDeviceMenuClick(item: DeviceMenuItem, device: TrezorDevice): void { onDeviceMenuClick(item: DeviceMenuItem, device: TrezorDevice): void {
if (item.type === 'reload') { if (item.type === 'reload') {
this.props.acquireDevice(); this.props.acquireDevice();

@ -17,11 +17,26 @@ type Props = {
const AsideWrapper = styled.aside` const AsideWrapper = styled.aside`
position: relative; position: relative;
top: 0;
width: 320px; width: 320px;
min-width: 320px; overflow: hidden;
overflow-x: hidden;
background: ${colors.MAIN}; background: ${colors.MAIN};
border-right: 1px solid ${colors.DIVIDER}; border-right: 1px solid ${colors.DIVIDER};
.fixed {
position: fixed;
border-right: 1px solid ${colors.DIVIDER};
}
.fixed-bottom {
padding-bottom: 60px;
.sticky-bottom {
position: fixed;
bottom: 0;
background: ${colors.MAIN};
border-right: 1px solid ${colors.DIVIDER};
}
}
`; `;
const StickyContainerWrapper = styled.div` const StickyContainerWrapper = styled.div`

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import colors from 'config/colors'; import colors from 'config/colors';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import Sticky from 'react-sticky-el';
import icons from 'config/icons'; import icons from 'config/icons';
import { TransitionGroup, CSSTransition } from 'react-transition-group'; import { TransitionGroup, CSSTransition } from 'react-transition-group';
import styled from 'styled-components'; import styled from 'styled-components';
@ -11,6 +12,8 @@ import CoinMenu from './components/CoinMenu';
import DeviceMenu from './components/DeviceMenu'; import DeviceMenu from './components/DeviceMenu';
import StickyContainer from './components/StickyContainer'; import StickyContainer from './components/StickyContainer';
const Header = styled(DeviceHeader)``;
const TransitionGroupWrapper = styled(TransitionGroup)` const TransitionGroupWrapper = styled(TransitionGroup)`
width: 640px; width: 640px;
`; `;
@ -21,14 +24,15 @@ const TransitionContentWrapper = styled.div`
vertical-align: top; vertical-align: top;
`; `;
const StickyBottom = styled.div` const Footer = styled.div`
position: fixed; position: fixed;
bottom: 0; bottom: 0;
background: ${colors.MAIN}; background: ${colors.MAIN};
border-right: 1px solid ${colors.DIVIDER}; border-right: 1px solid ${colors.DIVIDER};
`; `;
const MenuWrapper = styled.div` const Body = styled.div`
overflow: auto;
background: ${colors.LANDING}; background: ${colors.LANDING};
`; `;
@ -40,11 +44,6 @@ const Help = styled.div`
width: 319px; width: 319px;
padding: 8px 0px; padding: 8px 0px;
border-top: 1px solid ${colors.DIVIDER}; border-top: 1px solid ${colors.DIVIDER};
&.fixed {
position: fixed;
bottom: 0px;
}
`; `;
const A = styled.a` const A = styled.a`
@ -150,28 +149,25 @@ class LeftNavigation extends Component {
} }
render() { render() {
const { selectedDevice } = this.props.wallet;
return ( return (
<StickyContainer <StickyContainer
location={this.props.location.pathname} location={this.props.location.pathname}
deviceSelection={this.props.deviceDropdownOpened} deviceSelection={this.props.deviceDropdownOpened}
> >
{selectedDevice && ( <Header
<DeviceHeader onClickWrapper={() => this.handleOpen()}
onClickWrapper={() => this.handleOpen()} device={this.props.wallet.selectedDevice}
device={this.props.wallet.selectedDevice} transport={this.props.connect.transport}
transport={this.props.connect.transport} devices={this.props.devices}
devices={this.props.devices} isOpen={this.props.deviceDropdownOpened}
isOpen={this.props.deviceDropdownOpened} {...this.props}
{...this.props} />
/> <Body>
) }
<MenuWrapper>
{this.state.shouldRenderDeviceSelection && <DeviceMenu {...this.props} />} {this.state.shouldRenderDeviceSelection && <DeviceMenu {...this.props} />}
{this.shouldRenderAccounts() && this.getMenuTransition(<AccountMenu {...this.props} />)} {this.shouldRenderAccounts() && this.getMenuTransition(<AccountMenu {...this.props} />)}
{this.shouldRenderCoins() && this.getMenuTransition(<CoinMenu {...this.props} />)} {this.shouldRenderCoins() && this.getMenuTransition(<CoinMenu {...this.props} />)}
</MenuWrapper> </Body>
<StickyBottom> <Footer className="sticky-bottom">
<Help> <Help>
<A <A
href="https://trezor.io/support/" href="https://trezor.io/support/"
@ -181,7 +177,8 @@ class LeftNavigation extends Component {
<Icon size={26} icon={icons.CHAT} color={colors.TEXT_SECONDARY} />Need help? <Icon size={26} icon={icons.CHAT} color={colors.TEXT_SECONDARY} />Need help?
</A> </A>
</Help> </Help>
</StickyBottom> </Footer>
<Sticky />
</StickyContainer> </StickyContainer>
); );
} }

@ -15,8 +15,6 @@ const Wrapper = styled.div`
class Indicator extends Component<Props, State> { class Indicator extends Component<Props, State> {
reposition: () => void; reposition: () => void;
state: State;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
@ -30,6 +28,8 @@ class Indicator extends Component<Props, State> {
this.reposition = this.reposition.bind(this); this.reposition = this.reposition.bind(this);
} }
state: State;
handleResize() { handleResize() {
this.reposition(); this.reposition();
} }
@ -43,7 +43,7 @@ class Indicator extends Component<Props, State> {
window.removeEventListener('resize', this.reposition, false); window.removeEventListener('resize', this.reposition, false);
} }
componentDidUpdate(newProps: Props) { componentDidUpdate() {
this.reposition(); this.reposition();
} }

@ -36,7 +36,8 @@ const StyledNavLink = styled(NavLink)`
} }
`; `;
const TopNavigationAccount = (props: any) => {
const TopNavigationAccount = (props) => {
const urlParams = props.match.params; const urlParams = props.match.params;
const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`; const basePath = `/device/${urlParams.device}/network/${urlParams.network}/account/${urlParams.account}`;

@ -20,13 +20,13 @@ import TopNavigationAccount from './components/TopNavigationAccount';
import TopNavigationDeviceSettings from './components/TopNavigationDeviceSettings'; import TopNavigationDeviceSettings from './components/TopNavigationDeviceSettings';
type WalletContainerProps = { type WalletContainerProps = {
wallet: $ElementType<State, 'wallet'>, // wallet: $ElementType<State, 'wallet'>,
children?: React.Node children?: React.Node
} }
type ContentProps = { // type ContentProps = {
children?: React.Node // children?: React.Node
} // }
const AppWrapper = styled.div` const AppWrapper = styled.div`
position: relative; position: relative;
@ -83,7 +83,7 @@ const Wallet = (props: WalletContainerProps) => (
<AppWrapper> <AppWrapper>
<Header /> <Header />
<WalletWrapper> <WalletWrapper>
<LeftNavigation /> {props.wallet.selectedDevice && <LeftNavigation />}
<MainContent> <MainContent>
<Navigation> <Navigation>
<Route path="/device/:device/network/:network/account/:account" component={TopNavigationAccount} /> <Route path="/device/:device/network/:network/account/:account" component={TopNavigationAccount} />
@ -101,7 +101,7 @@ const Wallet = (props: WalletContainerProps) => (
</AppWrapper> </AppWrapper>
); );
const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State, own: {}): WalletContainerProps => ({ const mapStateToProps: MapStateToProps<State, {}, WalletContainerProps> = (state: State): WalletContainerProps => ({
wallet: state.wallet, wallet: state.wallet,
}); });

@ -24,7 +24,7 @@ type DispatchProps = BaseDispatchProps & {
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps; export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => ({
className: 'receive', className: 'receive',
selectedAccount: state.selectedAccount, selectedAccount: state.selectedAccount,
wallet: state.wallet, wallet: state.wallet,

@ -25,6 +25,7 @@ const AddressWrapper = styled.div`
padding: 0px 48px; padding: 0px 48px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: ${props => (props.isShowingQrCode ? 'column' : 'row')};
`; `;
const ValueWrapper = styled.div` const ValueWrapper = styled.div`
@ -139,7 +140,9 @@ const AccountReceive = (props: Props) => {
<SelectedAccount {...props}> <SelectedAccount {...props}>
<Wrapper> <Wrapper>
<StyledH2>Receive Ethereum or tokens</StyledH2> <StyledH2>Receive Ethereum or tokens</StyledH2>
<AddressWrapper> <AddressWrapper
isShowingQrCode={addressVerified || addressUnverified}
>
{isAddressVerifying && ( {isAddressVerifying && (
<AddressInfoText>Confirm address on TREZOR</AddressInfoText> <AddressInfoText>Confirm address on TREZOR</AddressInfoText>
)} )}

@ -5,7 +5,7 @@ import * as React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { default as SendFormActions } from 'actions/SendFormActions'; import SendFormActions from 'actions/SendFormActions';
import * as SessionStorageActions from 'actions/SessionStorageActions'; import * as SessionStorageActions from 'actions/SessionStorageActions';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux'; import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from 'flowtype'; import type { State, Dispatch } from 'flowtype';
@ -29,7 +29,7 @@ export type DispatchProps = BaseDispatchProps & {
export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps; export type Props = StateProps & BaseStateProps & DispatchProps & BaseDispatchProps;
const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State, own: OwnProps): StateProps => ({ const mapStateToProps: MapStateToProps<State, OwnProps, StateProps> = (state: State): StateProps => ({
className: 'send-from', className: 'send-from',
selectedAccount: state.selectedAccount, selectedAccount: state.selectedAccount,
wallet: state.wallet, wallet: state.wallet,

@ -9,7 +9,7 @@ import ScaleText from 'react-scale-text';
import type { Coin } from 'reducers/LocalStorageReducer'; import type { Coin } from 'reducers/LocalStorageReducer';
import type { Token } from 'reducers/TokensReducer'; import type { Token } from 'reducers/TokensReducer';
import type { Props as BaseProps } from '../Container'; import type { Props as BaseProps } from '../../Container';
type Props = { type Props = {
pending: $PropertyType<$ElementType<BaseProps, 'selectedAccount'>, 'pending'>, pending: $PropertyType<$ElementType<BaseProps, 'selectedAccount'>, 'pending'>,

@ -13,7 +13,7 @@ const constants: Object = Object.freeze({
TREZOR_CONNECT_ROOT: path.join(ABSOLUTE_BASE, '../trezor-connect/'), TREZOR_CONNECT_ROOT: path.join(ABSOLUTE_BASE, '../trezor-connect/'),
}); });
export const TREZOR_CONNECT_ROOT: string = constants.TREZOR_CONNECT_ROOT; export const { TREZOR_CONNECT_ROOT }: { TREZOR_CONNECT_ROOT: string } = constants;
export const TREZOR_CONNECT: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/js/index'); export const TREZOR_CONNECT: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/js/index');
export const TREZOR_IFRAME: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/js/iframe/iframe.js'); export const TREZOR_IFRAME: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/js/iframe/iframe.js');
export const TREZOR_POPUP: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/js/popup/popup.js'); export const TREZOR_POPUP: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/js/popup/popup.js');
@ -21,10 +21,16 @@ export const TREZOR_WEBUSB: string = path.join(constants.TREZOR_CONNECT_ROOT, 's
export const TREZOR_CONNECT_HTML: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/html/'); export const TREZOR_CONNECT_HTML: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/html/');
export const TREZOR_CONNECT_FILES: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/data/'); export const TREZOR_CONNECT_FILES: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/data/');
export const BUILD: string = constants.BUILD; export const {
export const SRC: string = constants.SRC; BUILD,
export const PORT: string = constants.PORT; SRC,
export const INDEX: string = constants.INDEX; PORT,
export const PUBLIC: string = constants.PUBLIC; INDEX,
PUBLIC,
}: { BUILD: string, SRC: string, PORT: string, INDEX: string, PUBLIC: string } = constants;
// export const SRC: string = constants.SRC;
// export const PORT: string = constants.PORT;
// export const INDEX: string = constants.INDEX;
// export const PUBLIC: string = constants.PUBLIC;
export default constants; export default constants;
Loading…
Cancel
Save