diff --git a/src/js/actions/AddressActions.js b/src/js/actions/AddressActions.js index 0310d32e..3a8bbeb5 100644 --- a/src/js/actions/AddressActions.js +++ b/src/js/actions/AddressActions.js @@ -2,14 +2,48 @@ 'use strict'; import * as ADDRESS from './constants/address'; +import type { Action, TrezorDevice } from '../flowtype'; +import type { State } from '../reducers/AccountsReducer'; -export type AddressAction = { +export type AddressAction = + AddressFromStorageAction + | AddressCreateAction + | AddressSetBalanceAction + | AddressSetNonceAction; + +export type AddressFromStorageAction = { + type: typeof ADDRESS.FROM_STORAGE, + payload: State +} + +export type AddressCreateAction = { type: typeof ADDRESS.CREATE, - payload: any -} | { + device: TrezorDevice, + network: string, + index: number, + path: Array, + address: string +} + +export type AddressSetBalanceAction = { type: typeof ADDRESS.SET_BALANCE, - payload: any -} | { + address: string, + network: string, + balance: string +} + +export type AddressSetNonceAction = { type: typeof ADDRESS.SET_NONCE, - payload: any + address: string, + network: string, + nonce: number +} + +export const setBalance = (address: string, network: string, balance: string): Action => { + return { + type: ADDRESS.SET_BALANCE, + address, + network, + balance + } } \ No newline at end of file diff --git a/src/js/actions/DiscoveryActions.js b/src/js/actions/DiscoveryActions.js index 62399cd2..d6a68558 100644 --- a/src/js/actions/DiscoveryActions.js +++ b/src/js/actions/DiscoveryActions.js @@ -7,24 +7,52 @@ import * as DISCOVERY from './constants/discovery'; import * as ADDRESS from './constants/address'; import * as TOKEN from './constants/token'; import * as NOTIFICATION from './constants/notification'; -import type { Discovery } from '../reducers/DiscoveryReducer'; +import * as AddressActions from '../actions/AddressActions'; import HDKey from 'hdkey'; import EthereumjsUtil from 'ethereumjs-util'; import { getNonceAsync, getBalanceAsync, getTokenBalanceAsync } from './Web3Actions'; +import { setBalance as setTokenBalance } from './TokenActions'; - +import type { AsyncAction, Action, GetState, Dispatch, TrezorDevice } from '../flowtype'; +import type { Discovery, State } from '../reducers/DiscoveryReducer'; export type DiscoveryAction = { + type: typeof DISCOVERY.FROM_STORAGE, + payload: State +} | DiscoveryStartAction + | DiscoveryWaitingAction + | DiscoveryStopAction + | DiscoveryCompleteAction; + +export type DiscoveryStartAction = { type: typeof DISCOVERY.START, -} | { + device: TrezorDevice, + network: string, + xpub: string, + basePath: Array, + hdKey: HDKey +} + +export type DiscoveryWaitingAction = { + type: typeof DISCOVERY.WAITING, + device: TrezorDevice, + network: string +} + +export type DiscoveryStopAction = { type: typeof DISCOVERY.STOP, -} | { + device: TrezorDevice +} + +export type DiscoveryCompleteAction = { type: typeof DISCOVERY.COMPLETE, -}; + device: TrezorDevice, + network: string +} -export const start = (device: any, network: string, ignoreCompleted?: boolean): any => { - return (dispatch, getState) => { +export const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): AsyncAction => { + return (dispatch: Dispatch, getState: GetState): void => { const selected = findSelectedDevice(getState().connect); if (!selected) { @@ -42,9 +70,11 @@ export const start = (device: any, network: string, ignoreCompleted?: boolean): return; } - const discovery = getState().discovery; - let discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network); + + const discovery: State = getState().discovery; + let discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network); + if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) { dispatch({ type: DISCOVERY.WAITING, @@ -75,11 +105,12 @@ export const start = (device: any, network: string, ignoreCompleted?: boolean): } } -const begin = (device: any, network: string) => { - return async (dispatch, getState) => { +const begin = (device: TrezorDevice, network: string): AsyncAction => { + return async (dispatch: Dispatch, getState: GetState): Promise => { const { config } = getState().localStorage; const coinToDiscover = config.coins.find(c => c.network === network); + if (!coinToDiscover) return; dispatch({ type: DISCOVERY.WAITING, @@ -88,7 +119,7 @@ const begin = (device: any, network: string) => { }); // get xpub from TREZOR - const response = await TrezorConnect.getPublicKey({ + const response: Object = await TrezorConnect.getPublicKey({ device: { path: device.path, instance: device.instance, @@ -145,8 +176,8 @@ const begin = (device: any, network: string) => { } } -const discoverAddress = (device: any, discoveryProcess: Discovery): any => { - return async (dispatch, getState) => { +const discoverAddress = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => { + return async (dispatch: Dispatch, getState: GetState): Promise => { const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`); const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex); @@ -236,15 +267,13 @@ const discoverAddress = (device: any, discoveryProcess: Discovery): any => { } const web3instance = getState().web3.find(w3 => w3.network === network); + if (!web3instance) return; const balance = await getBalanceAsync(web3instance.web3, ethAddress); if (discoveryProcess.interrupted) return; - dispatch({ - type: ADDRESS.SET_BALANCE, - address: ethAddress, - network, - balance: web3instance.web3.fromWei(balance.toString(), 'ether') - }); + dispatch( + AddressActions.setBalance(ethAddress, network, web3instance.web3.fromWei(balance.toString(), 'ether')) + ); const userTokens = []; // const userTokens = [ @@ -255,16 +284,10 @@ const discoverAddress = (device: any, discoveryProcess: Discovery): any => { for (let i = 0; i < userTokens.length; i++) { const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, userTokens[i].address, ethAddress); if (discoveryProcess.interrupted) return; - dispatch({ - type: TOKEN.SET_BALANCE, - tokenName: userTokens[i].symbol, - ethAddress: ethAddress, - tokenAddress: userTokens[i].address, - balance: tokenBalance.toString() - }) + dispatch( setTokenBalance(userTokens[i].address, ethAddress, tokenBalance.toString()) ) } - const nonce = await getNonceAsync(web3instance.web3, ethAddress); + const nonce: number = await getNonceAsync(web3instance.web3, ethAddress); if (discoveryProcess.interrupted) return; dispatch({ type: ADDRESS.SET_NONCE, @@ -298,8 +321,8 @@ const discoverAddress = (device: any, discoveryProcess: Discovery): any => { } } -export const restore = (): any => { - return (dispatch, getState): void => { +export const restore = (): AsyncAction => { + return (dispatch: Dispatch, getState: GetState): void => { const selected = findSelectedDevice(getState().connect); if (selected && selected.connected && !selected.unacquired) { @@ -315,8 +338,8 @@ export const restore = (): any => { // there is no discovery process but it should be // this is possible race condition when "network" was changed in url but device was not authenticated yet // try to start discovery after CONNECT.AUTH_DEVICE action -export const check = (): any => { - return (dispatch, getState): void => { +export const check = (): AsyncAction => { + return (dispatch: Dispatch, getState: GetState): void => { const selected = findSelectedDevice(getState().connect); if (!selected) return; @@ -330,7 +353,7 @@ export const check = (): any => { } } -export const stop = (device: any): any => { +export const stop = (device: TrezorDevice): Action => { // TODO: release devices session // corner case switch /eth to /etc (discovery start stop - should not be async) return { diff --git a/src/js/actions/LocalStorageActions.js b/src/js/actions/LocalStorageActions.js index 24ad696f..425f08a6 100644 --- a/src/js/actions/LocalStorageActions.js +++ b/src/js/actions/LocalStorageActions.js @@ -6,6 +6,7 @@ import * as ADDRESS from './constants/address'; import * as TOKEN from './constants/token'; import * as DISCOVERY from './constants/discovery'; import * as STORAGE from './constants/localStorage'; +import * as PENDING from '../actions/constants/pendingTx'; import { JSONRequest, httpRequest } from '../utils/networkUtils'; import type { AsyncAction, GetState, Dispatch } from '../flowtype'; @@ -13,9 +14,9 @@ import type { Config, Coin, TokensCollection } from '../reducers/LocalStorageRed export type StorageAction = { type: typeof STORAGE.READY, - config: any, - tokens: any, - ERC20Abi: any + config: Config, + tokens: TokensCollection, + ERC20Abi: Array } | { type: typeof STORAGE.SAVE, network: string, @@ -119,7 +120,7 @@ export function loadTokensFromJSON(): AsyncAction { const pending: ?string = get('pending'); if (pending) { dispatch({ - type: 'PENDING.FROM_STORAGE', + type: PENDING.FROM_STORAGE, payload: JSON.parse(pending) }) } diff --git a/src/js/actions/ModalActions.js b/src/js/actions/ModalActions.js index 281f1d3b..3ec55692 100644 --- a/src/js/actions/ModalActions.js +++ b/src/js/actions/ModalActions.js @@ -5,7 +5,7 @@ import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect'; import * as MODAL from './constants/modal'; import * as CONNECT from './constants/TrezorConnect'; -import type { AsyncAction, Action, GetState, Dispatch } from '../flowtype'; +import type { AsyncAction, Action, GetState, Dispatch, TrezorDevice } from '../flowtype'; export type ModalAction = { type: typeof MODAL.CLOSE @@ -37,28 +37,28 @@ export const onPassphraseSubmit = (passphrase: string): AsyncAction => { } } -export const askForRemember = (device: any): Action => { - return { - type: MODAL.REMEMBER, - device - } -} +// export const askForRemember = (device: TrezorDevice): Action => { +// return { +// type: MODAL.REMEMBER, +// device +// } +// } -export const onRememberDevice = (device: any): Action => { +export const onRememberDevice = (device: TrezorDevice): Action => { return { type: CONNECT.REMEMBER, device } } -export const onForgetDevice = (device: any): Action => { +export const onForgetDevice = (device: TrezorDevice): Action => { return { type: CONNECT.FORGET, device, } } -export const onForgetSingleDevice = (device: any): Action => { +export const onForgetSingleDevice = (device: TrezorDevice): Action => { return { type: CONNECT.FORGET_SINGLE, device, @@ -71,7 +71,7 @@ export const onCancel = (): Action => { } } -export const onDuplicateDevice = (device: any): AsyncAction => { +export const onDuplicateDevice = (device: TrezorDevice): AsyncAction => { return (dispatch: Dispatch, getState: GetState): void => { dispatch( onCancel() ); @@ -86,7 +86,7 @@ export const onDuplicateDevice = (device: any): AsyncAction => { export default { onPinSubmit, onPassphraseSubmit, - askForRemember, + // askForRemember, onRememberDevice, onForgetDevice, onForgetSingleDevice, diff --git a/src/js/actions/NotificationActions.js b/src/js/actions/NotificationActions.js index e4d455f8..07384db6 100644 --- a/src/js/actions/NotificationActions.js +++ b/src/js/actions/NotificationActions.js @@ -3,7 +3,8 @@ import * as NOTIFICATION from './constants/notification'; -import type { AsyncAction, GetState, Dispatch, RouterLocationState } from '../flowtype'; +import type { Action, AsyncAction, GetState, Dispatch, RouterLocationState } from '../flowtype'; +import type { CallbackAction } from '../reducers/NotificationReducer'; export type NotificationAction = { type: typeof NOTIFICATION.ADD, @@ -12,7 +13,7 @@ export type NotificationAction = { +title: string, +message?: string, +cancelable: boolean, - actions?: Array + actions?: Array } } | { type: typeof NOTIFICATION.CLOSE, @@ -22,8 +23,11 @@ export type NotificationAction = { } } -export const close = () => { - +export const close = (payload: any = {}): Action => { + return { + type: NOTIFICATION.CLOSE, + payload + } } diff --git a/src/js/actions/PendingTxActions.js b/src/js/actions/PendingTxActions.js new file mode 100644 index 00000000..1a3eb0fb --- /dev/null +++ b/src/js/actions/PendingTxActions.js @@ -0,0 +1,15 @@ +/* @flow */ +'use strict'; + +import * as PENDING from './constants/pendingTx'; +import type { State, PendingTx } from '../reducers/PendingTxReducer' + +export type PendingTxAction = { + type: typeof PENDING.FROM_STORAGE, + payload: State +} | { + type: typeof PENDING.TX_RESOLVED, + tx: PendingTx, + receipt: Object, + block: string +} \ No newline at end of file diff --git a/src/js/actions/SendFormActions.js b/src/js/actions/SendFormActions.js index b7081450..1be79bf3 100644 --- a/src/js/actions/SendFormActions.js +++ b/src/js/actions/SendFormActions.js @@ -78,6 +78,10 @@ export type SendFormAction = { } | { type: typeof SEND.DATA_CHANGE, state: State +} | { + type: typeof SEND.SEND, +} | { + type: typeof SEND.TX_ERROR, }; //const numberRegExp = new RegExp('^([0-9]{0,10}\\.)?[0-9]{1,18}$'); diff --git a/src/js/actions/TokenActions.js b/src/js/actions/TokenActions.js index 668e4d5f..8de88489 100644 --- a/src/js/actions/TokenActions.js +++ b/src/js/actions/TokenActions.js @@ -20,15 +20,7 @@ export type TokenAction = { token: Token } | { type: typeof TOKEN.SET_BALANCE, - payload: { - ethAddress: string, - address: string, - } -} - -export const setBalance = (web3: any, coinIndex: number = 0): AsyncAction => { - return async (dispatch: Dispatch, getState: GetState): Promise => { - } + payload: State } type SelectOptions = { @@ -93,15 +85,23 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => { }); const tokenBalance = await getTokenBalanceAsync(web3instance.erc20, token.address, account.address); + dispatch( setBalance(token.address, account.address, tokenBalance.toString()) ) + } +} + +export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => { + return async (dispatch: Dispatch, getState: GetState): Promise => { + const newState: Array = [ ...getState().tokens ]; + let token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress); + if (token) { + token.loaded = true; + token.balance = balance; + } + dispatch({ type: TOKEN.SET_BALANCE, - payload: { - ethAddress: account.address, - address: token.address, - balance: tokenBalance.toString() - } + payload: newState }) - } } diff --git a/src/js/actions/TrezorConnectActions.js b/src/js/actions/TrezorConnectActions.js index 18ea6aa8..20caf845 100644 --- a/src/js/actions/TrezorConnectActions.js +++ b/src/js/actions/TrezorConnectActions.js @@ -38,7 +38,7 @@ import type { export type TrezorConnectAction = { type: typeof CONNECT.INITIALIZATION_ERROR, - error: any + error: string } | { type: typeof CONNECT.SELECT_DEVICE, payload: ?{ @@ -52,40 +52,41 @@ export type TrezorConnectAction = { } } | { type: typeof CONNECT.AUTH_DEVICE, - device: any + device: TrezorDevice } | { type: typeof CONNECT.DUPLICATE, - device: any + device: TrezorDevice } | { type: typeof CONNECT.REMEMBER_REQUEST, - device: any + device: TrezorDevice, + instances: Array } | { type: typeof CONNECT.DISCONNECT_REQUEST, - device: any + device: TrezorDevice } | { type: typeof CONNECT.FORGET_REQUEST, - device: any + device: TrezorDevice } | { type: typeof CONNECT.FORGET, - device: any + device: TrezorDevice } | { type: typeof CONNECT.FORGET_SINGLE, - device: any + device: TrezorDevice } | { type: typeof CONNECT.REMEMBER, - device: any + device: TrezorDevice } | { type: typeof CONNECT.TRY_TO_DUPLICATE, - device: any + device: TrezorDevice } | { type: typeof CONNECT.DEVICE_FROM_STORAGE, - payload: Array + payload: Array } | { type: typeof CONNECT.START_ACQUIRING, - device: any + device: TrezorDevice } | { type: typeof CONNECT.STOP_ACQUIRING, - device: any + device: TrezorDevice }; @@ -349,12 +350,12 @@ export const deviceDisconnect = (device: Device): AsyncAction => { dispatch( DiscoveryActions.stop(selected) ); } - const affected = getState().connect.devices.filter(d => d.features && d.state && !d.remember && d.features.device_id === device.features.device_id); - if (affected.length > 0) { + const instances = getState().connect.devices.filter(d => d.features && d.state && !d.remember && d.features.device_id === device.features.device_id); + if (instances.length > 0) { dispatch({ type: CONNECT.REMEMBER_REQUEST, device, - instances: affected + instances, }); } } @@ -469,7 +470,7 @@ export const gotoDeviceSettings = (device: any): AsyncAction => { } // called from Aside - device menu (forget single instance) -export const forget = (device: any): Action => { +export const forget = (device: TrezorDevice): Action => { return { type: CONNECT.FORGET_REQUEST, device @@ -497,6 +498,7 @@ export const onDuplicateDevice = (): AsyncAction => { export function addAddress(): AsyncAction { return (dispatch: Dispatch, getState: GetState): void => { const selected = findSelectedDevice(getState().connect); + if (!selected) return; dispatch( DiscoveryActions.start(selected, getState().router.location.state.network, true) ); // TODO: network nicer } } diff --git a/src/js/actions/Web3Actions.js b/src/js/actions/Web3Actions.js index 1b2de3d4..283d98e8 100644 --- a/src/js/actions/Web3Actions.js +++ b/src/js/actions/Web3Actions.js @@ -9,6 +9,8 @@ import TrezorConnect from 'trezor-connect'; import { strip } from '../utils/ethUtils'; import * as ADDRESS from './constants/address'; import * as WEB3 from './constants/web3'; +import * as PENDING from './constants/pendingTx'; +import * as AddressActions from '../actions/AddressActions'; import type { Dispatch, @@ -18,13 +20,12 @@ import type { } from '../flowtype'; import type { Account } from '../reducers/AccountsReducer'; +import type { PendingTx } from '../reducers/PendingTxReducer'; export type Web3Action = { type: typeof WEB3.READY, -} | { - type: typeof WEB3.PENDING_TX_RESOLVED -} | Web3CreateAction - | Web3UpdateBlockAction +} | Web3CreateAction + | Web3UpdateBlockAction | Web3UpdateGasPriceAction; export type Web3CreateAction = { @@ -261,12 +262,11 @@ export function getBalance(account: Account): AsyncAction { if (!error) { const newBalance: string = web3.fromWei(balance.toString(), 'ether'); if (account.balance !== newBalance) { - dispatch({ - type: ADDRESS.SET_BALANCE, - address: account.address, - network: account.network, - balance: newBalance - }); + dispatch(AddressActions.setBalance( + account.address, + account.network, + newBalance + )); // dispatch( loadHistory(addr) ); } @@ -282,7 +282,7 @@ export function getNonce(account: Account): AsyncAction { const web3instance = getState().web3.filter(w3 => w3.network === account.network)[0]; const web3 = web3instance.web3; - web3.eth.getTransactionCount(account.address, (error, result) => { + web3.eth.getTransactionCount(account.address, (error: Error, result: number) => { if (!error) { if (account.nonce !== result) { dispatch({ @@ -297,7 +297,7 @@ export function getNonce(account: Account): AsyncAction { } } -export function getTransactionReceipt(tx: any): AsyncAction { +export const getTransactionReceipt = (tx: PendingTx): AsyncAction => { return async (dispatch: Dispatch, getState: GetState): Promise => { const web3instance = getState().web3.filter(w3 => w3.network === tx.network)[0]; @@ -305,12 +305,10 @@ export function getTransactionReceipt(tx: any): AsyncAction { //web3.eth.getTransactionReceipt(txid, (error, tx) => { web3.eth.getTransaction(tx.id, (error, receipt) => { - console.log("RECEIP", receipt) if (receipt && receipt.blockNumber) { web3.eth.getBlock(receipt.blockHash, (error, block) => { - console.log("---MAMM BLOCK", error, block, receipt, receipt.blockHash) dispatch({ - type: WEB3.PENDING_TX_RESOLVED, + type: PENDING.TX_RESOLVED, tx, receipt, block @@ -359,7 +357,7 @@ export const getTokenBalanceAsync = (erc20: any, token: string, address: string) return new Promise((resolve, reject) => { const contr = erc20.at(token); - contr.balanceOf(address, (error, result) => { + contr.balanceOf(address, (error: Error, result) => { if (error) { reject(error); } else { @@ -369,9 +367,9 @@ export const getTokenBalanceAsync = (erc20: any, token: string, address: string) }); } -export const getNonceAsync = (web3: Web3, address: string): Promise => { +export const getNonceAsync = (web3: Web3, address: string): Promise => { return new Promise((resolve, reject) => { - web3.eth.getTransactionCount(address, (error, result) => { + web3.eth.getTransactionCount(address, (error: Error, result: number) => { if (error) { reject(error); } else { diff --git a/src/js/actions/constants/pendingTx.js b/src/js/actions/constants/pendingTx.js new file mode 100644 index 00000000..a6b654a1 --- /dev/null +++ b/src/js/actions/constants/pendingTx.js @@ -0,0 +1,5 @@ +/* @flow */ +'use strict'; + +export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage'; +export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved'; \ No newline at end of file diff --git a/src/js/components/common/Notification.js b/src/js/components/common/Notification.js index 8bd37ded..09b4029c 100644 --- a/src/js/components/common/Notification.js +++ b/src/js/components/common/Notification.js @@ -6,6 +6,7 @@ import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import * as NOTIFICATION from '../../actions/constants/notification'; +import * as NotificationActions from '../../actions/NotificationActions'; import type { Action, State, Dispatch } from '../../flowtype'; type Props = { @@ -20,7 +21,7 @@ type NProps = { title: string; message?: string; actions?: Array; - close?: (notif?: any) => Action + close?: typeof NotificationActions.close } export const Notification = (props: NProps): React$Element => { @@ -77,12 +78,7 @@ export default connect( }, (dispatch: Dispatch) => { return { - close: bindActionCreators((notif?: any): Action => { - return { - type: NOTIFICATION.CLOSE, - payload: notif - } - }, dispatch), + close: bindActionCreators(NotificationActions.close, dispatch), }; } )(NotificationGroup); \ No newline at end of file diff --git a/src/js/flowtype/index.js b/src/js/flowtype/index.js index cc665bf6..8b9a1ce1 100644 --- a/src/js/flowtype/index.js +++ b/src/js/flowtype/index.js @@ -3,10 +3,9 @@ import type { Store as ReduxStore, - StoreEnhancer as ReduxStoreEnhancer, Dispatch as ReduxDispatch, - Middleware as ReduxMiddleware, MiddlewareAPI as ReduxMiddlewareAPI, + Middleware as ReduxMiddleware, ThunkAction as ReduxThunkAction, ThunkDispatch as ReduxThunkDispatch, PlainDispatch as ReduxPlainDispatch @@ -22,6 +21,7 @@ import type { StorageAction } from '../actions/LocalStorageActions'; import type { LogAction } from '../actions/LogActions'; import type { ModalAction } from '../actions/ModalActions'; import type { NotificationAction } from '../actions/NotificationActions'; +import type { PendingTxAction } from '../actions/PendingTxActions'; import type { ReceiveAction } from '../actions/ReceiveActions'; import type { SendFormAction } from '../actions/SendFormActions'; import type { SummaryAction } from '../actions/SummaryActions'; @@ -58,6 +58,22 @@ export type TrezorDevice = { ts: number; } +export type AcquiredDevice = { + remember: boolean; + connected: boolean; + available: boolean; // device cannot be used because of features.passphrase_protection is different then expected (saved) + path: string; + label: string; + state: string; + instance: ?number; + instanceLabel: string; + features: Features; + acquiring: boolean; + isUsedElsewhere?: boolean; + featuresNeedsReload?: boolean; + ts: number; +} + export type RouterLocationState = LocationState; // Cast event from TrezorConnect event listener to react Action @@ -96,6 +112,7 @@ export type Action = | LogAction | ModalAction | NotificationAction + | PendingTxAction | ReceiveAction | SendFormAction | SummaryAction @@ -111,7 +128,6 @@ export type Accounts = $ElementType; export type LocalStorage = $ElementType; export type Config = $PropertyType<$ElementType, 'config'>; - export type Dispatch = ReduxDispatch; export type MiddlewareDispatch = ReduxPlainDispatch; @@ -119,8 +135,5 @@ export type MiddlewareAPI = ReduxMiddlewareAPI; export type Middleware = ReduxMiddleware; export type Store = ReduxStore; -export type StoreEnhancer = ReduxStoreEnhancer; - export type GetState = () => State; - export type AsyncAction = ReduxThunkAction; diff --git a/src/js/flowtype/redux_v3.x.x.1.js b/src/js/flowtype/redux_v3.x.x.1.js new file mode 100644 index 00000000..a0b7e4db --- /dev/null +++ b/src/js/flowtype/redux_v3.x.x.1.js @@ -0,0 +1,74 @@ +// flow-typed signature: cca4916b0213065533df8335c3285a4a +// flow-typed version: cab04034e7/redux_v3.x.x/flow_>=v0.55.x + +declare module 'redux' { + + /* + + S = State + A = Action + D = Dispatch + + */ + + declare export type DispatchAPI = (action: A) => A; + // declare export type Dispatch }> = DispatchAPI; + /* NEW: Dispatch is now a combination of these different dispatch types */ + declare export type Dispatch = PlainDispatch & ThunkDispatch; + + // declare export type ThunkAction = (dispatch: Dispatch, getState: () => S) => Promise | void; + //declare export type ThunkAction = (dispatch: PlainDispatch | ThunkDispatch, getState: () => S) => Promise | void; + declare export type ThunkAction = (dispatch: ThunkDispatch, getState: () => S) => Promise | void; + //declare export type ThunkDispatch = (action: ThunkAction) => void; + declare export type ThunkDispatch = (action: PlainDispatch | ThunkDispatch) => void; + + declare export type PlainDispatch}> = DispatchAPI; + + // declare export type ThunkAction = (dispatch: D, getState: () => S) => Promise | void; + // declare type ThunkDispatch = (action: ThunkAction>) => void; + + declare export type MiddlewareAPI = { + dispatch: ThunkDispatch; + // dispatch: (action: PlainDispatch | ThunkDispatch) => void; + getState(): S; + }; + + declare export type Middleware = + (api: MiddlewareAPI) => + (next: PlainDispatch) => PlainDispatch; + + declare export type Store> = { + // rewrite MiddlewareAPI members in order to get nicer error messages (intersections produce long messages) + dispatch: D; + getState(): S; + subscribe(listener: () => void): () => void; + replaceReducer(nextReducer: Reducer): void + }; + + declare export type Reducer = (state: S | void, action: A) => S; + + declare export type CombinedReducer = (state: $Shape & {} | void, action: A) => S; + + declare export type StoreCreator> = { + (reducer: Reducer, enhancer?: StoreEnhancer): Store; + (reducer: Reducer, preloadedState: S, enhancer?: StoreEnhancer): Store; + }; + + declare export type StoreEnhancer> = (next: StoreCreator) => StoreCreator; + + declare export function createStore(reducer: Reducer, enhancer?: StoreEnhancer): Store; + declare export function createStore(reducer: Reducer, preloadedState?: S, enhancer?: StoreEnhancer): Store; + + declare export function applyMiddleware(...middlewares: Array>): StoreEnhancer; + + declare export type ActionCreator = (...args: Array) => A; + declare export type ActionCreators = { [key: K]: ActionCreator }; + + declare export function bindActionCreators, D: DispatchAPI>(actionCreator: C, dispatch: D): C; + declare export function bindActionCreators, D: DispatchAPI>(actionCreators: C, dispatch: D): C; + + declare export function combineReducers(reducers: O): CombinedReducer<$ObjMap(r: Reducer) => S>, A>; + + declare export var compose: $Compose; + } + \ No newline at end of file diff --git a/src/js/reducers/AccountsReducer.js b/src/js/reducers/AccountsReducer.js index b41275b3..7a1c236d 100644 --- a/src/js/reducers/AccountsReducer.js +++ b/src/js/reducers/AccountsReducer.js @@ -4,6 +4,13 @@ import * as CONNECT from '../actions/constants/TrezorConnect'; import * as ADDRESS from '../actions/constants/address'; +import type { Action, TrezorDevice } from '../flowtype'; +import type { + AddressCreateAction, + AddressSetBalanceAction, + AddressSetNonceAction +} from '../actions/AddressActions'; + export type Account = { loaded: boolean; +network: string; @@ -16,17 +23,19 @@ export type Account = { nonce: number; } -const initialState: Array = []; +export type State = Array; + +const initialState: State = []; -export const findAccount = (state: Array, index: number, deviceState: string, network: string): ?Account => { +export const findAccount = (state: State, index: number, deviceState: string, network: string): ?Account => { return state.find(a => a.deviceState === deviceState && a.index === index && a.network === network); } -const createAccount = (state: Array, action: any): Array => { +const createAccount = (state: State, action: AddressCreateAction): State => { // TODO check with device_id // check if account was created before - const exist: ?Account = state.find((account: Account) => account.address === action.address && account.network === action.network && account.deviceID === action.device.features.device_id); + const exist: ?Account = state.find((account: Account) => account.address === action.address && account.network === action.network && action.device.features && account.deviceID === action.device.features.device_id); if (exist) { return state; } @@ -34,7 +43,7 @@ const createAccount = (state: Array, action: any): Array => { const address: Account = { loaded: false, network: action.network, - deviceID: action.device.features.device_id, + deviceID: action.device.features ? action.device.features.device_id : '0', deviceState: action.device.state, index: action.index, addressPath: action.path, @@ -43,36 +52,36 @@ const createAccount = (state: Array, action: any): Array => { nonce: 0, } - const newState: Array = [ ...state ]; + const newState: State = [ ...state ]; newState.push(address); return newState; } -const removeAccounts = (state: Array, action: any): Array => { - return state.filter(account => account.deviceID !== action.device.features.device_id); +const removeAccounts = (state: State, device: TrezorDevice): State => { + return state.filter(account => device.features && account.deviceID !== device.features.device_id); } -const forgetAccounts = (state: Array, action: any): Array => { - return state.filter(account => action.accounts.indexOf(account) === -1); -} +// const forgetAccounts = (state: State, action: any): State => { +// return state.filter(account => action.accounts.indexOf(account) === -1); +// } -const setBalance = (state: Array, action: any): Array => { +const setBalance = (state: State, action: AddressSetBalanceAction): State => { const index: number = state.findIndex(account => account.address === action.address && account.network === action.network); - const newState: Array = [ ...state ]; + const newState: State = [ ...state ]; newState[index].loaded = true; newState[index].balance = action.balance; return newState; } -const setNonce = (state: Array, action: any): Array => { +const setNonce = (state: State, action: AddressSetNonceAction): State => { const index: number = state.findIndex(account => account.address === action.address && account.network === action.network); - const newState: Array = [ ...state ]; + const newState: State = [ ...state ]; newState[index].loaded = true; newState[index].nonce = action.nonce; return newState; } -export default (state: Array = initialState, action: any): Array => { +export default (state: State = initialState, action: Action): State => { switch (action.type) { @@ -81,7 +90,7 @@ export default (state: Array = initialState, action: any): Array; deviceState: string; accountIndex: number; interrupted: boolean; @@ -18,28 +32,29 @@ export type Discovery = { waitingForAuth?: boolean; } -const initialState: Array = []; +export type State = Array; +const initialState: State = []; -const findIndex = (state: Array, network: string, deviceState: string): number => { +const findIndex = (state: State, network: string, deviceState: string): number => { return state.findIndex(d => d.network === network && d.deviceState === deviceState); } -const start = (state: Array, action: any): Array => { - +const start = (state: State, action: DiscoveryStartAction): State => { + const deviceState: string = action.device.state || '0'; const instance: Discovery = { network: action.network, xpub: action.xpub, hdKey: action.hdKey, basePath: action.basePath, - deviceState: action.device.state, + deviceState, accountIndex: 0, interrupted: false, completed: false, waitingForDevice: false } - const newState: Array = [ ...state ]; - const index: number = findIndex(state, action.network, action.device.state); + const newState: State = [ ...state ]; + const index: number = findIndex(state, action.network, deviceState); if (index >= 0) { newState[index] = instance; } else { @@ -48,26 +63,26 @@ const start = (state: Array, action: any): Array => { return newState; } -const complete = (state: Array, action: any): Array => { - const index: number = findIndex(state, action.network, action.device.state); - const newState: Array = [ ...state ]; +const complete = (state: State, action: DiscoveryCompleteAction): State => { + const index: number = findIndex(state, action.network, action.device.state || '0'); + const newState: State = [ ...state ]; newState[index].completed = true; return newState; } -const addressCreate = (state: Array, action: any): Array => { - const index: number = findIndex(state, action.network, action.device.state); - const newState: Array = [ ...state ]; +const addressCreate = (state: State, action: AddressCreateAction): State => { + const index: number = findIndex(state, action.network, action.device.state || '0'); + const newState: State = [ ...state ]; newState[index].accountIndex++; return newState; } -const forgetDiscovery = (state: Array, action: any): Array => { - return state.filter(d => d.deviceState !== action.device.state); +const forgetDiscovery = (state: State, device: TrezorDevice): State => { + return state.filter(d => d.deviceState !== device.state); } -const stop = (state: Array, action: any): Array => { - const newState: Array = [ ...state ]; +const stop = (state: State, action: DiscoveryStopAction): State => { + const newState: State = [ ...state ]; return newState.map( (d: Discovery) => { if (d.deviceState === action.device.state && !d.completed) { d.interrupted = true; @@ -77,22 +92,23 @@ const stop = (state: Array, action: any): Array => { }); } -const waiting = (state: Array, action: any): Array => { +const waiting = (state: State, action: DiscoveryWaitingAction): State => { + const deviceState: string = action.device.state || '0'; const instance: Discovery = { network: action.network, - deviceState: action.device.state, + deviceState, xpub: '', hdKey: null, - basePath: null, + basePath: [], accountIndex: 0, interrupted: false, completed: false, waitingForDevice: true } - const index: number = findIndex(state, action.network, action.device.state); - const newState: Array = [ ...state ]; + const index: number = findIndex(state, action.network, deviceState); + const newState: State = [ ...state ]; if (index >= 0) { newState[index] = instance; } else { @@ -102,7 +118,7 @@ const waiting = (state: Array, action: any): Array => { return newState; } -export default function discovery(state: Array = initialState, action: any): Array { +export default function discovery(state: State = initialState, action: Action): State { switch (action.type) { case DISCOVERY.START : @@ -125,7 +141,7 @@ export default function discovery(state: Array = initialState, action }) case CONNECT.FORGET : case CONNECT.FORGET_SINGLE : - return forgetDiscovery(state, action); + return forgetDiscovery(state, action.device); default: return state; diff --git a/src/js/reducers/ModalReducer.js b/src/js/reducers/ModalReducer.js index 64ad7975..295901de 100644 --- a/src/js/reducers/ModalReducer.js +++ b/src/js/reducers/ModalReducer.js @@ -6,10 +6,12 @@ import * as RECEIVE from '../actions/constants/receive'; import * as MODAL from '../actions/constants/modal'; import * as CONNECT from '../actions/constants/TrezorConnect'; +import type { Action, TrezorDevice } from '../flowtype'; + export type State = { opened: boolean; - device: any; - instances: ?Array; + device: ?TrezorDevice; + instances: ?Array; windowType: ?string; } @@ -20,7 +22,7 @@ const initialState: State = { windowType: null }; -export default function modal(state: State = initialState, action: any): State { +export default function modal(state: State = initialState, action: Action): State { switch (action.type) { @@ -33,12 +35,18 @@ export default function modal(state: State = initialState, action: any): State { } case CONNECT.REMEMBER_REQUEST : + return { + ...state, + device: action.device, + instances: action.instances, + opened: true, + windowType: action.type + }; case CONNECT.FORGET_REQUEST : case CONNECT.DISCONNECT_REQUEST : return { ...state, device: action.device, - instances: action.instances, opened: true, windowType: action.type }; @@ -104,8 +112,6 @@ export default function modal(state: State = initialState, action: any): State { ...initialState, }; - - default: return state; } diff --git a/src/js/reducers/NotificationReducer.js b/src/js/reducers/NotificationReducer.js index 97a78c51..f46e9024 100644 --- a/src/js/reducers/NotificationReducer.js +++ b/src/js/reducers/NotificationReducer.js @@ -5,22 +5,26 @@ import { LOCATION_CHANGE } from 'react-router-redux'; import * as NOTIFICATION from '../actions/constants/notification'; import { DEVICE } from 'trezor-connect'; -type NotificationAction = { +import type { Action } from '../flowtype'; + +export type CallbackAction = { label: string; callback: Function; } -type NotificationEntry = { +export type NotificationEntry = { +id: ?string; +devicePath: ?string; +type: string; +title: string; +message: string; +cancelable: boolean; - +actions: Array; + +actions: Array; } -const initialState: Array = [ +export type State = Array; + +const initialState: State = [ // { // id: undefined, // type: "info", @@ -31,8 +35,8 @@ const initialState: Array = [ // } ]; -const addNotification = (state: Array, payload: any): Array => { - const newState: Array = state.filter(e => !e.cancelable); +const addNotification = (state: State, payload: any): State => { + const newState: State = state.filter(e => !e.cancelable); newState.push({ id: payload.id, devicePath: payload.devicePath, @@ -47,7 +51,7 @@ const addNotification = (state: Array, payload: any): Array, payload: any): Array => { +const closeNotification = (state: State, payload: any): State => { if (payload && typeof payload.id === 'string') { return state.filter(entry => entry.id !== payload.id); } else if (payload && typeof payload.devicePath === 'string') { @@ -57,9 +61,13 @@ const closeNotification = (state: Array, payload: any): Array } } -export default function notification(state: Array = initialState, action: Object): Array { +export default function notification(state: State = initialState, action: Action): State { switch(action.type) { + case DEVICE.DISCONNECT : + const path: string = action.device.path; // Flow warning + return state.filter(entry => entry.devicePath !== path); + case NOTIFICATION.ADD : return addNotification(state, action.payload); @@ -67,9 +75,6 @@ export default function notification(state: Array = initialSt case NOTIFICATION.CLOSE : return closeNotification(state, action.payload); - case DEVICE.DISCONNECT : - return state.filter(entry => entry.devicePath !== action.device.path); - default: return state; } diff --git a/src/js/reducers/PendingTxReducer.js b/src/js/reducers/PendingTxReducer.js index 9c4e5a0e..7dd0fb85 100644 --- a/src/js/reducers/PendingTxReducer.js +++ b/src/js/reducers/PendingTxReducer.js @@ -1,9 +1,12 @@ /* @flow */ 'use strict'; +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'; + export type PendingTx = { +id: string; +network: string; @@ -12,9 +15,11 @@ export type PendingTx = { +address: string; } -const initialState: Array = []; +export type State = Array; + +const initialState: State = []; -const add = (state: Array, action: any) => { +const add = (state: State, action: any) => { const newState = [ ...state ]; newState.push({ id: action.txid, @@ -26,25 +31,25 @@ const add = (state: Array, action: any) => { return newState; } -const remove = (state: Array, action: any) => { +const remove = (state: State, action: any) => { return state.filter(tx => tx.id !== action.tx.id); } -const fromStorage = (state: Array, action: any) => { +const fromStorage = (state: State, action: any) => { return state.filter(tx => tx.id !== action.tx.id); } -export default function pending(state: Array = initialState, action: any): Array { +export default function pending(state: State = initialState, action: Action): State { switch (action.type) { case SEND.TX_COMPLETE : return add(state, action); - case WEB3.PENDING_TX_RESOLVED : + case PENDING.TX_RESOLVED : return remove(state, action); - case 'PENDING.FROM_STORAGE' : + case PENDING.FROM_STORAGE : return action.payload; default: diff --git a/src/js/reducers/ReceiveReducer.js b/src/js/reducers/ReceiveReducer.js index 54c772a9..71eee86c 100644 --- a/src/js/reducers/ReceiveReducer.js +++ b/src/js/reducers/ReceiveReducer.js @@ -2,6 +2,7 @@ 'use strict'; import * as RECEIVE from '../actions/constants/receive'; +import type { Action } from '../flowtype'; export type State = { addressVerified: boolean; @@ -13,7 +14,7 @@ export const initialState: State = { addressUnverified: false, }; -export default (state: State = initialState, action: any): State => { +export default (state: State = initialState, action: Action): State => { switch (action.type) { diff --git a/src/js/reducers/SendFormReducer.js b/src/js/reducers/SendFormReducer.js index 1d88dd09..5fcbb77b 100644 --- a/src/js/reducers/SendFormReducer.js +++ b/src/js/reducers/SendFormReducer.js @@ -8,6 +8,7 @@ import EthereumjsUnits from 'ethereumjs-units'; import BigNumber from 'bignumber.js'; import { getFeeLevels } from '../actions/SendFormActions'; +import type { Action } from '../flowtype'; import type { Web3CreateAction, Web3UpdateBlockAction, @@ -122,7 +123,7 @@ const onBalanceUpdated = (state: State, action: any): State => { } -export default (state: State = initialState, action: any): State => { +export default (state: State = initialState, action: Action): State => { switch (action.type) { diff --git a/src/js/reducers/TokensReducer.js b/src/js/reducers/TokensReducer.js index 2ac9010c..77b6304b 100644 --- a/src/js/reducers/TokensReducer.js +++ b/src/js/reducers/TokensReducer.js @@ -26,28 +26,18 @@ export const findToken = (state: Array, address: string, symbol: string, return state.find(t => t.ethAddress === address && t.symbol === symbol && t.deviceState === deviceState); } -const setBalance = (state: State, payload: any): State => { +// const setBalance = (state: State, payload: any): State => { +// const newState: Array = [ ...state ]; +// let index: number = state.findIndex(t => t.address === payload.address && t.ethAddress === payload.ethAddress); +// if (index >= 0) { +// newState[index].loaded = true; +// newState[index].balance = payload.balance; +// } +// return newState; +// } + +const create = (state: State, token: Token): State => { const newState: Array = [ ...state ]; - let index: number = state.findIndex(t => t.address === payload.address && t.ethAddress === payload.ethAddress); - if (index >= 0) { - newState[index].loaded = true; - newState[index].balance = payload.balance; - } - return newState; -} - -const create = (state: State, payload: any): State => { - const newState: Array = [ ...state ]; - const token: Token = { - loaded: false, - deviceState: payload.deviceState, - name: payload.name, - symbol: payload.symbol, - address: payload.address, - ethAddress: payload.ethAddress, - decimals: payload.decimals, - balance: '0' - } newState.push(token); return newState; } @@ -74,7 +64,7 @@ export default (state: State = initialState, action: Action): State => { case TOKEN.REMOVE : return remove(state, action.token); case TOKEN.SET_BALANCE : - return setBalance(state, action.payload); + return action.payload; case CONNECT.FORGET : case CONNECT.FORGET_SINGLE : diff --git a/src/js/reducers/index.js b/src/js/reducers/index.js index 641c684a..083fce08 100644 --- a/src/js/reducers/index.js +++ b/src/js/reducers/index.js @@ -41,24 +41,8 @@ const reducers = { wallet } - export type Reducers = typeof reducers; type $ExtractFunctionReturn = (v: (...args: any) => V) => V; export type ReducersState = $ObjMap; - - - export default combineReducers(reducers); - -// export type Reducers = { -// router: any; -// log: any; - -// connect: TrezorConnectState; -// accounts: Array; -// tokens: Array; -// pending: Array; -// discovery: Array; -// localStorage: LocalStorageState; -// } \ No newline at end of file diff --git a/src/js/services/LocalStorageService.js b/src/js/services/LocalStorageService.js index e360c234..88753163 100644 --- a/src/js/services/LocalStorageService.js +++ b/src/js/services/LocalStorageService.js @@ -11,6 +11,7 @@ import * as ADDRESS from '../actions/constants/address'; 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 { LOCATION_CHANGE } from 'react-router-redux'; import type { @@ -131,7 +132,7 @@ const LocalStorageService: Middleware = (api: MiddlewareAPI) => (next: Middlewar break; case SEND.TX_COMPLETE : - case WEB3.PENDING_TX_RESOLVED : + case PENDING.TX_RESOLVED : save(api.dispatch, api.getState); break; diff --git a/src/js/services/TrezorConnectService.js b/src/js/services/TrezorConnectService.js index 45986541..9884b4d3 100644 --- a/src/js/services/TrezorConnectService.js +++ b/src/js/services/TrezorConnectService.js @@ -105,7 +105,7 @@ const TrezorConnectService: Middleware = (api: MiddlewareAPI) => (next: Middlewa // TODO: move it to modal actions const { modal } = api.getState(); if (modal.opened && modal.windowType === CONNECT.REMEMBER_REQUEST) { - if (action.device.features && modal.device.features.device_id === action.device.features.device_id) { + if (action.device.features && modal.device && modal.device.features && modal.device.features.device_id === action.device.features.device_id) { api.dispatch({ type: MODAL.CLOSE, });