1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-12-25 00:18:07 +00:00

Merge branch 'master' into fix-menu-underline

This commit is contained in:
Szymon Lesisz 2018-09-12 11:42:33 +02:00 committed by GitHub
commit 397b757d1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 446 additions and 447 deletions

54
LICENSE Normal file
View File

@ -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.

View File

@ -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);

View File

@ -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,
};

View File

@ -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,

View File

@ -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`));

View File

@ -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

View File

@ -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)
});

View File

@ -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';
@ -118,21 +117,24 @@ const Button = ({
isWhite = false,
isWebUsb = false,
isTransparent = false,
}) => (
<Wrapper
className={className}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onFocus={onFocus}
isDisabled={isDisabled}
isWhite={isWhite}
isWebUsb={isWebUsb}
isTransparent={isTransparent}
>
{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.node.isRequired,

View File

@ -1,4 +1,3 @@
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
@ -164,9 +163,9 @@ 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}
key={n.title}
type={n.type}
title={n.title}
message={n.message}

View File

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

View File

@ -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';

View File

@ -8,9 +8,8 @@ import colors from 'config/colors';
import icons from 'config/icons';
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 {

View File

@ -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,

View File

@ -6,7 +6,7 @@ import P from 'components/Paragraph';
import Loader from 'components/Loader';
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>
);
});
}

View File

@ -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,

View File

@ -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/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,
});
}));
}
}

View File

@ -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;

View File

@ -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 Button from 'components/Button';
import PinButton from './components/PinButton';
import type { Props } from './index';
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 {

View File

@ -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';

View File

@ -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') {

View File

@ -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);

View File

@ -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';

View File

@ -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: '',

View File

@ -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';

View File

@ -17,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;
@ -64,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;

View File

@ -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,

View File

@ -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';

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -1,19 +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 });
@ -31,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),

View File

@ -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;

View File

@ -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 => {

View File

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

View File

@ -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 => ({
});

View File

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

View File

@ -13,18 +13,18 @@ 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 = {

View File

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

View File

@ -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,
accounts: state.accounts,
router: state.router,

View File

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

View File

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

View File

@ -3,7 +3,6 @@ import React, { Component } from 'react';
import styled from 'styled-components';
import TrezorConnect from 'trezor-connect';
import type { TrezorDevice } from 'flowtype';
import DeviceHeader from 'components/DeviceHeader';
import Button from 'components/Button';
import { isWebUSB } from 'utils/device';
import MenuItems from './components/MenuItems';
@ -39,6 +38,13 @@ class DeviceMenu extends Component<Props> {
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() {
const { transport } = this.props.connect;
if (isWebUSB(transport)) TrezorConnect.renderWebUSBButton();
@ -61,17 +67,10 @@ class DeviceMenu extends Component<Props> {
}
}
blurHandler(event: FocusEvent): void {
blurHandler(): void {
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 {
if (item.type === 'reload') {
this.props.acquireDevice();

View File

@ -17,11 +17,26 @@ type Props = {
const AsideWrapper = styled.aside`
position: relative;
top: 0;
width: 320px;
min-width: 320px;
overflow-x: hidden;
overflow: hidden;
background: ${colors.MAIN};
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`

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ type 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',
selectedAccount: state.selectedAccount,
wallet: state.wallet,

View File

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

View File

@ -5,7 +5,7 @@ import * as React from 'react';
import { bindActionCreators } from '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 type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import type { State, Dispatch } from 'flowtype';
@ -29,7 +29,7 @@ export type 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',
selectedAccount: state.selectedAccount,
wallet: state.wallet,

View File

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

View File

@ -13,7 +13,7 @@ const constants: Object = Object.freeze({
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_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');
@ -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_FILES: string = path.join(constants.TREZOR_CONNECT_ROOT, 'src/data/');
export const BUILD: string = constants.BUILD;
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 const {
BUILD,
SRC,
PORT,
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;