diff --git a/.eslintrc b/.eslintrc index a9e8a81f..b8139df6 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,10 +3,10 @@ "eslint-config-airbnb", "prettier", "prettier/babel", + "plugin:flowtype/recommended", "prettier/flowtype", "prettier/react", "plugin:jest/recommended", - "plugin:flowtype/recommended", "plugin:jest/recommended" ], "globals": { diff --git a/package.json b/package.json index 52deacee..89844a6e 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "lint": "run-s lint:*", "lint:js": "npx eslint ./src ./webpack", "lint:css": "npx stylelint './src/**/*.js'", - "lint:prettier": "pretty-quick", + "lint:prettier": "npx pretty-quick", + "lint-fix": "npx eslint ./src ./webpack --fix", "prettier:check": "eslint --print-config ./src | eslint-config-prettier-check", "test": "run-s test:*", "test:unit": "npx jest", @@ -142,4 +143,4 @@ "optionalDependencies": { "fsevents": "1.2.7" } -} \ No newline at end of file +} diff --git a/src/actions/AccountsActions.js b/src/actions/AccountsActions.js index 6b918df8..ee59d816 100644 --- a/src/actions/AccountsActions.js +++ b/src/actions/AccountsActions.js @@ -4,13 +4,15 @@ import * as ACCOUNT from 'actions/constants/account'; import type { Action } from 'flowtype'; import type { Account, State } from 'reducers/AccountsReducer'; -export type AccountAction = { - type: typeof ACCOUNT.FROM_STORAGE, - payload: State -} | { - type: typeof ACCOUNT.CREATE | typeof ACCOUNT.UPDATE, - payload: Account, -}; +export type AccountAction = + | { + type: typeof ACCOUNT.FROM_STORAGE, + payload: State, + } + | { + type: typeof ACCOUNT.CREATE | typeof ACCOUNT.UPDATE, + payload: Account, + }; export const update = (account: Account): Action => ({ type: ACCOUNT.UPDATE, diff --git a/src/actions/BlockchainActions.js b/src/actions/BlockchainActions.js index f3e31755..afb967bf 100644 --- a/src/actions/BlockchainActions.js +++ b/src/actions/BlockchainActions.js @@ -4,35 +4,35 @@ import * as BLOCKCHAIN from 'actions/constants/blockchain'; import * as EthereumBlockchainActions from 'actions/ethereum/BlockchainActions'; import * as RippleBlockchainActions from 'actions/ripple/BlockchainActions'; -import type { - Dispatch, - GetState, - PromiseAction, - BlockchainFeeLevel, -} from 'flowtype'; +import type { Dispatch, GetState, PromiseAction, BlockchainFeeLevel } from 'flowtype'; import type { BlockchainBlock, BlockchainNotification, BlockchainError } from 'trezor-connect'; - -export type BlockchainAction = { - type: typeof BLOCKCHAIN.READY, -} | { - type: typeof BLOCKCHAIN.UPDATE_FEE, - shortcut: string, - feeLevels: Array, -} | { - type: typeof BLOCKCHAIN.START_SUBSCRIBE, - shortcut: string, -} +export type BlockchainAction = + | { + type: typeof BLOCKCHAIN.READY, + } + | { + type: typeof BLOCKCHAIN.UPDATE_FEE, + shortcut: string, + feeLevels: Array, + } + | { + type: typeof BLOCKCHAIN.START_SUBSCRIBE, + shortcut: string, + }; // Conditionally subscribe to blockchain backend // called after TrezorConnect.init successfully emits TRANSPORT.START event // checks if there are discovery processes loaded from LocalStorage // if so starts subscription to proper networks -export const init = (): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const init = (): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { if (getState().discovery.length > 0) { // get unique networks const networks: Array = []; - getState().discovery.forEach((discovery) => { + getState().discovery.forEach(discovery => { if (networks.indexOf(discovery.network) < 0) { networks.push(discovery.network); } @@ -50,7 +50,10 @@ export const init = (): PromiseAction => async (dispatch: Dispatch, getSta }); }; -export const subscribe = (networkName: string): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const subscribe = (networkName: string): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const { config } = getState().localStorage; const network = config.networks.find(c => c.shortcut === networkName); if (!network) return; @@ -67,11 +70,14 @@ export const subscribe = (networkName: string): PromiseAction => async (di case 'ripple': await dispatch(RippleBlockchainActions.subscribe(networkName)); break; - default: break; + default: + break; } }; -export const onBlockMined = (payload: $ElementType): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onBlockMined = ( + payload: $ElementType +): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const shortcut = payload.coin.shortcut.toLowerCase(); if (getState().router.location.state.network !== shortcut) return; @@ -86,11 +92,14 @@ export const onBlockMined = (payload: $ElementType): case 'ripple': await dispatch(RippleBlockchainActions.onBlockMined(shortcut)); break; - default: break; + default: + break; } }; -export const onNotification = (payload: $ElementType): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onNotification = ( + payload: $ElementType +): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const shortcut = payload.coin.shortcut.toLowerCase(); const { config } = getState().localStorage; const network = config.networks.find(c => c.shortcut === shortcut); @@ -104,13 +113,16 @@ export const onNotification = (payload: $ElementType): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onError = ( + payload: $ElementType +): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const shortcut = payload.coin.shortcut.toLowerCase(); const { config } = getState().localStorage; const network = config.networks.find(c => c.shortcut === shortcut); @@ -124,6 +136,7 @@ export const onError = (payload: $ElementType): Prom // this error is handled in BlockchainReducer // await dispatch(RippleBlockchainActions.onBlockMined(shortcut)); break; - default: break; + default: + break; } -}; \ No newline at end of file +}; diff --git a/src/actions/DiscoveryActions.js b/src/actions/DiscoveryActions.js index 5feebc38..22ca82a0 100644 --- a/src/actions/DiscoveryActions.js +++ b/src/actions/DiscoveryActions.js @@ -20,63 +20,85 @@ import * as BlockchainActions from './BlockchainActions'; import * as EthereumDiscoveryActions from './ethereum/DiscoveryActions'; import * as RippleDiscoveryActions from './ripple/DiscoveryActions'; -export type DiscoveryStartAction = EthereumDiscoveryActions.DiscoveryStartAction | RippleDiscoveryActions.DiscoveryStartAction; +export type DiscoveryStartAction = + | EthereumDiscoveryActions.DiscoveryStartAction + | RippleDiscoveryActions.DiscoveryStartAction; export type DiscoveryWaitingAction = { - type: typeof DISCOVERY.WAITING_FOR_DEVICE | typeof DISCOVERY.WAITING_FOR_BLOCKCHAIN | typeof DISCOVERY.FIRMWARE_NOT_SUPPORTED | typeof DISCOVERY.FIRMWARE_OUTDATED, + type: + | typeof DISCOVERY.WAITING_FOR_DEVICE + | typeof DISCOVERY.WAITING_FOR_BLOCKCHAIN + | typeof DISCOVERY.FIRMWARE_NOT_SUPPORTED + | typeof DISCOVERY.FIRMWARE_OUTDATED, device: TrezorDevice, network: string, -} +}; export type DiscoveryCompleteAction = { type: typeof DISCOVERY.COMPLETE, device: TrezorDevice, network: string, -} - -export type DiscoveryAction = { - type: typeof DISCOVERY.FROM_STORAGE, - payload: State -} | { - type: typeof DISCOVERY.STOP, - device: TrezorDevice -} | DiscoveryStartAction - | DiscoveryWaitingAction - | DiscoveryCompleteAction; +}; + +export type DiscoveryAction = + | { + type: typeof DISCOVERY.FROM_STORAGE, + payload: State, + } + | { + type: typeof DISCOVERY.STOP, + device: TrezorDevice, + } + | DiscoveryStartAction + | DiscoveryWaitingAction + | DiscoveryCompleteAction; // There are multiple async methods during discovery process (trezor-connect and blockchain actions) // This method will check after each of async action if process was interrupted (for example by network change or device disconnect) -const isProcessInterrupted = (process?: Discovery): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { +const isProcessInterrupted = (process?: Discovery): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): boolean => { if (!process) { return false; } const { deviceState, network } = process; - const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === deviceState && d.network === network); + const discoveryProcess: ?Discovery = getState().discovery.find( + d => d.deviceState === deviceState && d.network === network + ); if (!discoveryProcess) return false; return discoveryProcess.interrupted; }; // Private action // Called from "this.begin", "this.restore", "this.addAccount" -const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const selected = getState().wallet.selectedDevice; if (!selected) { // TODO: throw error console.error('Start discovery: no selected device', device); return; - } if (selected.path !== device.path) { + } + if (selected.path !== device.path) { console.error('Start discovery: requested device is not selected', device, selected); return; - } if (!selected.state) { + } + if (!selected.state) { console.warn("Start discovery: Selected device wasn't authenticated yet..."); return; - } if (selected.connected && !selected.available) { + } + if (selected.connected && !selected.available) { console.warn('Start discovery: Selected device is unavailable...'); return; } const { discovery } = getState(); - const discoveryProcess: ?Discovery = discovery.find(d => d.deviceState === device.state && d.network === network); + const discoveryProcess: ?Discovery = discovery.find( + d => d.deviceState === device.state && d.network === network + ); if (!selected.connected && (!discoveryProcess || !discoveryProcess.completed)) { dispatch({ @@ -105,7 +127,11 @@ const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean) device, network, }); - } else if (discoveryProcess.interrupted || discoveryProcess.waitingForDevice || discoveryProcess.waitingForBlockchain) { + } else if ( + discoveryProcess.interrupted || + discoveryProcess.waitingForDevice || + discoveryProcess.waitingForBlockchain + ) { // discovery cycle was interrupted // start from beginning dispatch(begin(device, network)); @@ -117,7 +143,10 @@ const start = (device: TrezorDevice, network: string, ignoreCompleted?: boolean) // first iteration // generate public key for this account // start discovery process -const begin = (device: TrezorDevice, networkName: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +const begin = (device: TrezorDevice, networkName: string): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const { config } = getState().localStorage; const network = config.networks.find(c => c.shortcut === networkName); if (!network) return; @@ -165,7 +194,9 @@ const begin = (device: TrezorDevice, networkName: string): AsyncAction => async // check for interruption // corner case: DISCOVERY.START wasn't called yet, but Discovery exists in reducer created by DISCOVERY.WAITING_FOR_DEVICE action // this is why we need to get process instance directly from reducer - const discoveryProcess = getState().discovery.find(d => d.deviceState === device.state && d.network === network); + const discoveryProcess = getState().discovery.find( + d => d.deviceState === device.state && d.network === network + ); if (dispatch(isProcessInterrupted(discoveryProcess))) return; // send data to reducer @@ -175,7 +206,10 @@ const begin = (device: TrezorDevice, networkName: string): AsyncAction => async dispatch(start(device, networkName)); }; -const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const { config } = getState().localStorage; const network = config.networks.find(c => c.shortcut === discoveryProcess.network); if (!network) return; @@ -185,13 +219,19 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy try { switch (network.type) { case 'ethereum': - account = await dispatch(EthereumDiscoveryActions.discoverAccount(device, discoveryProcess)); + account = await dispatch( + EthereumDiscoveryActions.discoverAccount(device, discoveryProcess) + ); break; case 'ripple': - account = await dispatch(RippleDiscoveryActions.discoverAccount(device, discoveryProcess)); + account = await dispatch( + RippleDiscoveryActions.discoverAccount(device, discoveryProcess) + ); break; default: - throw new Error(`DiscoveryActions.discoverAccount: Unknown network type: ${network.type}`); + throw new Error( + `DiscoveryActions.discoverAccount: Unknown network type: ${network.type}` + ); } } catch (error) { // handle not supported firmware error @@ -242,7 +282,11 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy if (dispatch(isProcessInterrupted(discoveryProcess))) return; const accountIsEmpty = account.empty; - if (!accountIsEmpty || (accountIsEmpty && completed) || (accountIsEmpty && accountIndex === 0)) { + if ( + !accountIsEmpty || + (accountIsEmpty && completed) || + (accountIsEmpty && accountIndex === 0) + ) { dispatch({ type: ACCOUNT.CREATE, payload: account, @@ -256,7 +300,9 @@ const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): Asy } }; -const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async (dispatch: Dispatch): Promise => { +const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction => async ( + dispatch: Dispatch +): Promise => { await TrezorConnect.getFeatures({ device: { path: device.path, @@ -279,7 +325,9 @@ const finish = (device: TrezorDevice, discoveryProcess: Discovery): AsyncAction }); }; -export const reconnect = (network: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const reconnect = (network: string): PromiseAction => async ( + dispatch: Dispatch +): Promise => { await dispatch(BlockchainActions.subscribe(network)); dispatch(restore()); }; @@ -296,10 +344,16 @@ export const restore = (): ThunkAction => (dispatch: Dispatch, getState: GetStat if (!selected) return; // find discovery process for requested network - const discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === selected.state && d.network === urlParams.network); + const discoveryProcess: ?Discovery = getState().discovery.find( + d => d.deviceState === selected.state && d.network === urlParams.network + ); // if there was no process befor OR process was interrupted/waiting - const shouldStart = !discoveryProcess || (discoveryProcess.interrupted || discoveryProcess.waitingForDevice || discoveryProcess.waitingForBlockchain); + const shouldStart = + !discoveryProcess || + (discoveryProcess.interrupted || + discoveryProcess.waitingForDevice || + discoveryProcess.waitingForBlockchain); if (shouldStart) { dispatch(start(selected, urlParams.network)); } @@ -310,7 +364,9 @@ export const stop = (): ThunkAction => (dispatch: Dispatch, getState: GetState): if (!device) return; // get all uncompleted discovery processes which assigned to selected device - const discoveryProcesses = getState().discovery.filter(d => d.deviceState === device.state && !d.completed); + const discoveryProcesses = getState().discovery.filter( + d => d.deviceState === device.state && !d.completed + ); if (discoveryProcesses.length > 0) { dispatch({ type: DISCOVERY.STOP, diff --git a/src/actions/LocalStorageActions.js b/src/actions/LocalStorageActions.js index c718d0dc..6355c962 100644 --- a/src/actions/LocalStorageActions.js +++ b/src/actions/LocalStorageActions.js @@ -1,6 +1,5 @@ /* @flow */ - import * as CONNECT from 'actions/constants/TrezorConnect'; import * as ACCOUNT from 'actions/constants/account'; import * as TOKEN from 'actions/constants/token'; @@ -32,18 +31,21 @@ import type { Config, Network, TokensCollection } from 'reducers/LocalStorageRed import Erc20AbiJSON from 'public/data/ERC20Abi.json'; import AppConfigJSON from 'public/data/appConfig.json'; -export type StorageAction = { - type: typeof STORAGE.READY, - config: Config, - tokens: TokensCollection, - ERC20Abi: Array -} | { - type: typeof STORAGE.SAVE, - network: string, -} | { - type: typeof STORAGE.ERROR, - error: string, -}; +export type StorageAction = + | { + type: typeof STORAGE.READY, + config: Config, + tokens: TokensCollection, + ERC20Abi: Array, + } + | { + type: typeof STORAGE.SAVE, + network: string, + } + | { + type: typeof STORAGE.ERROR, + error: string, + }; const TYPE: 'local' = 'local'; const { STORAGE_PATH } = storageUtils; @@ -60,16 +62,39 @@ const KEY_LANGUAGE: string = `${STORAGE_PATH}language`; // or // https://www.npmjs.com/package/redux-react-session -const findAccounts = (devices: Array, accounts: Array): Array => devices.reduce((arr, dev) => arr.concat(accounts.filter(a => a.deviceState === dev.state)), []); - -const findTokens = (accounts: Array, tokens: Array): Array => accounts.reduce((arr, account) => arr.concat(getAccountTokens(tokens, account)), []); - -const findDiscovery = (devices: Array, discovery: Array): Array => devices.reduce((arr, dev) => arr.concat(discovery.filter(d => d.deviceState === dev.state && d.completed)), []); - -const findPendingTxs = (accounts: Array, pending: Array): Array => accounts.reduce((result, account) => result.concat(pending.filter(p => p.descriptor === account.descriptor && p.network === account.network)), []); +const findAccounts = (devices: Array, accounts: Array): Array => + devices.reduce((arr, dev) => arr.concat(accounts.filter(a => a.deviceState === dev.state)), []); + +const findTokens = (accounts: Array, tokens: Array): Array => + accounts.reduce((arr, account) => arr.concat(getAccountTokens(tokens, account)), []); + +const findDiscovery = ( + devices: Array, + discovery: Array +): Array => + devices.reduce( + (arr, dev) => arr.concat(discovery.filter(d => d.deviceState === dev.state && d.completed)), + [] + ); + +const findPendingTxs = ( + accounts: Array, + pending: Array +): Array => + accounts.reduce( + (result, account) => + result.concat( + pending.filter( + p => p.descriptor === account.descriptor && p.network === account.network + ) + ), + [] + ); export const save = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { - const devices: Array = getState().devices.filter(d => d.features && d.remember === true); + const devices: Array = getState().devices.filter( + d => d.features && d.remember === true + ); const accounts: Array = findAccounts(devices, getState().accounts); const tokens: Array = findTokens(accounts, getState().tokens); const pending: Array = findPendingTxs(accounts, getState().pending); @@ -98,14 +123,12 @@ export const update = (event: StorageEvent): ThunkAction => (dispatch: Dispatch) // check if device was added/ removed // const newDevices: Array = JSON.parse(event.newValue); // const myDevices: Array = getState().connect.devices.filter(d => d.features); - // if (newDevices.length !== myDevices.length) { // const diff = myDevices.filter(d => newDevices.indexOf(d) < 0) // console.warn("DEV LIST CHANGED!", newDevices.length, myDevices.length, diff) // // check if difference is caused by local device which is not saved // // or device which was saved in other tab // } - // const diff = oldDevices.filter(d => newDevices.indexOf()) } @@ -151,19 +174,25 @@ const loadJSON = (): AsyncAction => async (dispatch: Dispatch): Promise => const ERC20Abi = await httpRequest(Erc20AbiJSON, 'json'); - window.addEventListener('storage', (event) => { + window.addEventListener('storage', event => { dispatch(update(event)); }); // load tokens - const tokens = await config.networks.reduce(async (promise: Promise, network: Network): Promise => { - const collection: TokensCollection = await promise; - if (network.tokens) { - const json = await httpRequest(network.tokens, 'json'); - collection[network.shortcut] = json; - } - return collection; - }, Promise.resolve({})); + const tokens = await config.networks.reduce( + async ( + promise: Promise, + network: Network + ): Promise => { + const collection: TokensCollection = await promise; + if (network.tokens) { + const json = await httpRequest(network.tokens, 'json'); + collection[network.shortcut] = json; + } + return collection; + }, + Promise.resolve({}) + ); dispatch({ type: STORAGE.READY, @@ -230,7 +259,9 @@ const loadStorageData = (): ThunkAction => (dispatch: Dispatch): void => { } if (buildUtils.isDev() || buildUtils.isBeta()) { - const betaModal = Object.keys(window.localStorage).find(key => key.indexOf(KEY_BETA_MODAL) >= 0); + const betaModal = Object.keys(window.localStorage).find( + key => key.indexOf(KEY_BETA_MODAL) >= 0 + ); if (!betaModal) { dispatch({ type: WALLET.SHOW_BETA_DISCLAIMER, diff --git a/src/actions/LogActions.js b/src/actions/LogActions.js index 5e1bf6a7..7feeaf85 100644 --- a/src/actions/LogActions.js +++ b/src/actions/LogActions.js @@ -2,19 +2,20 @@ import * as LOG from 'actions/constants/log'; -import type { - Action, ThunkAction, GetState, Dispatch, -} from 'flowtype'; +import type { Action, ThunkAction, GetState, Dispatch } from 'flowtype'; import type { LogEntry } from 'reducers/LogReducer'; -export type LogAction = { - type: typeof LOG.OPEN, -} | { - type: typeof LOG.CLOSE, -} | { - type: typeof LOG.ADD, - payload: LogEntry -}; +export type LogAction = + | { + type: typeof LOG.OPEN, + } + | { + type: typeof LOG.CLOSE, + } + | { + type: typeof LOG.ADD, + payload: LogEntry, + }; export const toggle = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { if (!getState().log.opened) { diff --git a/src/actions/ModalActions.js b/src/actions/ModalActions.js index fe2cd7af..f665762c 100644 --- a/src/actions/ModalActions.js +++ b/src/actions/ModalActions.js @@ -6,25 +6,25 @@ import type { Device } from 'trezor-connect'; import * as MODAL from 'actions/constants/modal'; import * as CONNECT from 'actions/constants/TrezorConnect'; -import type { - ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice, -} from 'flowtype'; +import type { ThunkAction, AsyncAction, Action, GetState, Dispatch, TrezorDevice } from 'flowtype'; import type { State } from 'reducers/ModalReducer'; import type { parsedURI } from 'utils/cryptoUriParser'; import sendEthereumFormActions from './ethereum/SendFormActions'; import sendRippleFormActions from './ripple/SendFormActions'; -export type ModalAction = { - type: typeof MODAL.CLOSE -} | { - type: typeof MODAL.OPEN_EXTERNAL_WALLET, - id: string, - url: string, -} | { - type: typeof MODAL.OPEN_SCAN_QR, -}; - +export type ModalAction = + | { + type: typeof MODAL.CLOSE, + } + | { + type: typeof MODAL.OPEN_EXTERNAL_WALLET, + id: string, + url: string, + } + | { + type: typeof MODAL.OPEN_SCAN_QR, + }; export const onPinSubmit = (value: string): Action => { TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value }); @@ -33,7 +33,10 @@ export const onPinSubmit = (value: string): Action => { }; }; -export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onPassphraseSubmit = (passphrase: string): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const { modal } = getState(); if (modal.context !== MODAL.CONTEXT_DEVICE) return; @@ -59,7 +62,9 @@ export const onPassphraseSubmit = (passphrase: string): AsyncAction => async (di }); }; -export const onReceiveConfirmation = (confirmation: any): AsyncAction => async (dispatch: Dispatch): Promise => { +export const onReceiveConfirmation = (confirmation: any): AsyncAction => async ( + dispatch: Dispatch +): Promise => { await TrezorConnect.uiResponse({ type: UI.RECEIVE_CONFIRMATION, payload: confirmation, @@ -89,7 +94,9 @@ export const onCancel = (): Action => ({ type: MODAL.CLOSE, }); -export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch): void => { +export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => ( + dispatch: Dispatch +): void => { dispatch(onCancel()); dispatch({ @@ -98,11 +105,17 @@ export const onDuplicateDevice = (device: TrezorDevice): ThunkAction => (dispatc }); }; -export const onRememberRequest = (prevState: State): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +export const onRememberRequest = (prevState: State): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().modal; // handle case where forget modal is already opened // TODO: 2 modals at once (two devices disconnected in the same time) - if (prevState.context === MODAL.CONTEXT_DEVICE && prevState.windowType === CONNECT.REMEMBER_REQUEST) { + if ( + prevState.context === MODAL.CONTEXT_DEVICE && + prevState.windowType === CONNECT.REMEMBER_REQUEST + ) { // forget current (new) if (state.context === MODAL.CONTEXT_DEVICE) { dispatch({ @@ -119,12 +132,20 @@ export const onRememberRequest = (prevState: State): ThunkAction => (dispatch: D } }; -export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +export const onDeviceConnect = (device: Device): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { // interrupt process of remembering device (force forget) // TODO: the same for disconnect more than 1 device at once const { modal } = getState(); if (modal.context === MODAL.CONTEXT_DEVICE && modal.windowType === CONNECT.REMEMBER_REQUEST) { - if (device.features && modal.device && modal.device.features && modal.device.features.device_id === device.features.device_id) { + if ( + device.features && + modal.device && + modal.device.features && + modal.device.features.device_id === device.features.device_id + ) { dispatch({ type: MODAL.CLOSE, }); @@ -137,7 +158,10 @@ export const onDeviceConnect = (device: Device): ThunkAction => (dispatch: Dispa } }; -export const onWalletTypeRequest = (hidden: boolean): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +export const onWalletTypeRequest = (hidden: boolean): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const { modal } = getState(); if (modal.context !== MODAL.CONTEXT_DEVICE) return; dispatch({ @@ -150,7 +174,9 @@ export const onWalletTypeRequest = (hidden: boolean): ThunkAction => (dispatch: }); }; -export const gotoExternalWallet = (id: string, url: string): ThunkAction => (dispatch: Dispatch): void => { +export const gotoExternalWallet = (id: string, url: string): ThunkAction => ( + dispatch: Dispatch +): void => { dispatch({ type: MODAL.OPEN_EXTERNAL_WALLET, id, @@ -164,7 +190,9 @@ export const openQrModal = (): ThunkAction => (dispatch: Dispatch): void => { }); }; -export const onQrScan = (parsedUri: parsedURI, networkType: string): ThunkAction => (dispatch: Dispatch): void => { +export const onQrScan = (parsedUri: parsedURI, networkType: string): ThunkAction => ( + dispatch: Dispatch +): void => { const { address = '', amount } = parsedUri; switch (networkType) { case 'ethereum': @@ -180,7 +208,6 @@ export const onQrScan = (parsedUri: parsedURI, networkType: string): ThunkAction } }; - export default { onPinSubmit, onPassphraseSubmit, @@ -194,4 +221,4 @@ export default { gotoExternalWallet, openQrModal, onQrScan, -}; \ No newline at end of file +}; diff --git a/src/actions/NotificationActions.js b/src/actions/NotificationActions.js index 3d0d68fb..48e2706e 100644 --- a/src/actions/NotificationActions.js +++ b/src/actions/NotificationActions.js @@ -2,41 +2,48 @@ import * as React from 'react'; import * as NOTIFICATION from 'actions/constants/notification'; -import type { - Action, AsyncAction, GetState, Dispatch, RouterLocationState, -} from 'flowtype'; +import type { Action, AsyncAction, GetState, Dispatch, RouterLocationState } from 'flowtype'; import type { CallbackAction } from 'reducers/NotificationReducer'; -export type NotificationAction = { - type: typeof NOTIFICATION.ADD, - payload: { - +type: string, - +title: React.Node | string, - +message?: ?(React.Node | string), - +cancelable: boolean, - actions?: Array - } -} | { - type: typeof NOTIFICATION.CLOSE, - payload?: { - id?: string; - devicePath?: string - } -} +export type NotificationAction = + | { + type: typeof NOTIFICATION.ADD, + payload: { + +type: string, + +title: React.Node | string, + +message?: ?(React.Node | string), + +cancelable: boolean, + actions?: Array, + }, + } + | { + type: typeof NOTIFICATION.CLOSE, + payload?: { + id?: string, + devicePath?: string, + }, + }; export const close = (payload: any = {}): Action => ({ type: NOTIFICATION.CLOSE, payload, }); - // called from RouterService -export const clear = (currentParams: RouterLocationState, requestedParams: RouterLocationState): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const clear = ( + currentParams: RouterLocationState, + requestedParams: RouterLocationState +): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { // if route has been changed from device view into something else (like other device, settings...) // try to remove all Notifications which are linked to previous device (they are not cancelable by user) - if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { - const entries = getState().notifications.filter(entry => typeof entry.devicePath === 'string'); - entries.forEach((entry) => { + if ( + currentParams.device !== requestedParams.device || + currentParams.deviceInstance !== requestedParams.deviceInstance + ) { + const entries = getState().notifications.filter( + entry => typeof entry.devicePath === 'string' + ); + entries.forEach(entry => { if (typeof entry.devicePath === 'string') { dispatch({ type: NOTIFICATION.CLOSE, diff --git a/src/actions/PendingTxActions.js b/src/actions/PendingTxActions.js index e6000374..60377154 100644 --- a/src/actions/PendingTxActions.js +++ b/src/actions/PendingTxActions.js @@ -1,24 +1,28 @@ /* @flow */ - import * as PENDING from 'actions/constants/pendingTx'; import type { Transaction } from 'flowtype'; import type { State } from 'reducers/PendingTxReducer'; -export type PendingTxAction = { - type: typeof PENDING.FROM_STORAGE, - payload: State -} | { - type: typeof PENDING.ADD, - payload: Transaction -} | { - type: typeof PENDING.TX_RESOLVED, - hash: string, -} | { - type: typeof PENDING.TX_REJECTED, - hash: string, -} | { - type: typeof PENDING.TX_TOKEN_ERROR, - hash: string, -} \ No newline at end of file +export type PendingTxAction = + | { + type: typeof PENDING.FROM_STORAGE, + payload: State, + } + | { + type: typeof PENDING.ADD, + payload: Transaction, + } + | { + type: typeof PENDING.TX_RESOLVED, + hash: string, + } + | { + type: typeof PENDING.TX_REJECTED, + hash: string, + } + | { + type: typeof PENDING.TX_TOKEN_ERROR, + hash: string, + }; diff --git a/src/actions/ReceiveActions.js b/src/actions/ReceiveActions.js index 0503fdf7..867f7e2a 100644 --- a/src/actions/ReceiveActions.js +++ b/src/actions/ReceiveActions.js @@ -1,6 +1,5 @@ /* @flow */ - import TrezorConnect from 'trezor-connect'; import * as RECEIVE from 'actions/constants/receive'; import * as NOTIFICATION from 'actions/constants/notification'; @@ -8,25 +7,29 @@ import * as NOTIFICATION from 'actions/constants/notification'; import { initialState } from 'reducers/ReceiveReducer'; import type { State } from 'reducers/ReceiveReducer'; -import type { - TrezorDevice, ThunkAction, AsyncAction, Action, GetState, Dispatch, -} from 'flowtype'; - -export type ReceiveAction = { - type: typeof RECEIVE.INIT, - state: State -} | { - type: typeof RECEIVE.DISPOSE, -} | { - type: typeof RECEIVE.REQUEST_UNVERIFIED, - device: TrezorDevice -} | { - type: typeof RECEIVE.SHOW_ADDRESS -} | { - type: typeof RECEIVE.HIDE_ADDRESS -} | { - type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS -} +import type { TrezorDevice, ThunkAction, AsyncAction, Action, GetState, Dispatch } from 'flowtype'; + +export type ReceiveAction = + | { + type: typeof RECEIVE.INIT, + state: State, + } + | { + type: typeof RECEIVE.DISPOSE, + } + | { + type: typeof RECEIVE.REQUEST_UNVERIFIED, + device: TrezorDevice, + } + | { + type: typeof RECEIVE.SHOW_ADDRESS, + } + | { + type: typeof RECEIVE.HIDE_ADDRESS, + } + | { + type: typeof RECEIVE.SHOW_UNVERIFIED_ADDRESS, + }; export const init = (): ThunkAction => (dispatch: Dispatch): void => { const state: State = { @@ -48,7 +51,10 @@ export const showUnverifiedAddress = (): Action => ({ }); //export const showAddress = (address_n: string): AsyncAction => { -export const showAddress = (path: Array): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const showAddress = (path: Array): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const selected = getState().wallet.selectedDevice; const { network } = getState().selectedAccount; @@ -81,7 +87,11 @@ export const showAddress = (path: Array): AsyncAction => async (dispatch response = await TrezorConnect.rippleGetAddress(params); break; default: - response = { payload: { error: `ReceiveActions.showAddress: Unknown network type: ${network.type}` } }; + response = { + payload: { + error: `ReceiveActions.showAddress: Unknown network type: ${network.type}`, + }, + }; break; } @@ -123,4 +133,4 @@ export default { dispose, showAddress, showUnverifiedAddress, -}; \ No newline at end of file +}; diff --git a/src/actions/RouterActions.js b/src/actions/RouterActions.js index 7fdeb835..d078baf1 100644 --- a/src/actions/RouterActions.js +++ b/src/actions/RouterActions.js @@ -18,9 +18,11 @@ import type { import type { RouterAction } from 'connected-react-router'; /* -* Parse url string to RouterLocationState object (key/value) -*/ -export const pathToParams = (path: string): PayloadAction => (): RouterLocationState => { + * Parse url string to RouterLocationState object (key/value) + */ +export const pathToParams = ( + path: string +): PayloadAction => (): RouterLocationState => { // split url into parts const parts: Array = path.split('/').slice(1); const params: RouterLocationState = {}; @@ -46,10 +48,13 @@ export const pathToParams = (path: string): PayloadAction = }; /* -* RouterLocationState validation -* Check if requested device or network exists in reducers -*/ -export const paramsValidation = (params: RouterLocationState): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { + * RouterLocationState validation + * Check if requested device or network exists in reducers + */ +export const paramsValidation = (params: RouterLocationState): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): boolean => { // validate requested device if (params.hasOwnProperty('device')) { @@ -57,9 +62,18 @@ export const paramsValidation = (params: RouterLocationState): PayloadAction d.features && d.features.device_id === params.device && d.instance === parseInt(params.deviceInstance, 10)); + device = devices.find( + d => + d.features && + d.features.device_id === params.device && + d.instance === parseInt(params.deviceInstance, 10) + ); } else { - device = devices.find(d => ((!d.features || d.mode === 'bootloader') && d.path === params.device) || (d.features && d.features.device_id === params.device)); + device = devices.find( + d => + ((!d.features || d.mode === 'bootloader') && d.path === params.device) || + (d.features && d.features.device_id === params.device) + ); } if (!device) return false; @@ -87,12 +101,16 @@ export const paramsValidation = (params: RouterLocationState): PayloadAction => (): ?string => { + * Composing url string from given RouterLocationState object + * Filters unrecognized fields and sorting in correct order + */ +export const paramsToPath = ( + params: RouterLocationState +): PayloadAction => (): ?string => { // get patterns (fields) from routes and sort them by complexity - const patterns: Array> = routes.map(r => r.fields).sort((a, b) => (a.length > b.length ? -1 : 1)); + const patterns: Array> = routes + .map(r => r.fields) + .sort((a, b) => (a.length > b.length ? -1 : 1)); // find pattern const keys: Array = Object.keys(params); @@ -111,7 +129,7 @@ export const paramsToPath = (params: RouterLocationState): PayloadAction { + patternToUse.forEach(field => { if (field === params[field]) { // standalone (odd) fields url += `/${field}`; @@ -127,7 +145,10 @@ export const paramsToPath = (params: RouterLocationState): PayloadAction => (dispatch: Dispatch, getState: GetState): string => { +export const getValidUrl = (action: RouterAction): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): string => { const { location } = getState().router; const { firstLocationChange } = getState().wallet; // redirect to landing page (loading screen) @@ -151,12 +172,17 @@ export const getValidUrl = (action: RouterAction): PayloadAction => (dis // example 2 (invalid blocking): url changes while passphrase modal opened because device disconnect const currentParams = dispatch(pathToParams(location.pathname)); const currentParamsAreValid = dispatch(paramsValidation(currentParams)); - if (currentParamsAreValid) { return location.pathname; } + if (currentParamsAreValid) { + return location.pathname; + } } // there are no connected devices or application isn't ready or initialization error occurred // redirect to landing page - const shouldBeLandingPage = getState().devices.length < 1 || !getState().wallet.ready || getState().connect.error !== null; + const shouldBeLandingPage = + getState().devices.length < 1 || + !getState().wallet.ready || + getState().connect.error !== null; const landingPageUrl = dispatch(isLandingPageUrl(requestedUrl)); if (shouldBeLandingPage) { const landingPageRoute = dispatch(isLandingPageUrl(requestedUrl, getState().wallet.ready)); @@ -185,13 +211,17 @@ export const getValidUrl = (action: RouterAction): PayloadAction => (dis }; /* -* Compose url from requested device object and returns url -*/ -const getDeviceUrl = (device: TrezorDevice | Device): PayloadAction => (dispatch: Dispatch, getState: GetState): ?string => { + * Compose url from requested device object and returns url + */ +const getDeviceUrl = (device: TrezorDevice | Device): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): ?string => { let url: ?string; if (!device.features) { url = `/device/${device.path}/${device.type === 'unreadable' ? 'unreadable' : 'acquire'}`; - } else if (device.mode === 'bootloader') { // device in bootloader doesn't have device_id + } else if (device.mode === 'bootloader') { + // device in bootloader doesn't have device_id url = `/device/${device.path}/bootloader`; } else if (device.mode === 'initialize') { url = `/device/${device.features.device_id}/initialize`; @@ -207,7 +237,9 @@ const getDeviceUrl = (device: TrezorDevice | Device): PayloadAction => if (!device.hasOwnProperty('ts')) { // it is device from trezor-connect triggered by DEVICE.CONNECT event // need to lookup if there are unavailable instances - const available: Array = getState().devices.filter(d => d.path === device.path); + const available: Array = getState().devices.filter( + d => d.path === device.path + ); const latest: Array = sortDevices(available); if (latest.length > 0 && latest[0].instance) { url += `:${latest[0].instance}`; @@ -218,13 +250,16 @@ const getDeviceUrl = (device: TrezorDevice | Device): PayloadAction => }; /* -* Try to find first available device using order: -* 1. First unacquired -* 2. First connected -* 3. Saved with latest timestamp -* OR redirect to landing page -*/ -export const getFirstAvailableDeviceUrl = (): PayloadAction => (dispatch: Dispatch, getState: GetState): ?string => { + * Try to find first available device using order: + * 1. First unacquired + * 2. First connected + * 3. Saved with latest timestamp + * OR redirect to landing page + */ +export const getFirstAvailableDeviceUrl = (): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): ?string => { const { devices } = getState(); let url: ?string; if (devices.length > 0) { @@ -241,20 +276,24 @@ export const getFirstAvailableDeviceUrl = (): PayloadAction => (dispatc }; /* -* Utility used in "getDeviceUrl" and "getFirstAvailableDeviceUrl" -* sorting device array by "ts" (timestamp) field -*/ -const sortDevices = (devices: Array): Array => devices.sort((a, b) => { - if (!a.ts || !b.ts) { - return -1; - } - return a.ts > b.ts ? -1 : 1; -}); + * Utility used in "getDeviceUrl" and "getFirstAvailableDeviceUrl" + * sorting device array by "ts" (timestamp) field + */ +const sortDevices = (devices: Array): Array => + devices.sort((a, b) => { + if (!a.ts || !b.ts) { + return -1; + } + return a.ts > b.ts ? -1 : 1; + }); /* -* Redirect to requested device -*/ -export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Redirect to requested device + */ +export const selectDevice = (device: TrezorDevice | Device): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { if (dispatch(setInitialUrl())) return; const url: ?string = dispatch(getDeviceUrl(device)); @@ -262,20 +301,30 @@ export const selectDevice = (device: TrezorDevice | Device): ThunkAction => (dis const currentParams: RouterLocationState = getState().router.location.state; const requestedParams = dispatch(pathToParams(url)); - if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { + if ( + currentParams.device !== requestedParams.device || + currentParams.deviceInstance !== requestedParams.deviceInstance + ) { dispatch(goto(url)); } }; /* -* Redirect to first device or landing page -*/ -export const selectFirstAvailableDevice = (gotoRoot: boolean = false): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Redirect to first device or landing page + */ +export const selectFirstAvailableDevice = (gotoRoot: boolean = false): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const url = dispatch(getFirstAvailableDeviceUrl()); if (url) { const currentParams = getState().router.location.state; const requestedParams = dispatch(pathToParams(url)); - if (gotoRoot || currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { + if ( + gotoRoot || + currentParams.device !== requestedParams.device || + currentParams.deviceInstance !== requestedParams.deviceInstance + ) { dispatch(goto(url)); } } else { @@ -284,8 +333,8 @@ export const selectFirstAvailableDevice = (gotoRoot: boolean = false): ThunkActi }; /* -* Internal method. redirect to given url -*/ + * Internal method. redirect to given url + */ const goto = (url: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { if (getState().router.location.pathname !== url) { dispatch(push(url)); @@ -293,15 +342,20 @@ const goto = (url: string): ThunkAction => (dispatch: Dispatch, getState: GetSta }; /* -* Check if requested OR current url is landing page -*/ -export const isLandingPageUrl = ($url?: string, checkRoutes: boolean = false): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { + * Check if requested OR current url is landing page + */ +export const isLandingPageUrl = ( + $url?: string, + checkRoutes: boolean = false +): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { let url: ?string = $url; if (typeof url !== 'string') { url = getState().router.location.pathname; } if (checkRoutes) { - const isLandingRoute = routes.find(r => r.pattern === url && r.name.indexOf('landing') >= 0); + const isLandingRoute = routes.find( + r => r.pattern === url && r.name.indexOf('landing') >= 0 + ); if (isLandingRoute) { return true; } @@ -310,8 +364,8 @@ export const isLandingPageUrl = ($url?: string, checkRoutes: boolean = false): P }; /* -* Try to redirect to landing page -*/ + * Try to redirect to landing page + */ export const gotoLandingPage = (): ThunkAction => (dispatch: Dispatch): void => { const isLandingPage = dispatch(isLandingPageUrl()); if (!isLandingPage) { @@ -320,61 +374,76 @@ export const gotoLandingPage = (): ThunkAction => (dispatch: Dispatch): void => }; /* -* Go to given device settings page -*/ -export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => (dispatch: Dispatch): void => { + * Go to given device settings page + */ +export const gotoDeviceSettings = (device: TrezorDevice): ThunkAction => ( + dispatch: Dispatch +): void => { if (device.features) { - const devUrl: string = `${device.features.device_id}${device.instance ? `:${device.instance}` : ''}`; + const devUrl: string = `${device.features.device_id}${ + device.instance ? `:${device.instance}` : '' + }`; dispatch(goto(`/device/${devUrl}/settings`)); } }; /* -* Go to UpdateBridge page -*/ + * Go to UpdateBridge page + */ export const gotoBridgeUpdate = (): ThunkAction => (dispatch: Dispatch): void => { dispatch(goto('/bridge')); }; /* -* Go to UpdateFirmware page -* Called from App notification -*/ -export const gotoFirmwareUpdate = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Go to UpdateFirmware page + * Called from App notification + */ +export const gotoFirmwareUpdate = (): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const { selectedDevice } = getState().wallet; if (!selectedDevice || !selectedDevice.features) return; - const devUrl: string = `${selectedDevice.features.device_id}${selectedDevice.instance ? `:${selectedDevice.instance}` : ''}`; + const devUrl: string = `${selectedDevice.features.device_id}${ + selectedDevice.instance ? `:${selectedDevice.instance}` : '' + }`; dispatch(goto(`/device/${devUrl}/firmware-update`)); }; /* -* Go to NoBackup page -*/ + * Go to NoBackup page + */ export const gotoBackup = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { const { selectedDevice } = getState().wallet; if (!selectedDevice || !selectedDevice.features) return; - const devUrl: string = `${selectedDevice.features.device_id}${selectedDevice.instance ? `:${selectedDevice.instance}` : ''}`; + const devUrl: string = `${selectedDevice.features.device_id}${ + selectedDevice.instance ? `:${selectedDevice.instance}` : '' + }`; dispatch(goto(`/device/${devUrl}/backup`)); }; - /* -* Try to redirect to initial url -*/ -export const setInitialUrl = (): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { + * Try to redirect to initial url + */ +export const setInitialUrl = (): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): boolean => { const { initialPathname } = getState().wallet; if (typeof initialPathname === 'string' && !dispatch(isLandingPageUrl(initialPathname, true))) { - const valid = dispatch(getValidUrl({ - type: LOCATION_CHANGE, - payload: { - location: { - pathname: initialPathname, - hash: '', - search: '', - state: {}, + const valid = dispatch( + getValidUrl({ + type: LOCATION_CHANGE, + payload: { + location: { + pathname: initialPathname, + hash: '', + search: '', + state: {}, + }, }, - }, - })); + }) + ); if (valid === initialPathname) { // reset initial url diff --git a/src/actions/SelectedAccountActions.js b/src/actions/SelectedAccountActions.js index c71c0457..75ef722d 100644 --- a/src/actions/SelectedAccountActions.js +++ b/src/actions/SelectedAccountActions.js @@ -11,13 +11,7 @@ import * as reducerUtils from 'reducers/utils'; import { getVersion } from 'utils/device'; import { initialState } from 'reducers/SelectedAccountReducer'; -import type { - PayloadAction, - Action, - GetState, - Dispatch, - State, -} from 'flowtype'; +import type { PayloadAction, Action, GetState, Dispatch, State } from 'flowtype'; import type { State as SelectedAccountState, @@ -26,12 +20,14 @@ import type { ExceptionPage, } from 'reducers/SelectedAccountReducer'; -export type SelectedAccountAction = { - type: typeof ACCOUNT.DISPOSE, -} | { - type: typeof ACCOUNT.UPDATE_SELECTED_ACCOUNT, - payload: SelectedAccountState, -}; +export type SelectedAccountAction = + | { + type: typeof ACCOUNT.DISPOSE, + } + | { + type: typeof ACCOUNT.UPDATE_SELECTED_ACCOUNT, + payload: SelectedAccountState, + }; export const dispose = (): Action => ({ type: ACCOUNT.DISPOSE, @@ -68,11 +64,7 @@ const getExceptionPage = (state: State, selectedAccount: SelectedAccountState): // display loader instead of component body const getAccountLoader = (state: State, selectedAccount: SelectedAccountState): ?Loader => { const device = state.wallet.selectedDevice; - const { - account, - discovery, - network, - } = selectedAccount; + const { account, discovery, network } = selectedAccount; if (!device || !device.state) { return { @@ -89,7 +81,6 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState): }; } - if (account) return null; // account not found (yet). checking why... @@ -135,7 +126,10 @@ const getAccountLoader = (state: State, selectedAccount: SelectedAccountState): }; // display notification above the component, with or without component body -const getAccountNotification = (state: State, selectedAccount: SelectedAccountState): ?(Notification & { shouldRender: boolean }) => { +const getAccountNotification = ( + state: State, + selectedAccount: SelectedAccountState +): ?(Notification & { shouldRender: boolean }) => { const device = state.wallet.selectedDevice; const { account, network, discovery } = selectedAccount; if (!device || !network) return null; @@ -190,16 +184,21 @@ const actions = [ ...Object.values(BLOCKCHAIN).filter(v => typeof v === 'string'), WALLET.SET_SELECTED_DEVICE, WALLET.UPDATE_SELECTED_DEVICE, - ...Object.values(ACCOUNT).filter(v => typeof v === 'string' && v !== ACCOUNT.UPDATE_SELECTED_ACCOUNT && v !== ACCOUNT.DISPOSE), // exported values got unwanted "__esModule: true" as first element + ...Object.values(ACCOUNT).filter( + v => typeof v === 'string' && v !== ACCOUNT.UPDATE_SELECTED_ACCOUNT && v !== ACCOUNT.DISPOSE + ), // exported values got unwanted "__esModule: true" as first element ...Object.values(DISCOVERY).filter(v => typeof v === 'string'), ...Object.values(TOKEN).filter(v => typeof v === 'string'), ...Object.values(PENDING).filter(v => typeof v === 'string'), ]; /* -* Called from WalletService -*/ -export const observe = (prevState: State, action: Action): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { + * Called from WalletService + */ +export const observe = (prevState: State, action: Action): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): boolean => { // ignore not listed actions if (actions.indexOf(action.type) < 0) return false; const state: State = getState(); @@ -238,12 +237,22 @@ export const observe = (prevState: State, action: Action): PayloadAction (dispatch: Dispatch, getState: GetState): void => { + * Called from WalletService + */ +export const observe = (prevState: ReducersState, action: Action): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { // ignore not listed actions if (actions.indexOf(action.type) < 0) return; @@ -62,6 +62,7 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction = case 'ripple': dispatch(RippleSendFormActions.observe(prevState, action)); break; - default: break; + default: + break; } }; diff --git a/src/actions/SessionStorageActions.js b/src/actions/SessionStorageActions.js index 411ab0f6..6744b0e0 100644 --- a/src/actions/SessionStorageActions.js +++ b/src/actions/SessionStorageActions.js @@ -4,12 +4,7 @@ import { findToken } from 'reducers/utils'; import type { State as EthereumSendFormState } from 'reducers/SendFormEthereumReducer'; import type { State as RippleSendFormState } from 'reducers/SendFormRippleReducer'; -import type { - ThunkAction, - PayloadAction, - GetState, - Dispatch, -} from 'flowtype'; +import type { ThunkAction, PayloadAction, GetState, Dispatch } from 'flowtype'; const TYPE: 'session' = 'session'; const { STORAGE_PATH } = storageUtils; @@ -20,7 +15,10 @@ const getTxDraftKey = (getState: GetState): string => { return `${KEY_TX_DRAFT}${pathname}`; }; -export const saveDraftTransaction = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +export const saveDraftTransaction = (): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state = getState().sendFormEthereum; if (state.untouched) return; @@ -28,7 +26,10 @@ export const saveDraftTransaction = (): ThunkAction => (dispatch: Dispatch, getS storageUtils.set(TYPE, key, JSON.stringify(state)); }; -export const loadEthereumDraftTransaction = (): PayloadAction => (dispatch: Dispatch, getState: GetState): ?EthereumSendFormState => { +export const loadEthereumDraftTransaction = (): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): ?EthereumSendFormState => { const key = getTxDraftKey(getState); const value: ?string = storageUtils.get(TYPE, key); if (!value) return null; @@ -53,7 +54,10 @@ export const loadEthereumDraftTransaction = (): PayloadAction => (dispatch: Dispatch, getState: GetState): ?RippleSendFormState => { +export const loadRippleDraftTransaction = (): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): ?RippleSendFormState => { const key = getTxDraftKey(getState); const value: ?string = storageUtils.get(TYPE, key); if (!value) return null; diff --git a/src/actions/SignVerifyActions.js b/src/actions/SignVerifyActions.js index 021694f8..d12bb678 100644 --- a/src/actions/SignVerifyActions.js +++ b/src/actions/SignVerifyActions.js @@ -1,41 +1,45 @@ /* @flow */ import TrezorConnect from 'trezor-connect'; -import type { - GetState, Dispatch, ThunkAction, AsyncAction, -} from 'flowtype'; +import type { GetState, Dispatch, ThunkAction, AsyncAction } from 'flowtype'; import { validateAddress } from 'utils/ethUtils'; import * as NOTIFICATION from 'actions/constants/notification'; import * as SIGN_VERIFY from './constants/signVerify'; -export type SignVerifyAction = { - type: typeof SIGN_VERIFY.SIGN_SUCCESS, - signSignature: string -} | { - type: typeof SIGN_VERIFY.CLEAR_SIGN, -} | { - type: typeof SIGN_VERIFY.CLEAR_VERIFY, -} | { - type: typeof SIGN_VERIFY.INPUT_CHANGE, - inputName: string, - value: string -} | { - type: typeof SIGN_VERIFY.TOUCH, - inputName: string, -} | { - type: typeof SIGN_VERIFY.ERROR, - inputName: string, - message: ?string -} | { - type: typeof SIGN_VERIFY.ERROR, - inputName: string, - message: ?string -} +export type SignVerifyAction = + | { + type: typeof SIGN_VERIFY.SIGN_SUCCESS, + signSignature: string, + } + | { + type: typeof SIGN_VERIFY.CLEAR_SIGN, + } + | { + type: typeof SIGN_VERIFY.CLEAR_VERIFY, + } + | { + type: typeof SIGN_VERIFY.INPUT_CHANGE, + inputName: string, + value: string, + } + | { + type: typeof SIGN_VERIFY.TOUCH, + inputName: string, + } + | { + type: typeof SIGN_VERIFY.ERROR, + inputName: string, + message: ?string, + } + | { + type: typeof SIGN_VERIFY.ERROR, + inputName: string, + message: ?string, + }; -const sign = ( - path: Array, - message: string, - hex: boolean = false, -): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +const sign = (path: Array, message: string, hex: boolean = false): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const selected = getState().wallet.selectedDevice; if (!selected) return; @@ -73,7 +77,7 @@ const verify = ( address: string, message: string, signature: string, - hex: boolean = false, + hex: boolean = false ): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { const selected = getState().wallet.selectedDevice; if (!selected) return; @@ -125,7 +129,9 @@ const verify = ( } }; -const inputChange = (inputName: string, value: string): ThunkAction => (dispatch: Dispatch): void => { +const inputChange = (inputName: string, value: string): ThunkAction => ( + dispatch: Dispatch +): void => { dispatch({ type: SIGN_VERIFY.INPUT_CHANGE, inputName, @@ -162,4 +168,4 @@ export default { clearSign, clearVerify, inputChange, -}; \ No newline at end of file +}; diff --git a/src/actions/SummaryActions.js b/src/actions/SummaryActions.js index dfbe7cad..e4e745f0 100644 --- a/src/actions/SummaryActions.js +++ b/src/actions/SummaryActions.js @@ -2,19 +2,20 @@ import * as SUMMARY from 'actions/constants/summary'; import { initialState } from 'reducers/SummaryReducer'; -import type { - ThunkAction, Action, Dispatch, -} from 'flowtype'; +import type { ThunkAction, Action, Dispatch } from 'flowtype'; import type { State } from 'reducers/SummaryReducer'; -export type SummaryAction = { - type: typeof SUMMARY.INIT, - state: State -} | { - type: typeof SUMMARY.DISPOSE, -} | { - type: typeof SUMMARY.DETAILS_TOGGLE -} +export type SummaryAction = + | { + type: typeof SUMMARY.INIT, + state: State, + } + | { + type: typeof SUMMARY.DISPOSE, + } + | { + type: typeof SUMMARY.DETAILS_TOGGLE, + }; export const init = (): ThunkAction => (dispatch: Dispatch): void => { const state: State = { diff --git a/src/actions/TokenActions.js b/src/actions/TokenActions.js index c1429c9d..dcea4e55 100644 --- a/src/actions/TokenActions.js +++ b/src/actions/TokenActions.js @@ -1,41 +1,47 @@ /* @flow */ - import * as TOKEN from 'actions/constants/token'; -import type { - GetState, AsyncAction, Action, Dispatch, -} from 'flowtype'; +import type { GetState, AsyncAction, Action, Dispatch } from 'flowtype'; import type { State, Token } from 'reducers/TokensReducer'; import type { Account } from 'reducers/AccountsReducer'; import type { NetworkToken } from 'reducers/LocalStorageReducer'; import * as BlockchainActions from 'actions/ethereum/BlockchainActions'; -export type TokenAction = { - type: typeof TOKEN.FROM_STORAGE, - payload: State -} | { - type: typeof TOKEN.ADD, - payload: Token -} | { - type: typeof TOKEN.REMOVE, - token: Token -} | { - type: typeof TOKEN.SET_BALANCE, - payload: State -} - +export type TokenAction = + | { + type: typeof TOKEN.FROM_STORAGE, + payload: State, + } + | { + type: typeof TOKEN.ADD, + payload: Token, + } + | { + type: typeof TOKEN.REMOVE, + token: Token, + } + | { + type: typeof TOKEN.SET_BALANCE, + payload: State, + }; // action from component -export const load = ($input: string, network: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const load = ($input: string, network: string): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { let input = $input; if (input.length < 1) input = '0x'; const tokens = getState().localStorage.tokens[network]; const value = input.toLowerCase(); - const result = tokens.filter(t => t.symbol.toLowerCase().indexOf(value) >= 0 - || t.address.toLowerCase().indexOf(value) >= 0 - || t.name.toLowerCase().indexOf(value) >= 0); + const result = tokens.filter( + t => + t.symbol.toLowerCase().indexOf(value) >= 0 || + t.address.toLowerCase().indexOf(value) >= 0 || + t.name.toLowerCase().indexOf(value) >= 0 + ); if (result.length > 0) { // TODO: Temporary fix for async select @@ -52,23 +58,33 @@ export const load = ($input: string, network: string): AsyncAction => async (dis return null; }; -export const setBalance = (tokenAddress: string, ethAddress: string, balance: string): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const setBalance = ( + tokenAddress: string, + ethAddress: string, + balance: string +): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { const newState: Array = [...getState().tokens]; - const token: ?Token = newState.find(t => t.address === tokenAddress && t.ethAddress === ethAddress); + const token: ?Token = newState.find( + t => t.address === tokenAddress && t.ethAddress === ethAddress + ); if (token) { const others = newState.filter(t => t !== token); dispatch({ type: TOKEN.SET_BALANCE, - payload: others.concat([{ - ...token, - loaded: true, - balance, - }]), + payload: others.concat([ + { + ...token, + loaded: true, + balance, + }, + ]), }); } }; -export const add = (token: NetworkToken, account: Account): AsyncAction => async (dispatch: Dispatch): Promise => { +export const add = (token: NetworkToken, account: Account): AsyncAction => async ( + dispatch: Dispatch +): Promise => { const tkn: Token = { loaded: false, deviceState: account.deviceState, @@ -93,4 +109,4 @@ export const add = (token: NetworkToken, account: Account): AsyncAction => async export const remove = (token: Token): Action => ({ type: TOKEN.REMOVE, token, -}); \ No newline at end of file +}); diff --git a/src/actions/TrezorConnectActions.js b/src/actions/TrezorConnectActions.js index 8b05b218..0b5ce5f2 100644 --- a/src/actions/TrezorConnectActions.js +++ b/src/actions/TrezorConnectActions.js @@ -1,6 +1,10 @@ /* @flow */ import TrezorConnect, { - DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT, BLOCKCHAIN_EVENT, + DEVICE, + DEVICE_EVENT, + UI_EVENT, + TRANSPORT_EVENT, + BLOCKCHAIN_EVENT, } from 'trezor-connect'; import { CONTEXT_NONE } from 'actions/constants/modal'; import urlConstants from 'constants/urls'; @@ -31,96 +35,127 @@ import type { TrezorDevice, } from 'flowtype'; - -export type TrezorConnectAction = { - type: typeof CONNECT.INITIALIZATION_ERROR, - error: string -} | { - type: typeof CONNECT.NETWORK_CHANGED, - payload: { - network: string - } -} | { - type: typeof CONNECT.AUTH_DEVICE, - device: TrezorDevice, - state: string -} | { - type: typeof CONNECT.DUPLICATE, - device: TrezorDevice -} | { - type: typeof CONNECT.REMEMBER_REQUEST, - device: TrezorDevice, - instances: Array -} | { - type: typeof CONNECT.DISCONNECT_REQUEST, - device: TrezorDevice -} | { - type: typeof CONNECT.FORGET_REQUEST, - device: TrezorDevice -} | { - type: typeof CONNECT.FORGET, - device: TrezorDevice -} | { - type: typeof CONNECT.FORGET_SINGLE | typeof CONNECT.FORGET_SILENT, - device: TrezorDevice -} | { - type: typeof CONNECT.REMEMBER, - device: TrezorDevice -} | { - type: typeof CONNECT.TRY_TO_DUPLICATE, - device: TrezorDevice -} | { - type: typeof CONNECT.DEVICE_FROM_STORAGE, - payload: Array -} | { - type: typeof CONNECT.START_ACQUIRING | typeof CONNECT.STOP_ACQUIRING, -} | { - type: typeof CONNECT.REQUEST_WALLET_TYPE, - device: TrezorDevice -} | { - type: typeof CONNECT.RECEIVE_WALLET_TYPE | typeof CONNECT.UPDATE_WALLET_TYPE, - device: TrezorDevice, - hidden: boolean, -}; +export type TrezorConnectAction = + | { + type: typeof CONNECT.INITIALIZATION_ERROR, + error: string, + } + | { + type: typeof CONNECT.NETWORK_CHANGED, + payload: { + network: string, + }, + } + | { + type: typeof CONNECT.AUTH_DEVICE, + device: TrezorDevice, + state: string, + } + | { + type: typeof CONNECT.DUPLICATE, + device: TrezorDevice, + } + | { + type: typeof CONNECT.REMEMBER_REQUEST, + device: TrezorDevice, + instances: Array, + } + | { + type: typeof CONNECT.DISCONNECT_REQUEST, + device: TrezorDevice, + } + | { + type: typeof CONNECT.FORGET_REQUEST, + device: TrezorDevice, + } + | { + type: typeof CONNECT.FORGET, + device: TrezorDevice, + } + | { + type: typeof CONNECT.FORGET_SINGLE | typeof CONNECT.FORGET_SILENT, + device: TrezorDevice, + } + | { + type: typeof CONNECT.REMEMBER, + device: TrezorDevice, + } + | { + type: typeof CONNECT.TRY_TO_DUPLICATE, + device: TrezorDevice, + } + | { + type: typeof CONNECT.DEVICE_FROM_STORAGE, + payload: Array, + } + | { + type: typeof CONNECT.START_ACQUIRING | typeof CONNECT.STOP_ACQUIRING, + } + | { + type: typeof CONNECT.REQUEST_WALLET_TYPE, + device: TrezorDevice, + } + | { + type: typeof CONNECT.RECEIVE_WALLET_TYPE | typeof CONNECT.UPDATE_WALLET_TYPE, + device: TrezorDevice, + hidden: boolean, + }; declare var LOCAL: ?string; -export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const init = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { // set listeners - TrezorConnect.on(DEVICE_EVENT, (event: DeviceMessage): void => { - // post event to reducers - const type: DeviceMessageType = event.type; // eslint-disable-line prefer-destructuring - dispatch({ - type, - device: event.payload, - }); - }); + TrezorConnect.on( + DEVICE_EVENT, + (event: DeviceMessage): void => { + // post event to reducers + const type: DeviceMessageType = event.type; // eslint-disable-line prefer-destructuring + dispatch({ + type, + device: event.payload, + }); + } + ); - TrezorConnect.on(UI_EVENT, (event: UiMessage): void => { - // post event to reducers - const type: UiMessageType = event.type; // eslint-disable-line prefer-destructuring - dispatch({ - type, - payload: event.payload, - }); - }); + TrezorConnect.on( + UI_EVENT, + (event: UiMessage): void => { + // post event to reducers + const type: UiMessageType = event.type; // eslint-disable-line prefer-destructuring + dispatch({ + type, + payload: event.payload, + }); + } + ); - TrezorConnect.on(TRANSPORT_EVENT, (event: TransportMessage): void => { - // post event to reducers - const type: TransportMessageType = event.type; // eslint-disable-line prefer-destructuring - dispatch({ - type, - payload: event.payload, - }); - }); + TrezorConnect.on( + TRANSPORT_EVENT, + (event: TransportMessage): void => { + // post event to reducers + const type: TransportMessageType = event.type; // eslint-disable-line prefer-destructuring + dispatch({ + type, + payload: event.payload, + }); + } + ); // post event to reducers - TrezorConnect.on(BLOCKCHAIN_EVENT, (event: BlockchainEvent): void => { - dispatch(event); - }); + TrezorConnect.on( + BLOCKCHAIN_EVENT, + (event: BlockchainEvent): void => { + dispatch(event); + } + ); if (buildUtils.isDev()) { - window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.trezor.io/connect/'; // eslint-disable-line no-underscore-dangle + // eslint-disable-next-line + window.__TREZOR_CONNECT_SRC = + typeof LOCAL === 'string' ? LOCAL : 'https://sisyfos.trezor.io/connect/'; // eslint-disable-line no-underscore-dangle // window.__TREZOR_CONNECT_SRC = typeof LOCAL === 'string' ? LOCAL : 'https://localhost:8088/'; // eslint-disable-line no-underscore-dangle window.TrezorConnect = TrezorConnect; } @@ -131,7 +166,7 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS debug: false, popup: false, webusb: true, - pendingTransportEvent: (getState().devices.length < 1), + pendingTransportEvent: getState().devices.length < 1, manifest: { email: 'info@trezor.io', appUrl: urlConstants.NEXT_WALLET, @@ -165,10 +200,18 @@ export const postInit = (): ThunkAction => (dispatch: Dispatch): void => { } }; -export const requestWalletType = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const requestWalletType = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const selected = getState().wallet.selectedDevice; if (!selected) return; - const isDeviceReady = selected.connected && selected.features && !selected.state && selected.mode === 'normal' && selected.firmware !== 'required'; + const isDeviceReady = + selected.connected && + selected.features && + !selected.state && + selected.mode === 'normal' && + selected.firmware !== 'required'; if (!isDeviceReady) return; if (selected.features && selected.features.passphrase_protection) { @@ -186,10 +229,18 @@ export const requestWalletType = (): AsyncAction => async (dispatch: Dispatch, g } }; -export const authorizeDevice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const authorizeDevice = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const selected = getState().wallet.selectedDevice; if (!selected) return; - const isDeviceReady = selected.connected && selected.features && !selected.state && selected.mode === 'normal' && selected.firmware !== 'required'; + const isDeviceReady = + selected.connected && + selected.features && + !selected.state && + selected.mode === 'normal' && + selected.firmware !== 'required'; if (!isDeviceReady) return; const response = await TrezorConnect.getDeviceState({ @@ -233,11 +284,24 @@ export const authorizeDevice = (): AsyncAction => async (dispatch: Dispatch, get } }; -export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const deviceDisconnect = (device: Device): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { if (device.features) { - const instances = getState().devices.filter(d => d.features && device.features && d.state && !d.remember && d.features.device_id === device.features.device_id); + const instances = getState().devices.filter( + d => + d.features && + device.features && + d.state && + !d.remember && + d.features.device_id === device.features.device_id + ); if (instances.length > 0) { - const isSelected = deviceUtils.isSelectedDevice(getState().wallet.selectedDevice, device); + const isSelected = deviceUtils.isSelectedDevice( + getState().wallet.selectedDevice, + device + ); if (!isSelected && getState().modal.context !== CONTEXT_NONE) { dispatch({ type: CONNECT.FORGET_SILENT, @@ -259,8 +323,7 @@ export const deviceDisconnect = (device: Device): AsyncAction => async (dispatch }; export function reload(): AsyncAction { - return async (): Promise => { - }; + return async (): Promise => {}; } export function acquire(): AsyncAction { @@ -314,7 +377,10 @@ export const forget = (device: TrezorDevice): Action => ({ device, }); -export const duplicateDeviceOld = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const duplicateDeviceOld = (device: TrezorDevice): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const instance: number = getDuplicateInstanceNumber(getState().devices, device); const extended: Object = { instance }; dispatch({ @@ -323,7 +389,9 @@ export const duplicateDeviceOld = (device: TrezorDevice): AsyncAction => async ( }); }; -export const duplicateDevice = (device: TrezorDevice): AsyncAction => async (dispatch: Dispatch): Promise => { +export const duplicateDevice = (device: TrezorDevice): AsyncAction => async ( + dispatch: Dispatch +): Promise => { dispatch({ type: CONNECT.REQUEST_WALLET_TYPE, device, diff --git a/src/actions/TxActions.js b/src/actions/TxActions.js index e3d35cd5..07f62753 100644 --- a/src/actions/TxActions.js +++ b/src/actions/TxActions.js @@ -7,27 +7,28 @@ import { toHex } from 'web3-utils'; // eslint-disable-line import/no-extraneous- import { initWeb3 } from 'actions/Web3Actions'; import * as ethUtils from 'utils/ethUtils'; -import type { - Dispatch, - PromiseAction, -} from 'flowtype'; +import type { Dispatch, PromiseAction } from 'flowtype'; import type { EthereumTransaction } from 'trezor-connect'; import type { Token } from 'reducers/TokensReducer'; type EthereumTxRequest = { - network: string; - token: ?Token; - from: string; - to: string; - amount: string; - data: string; - gasLimit: string; - gasPrice: string; - nonce: number; -} + network: string, + token: ?Token, + from: string, + to: string, + amount: string, + data: string, + gasLimit: string, + gasPrice: string, + nonce: number, +}; -export const prepareEthereumTx = (tx: EthereumTxRequest): PromiseAction => async (dispatch: Dispatch): Promise => { +export const prepareEthereumTx = ( + tx: EthereumTxRequest +): PromiseAction => async ( + dispatch: Dispatch +): Promise => { const instance = await dispatch(initWeb3(tx.network)); const { token } = tx; let data: string = ethUtils.sanitizeHex(tx.data); @@ -37,7 +38,9 @@ export const prepareEthereumTx = (tx: EthereumTxRequest): PromiseAction => async (): Promise => { +export const serializeEthereumTx = ( + tx: EthereumTransaction +): PromiseAction => async (): Promise => { const ethTx = new EthereumjsTx(tx); return `0x${ethTx.serialize().toString('hex')}`; -}; \ No newline at end of file +}; diff --git a/src/actions/WalletActions.js b/src/actions/WalletActions.js index 0b34fa52..da22dd97 100644 --- a/src/actions/WalletActions.js +++ b/src/actions/WalletActions.js @@ -19,34 +19,46 @@ import type { State, } from 'flowtype'; -export type WalletAction = { - type: typeof WALLET.SET_INITIAL_URL, - state?: RouterLocationState, - pathname?: string -} | { - type: typeof WALLET.TOGGLE_DEVICE_DROPDOWN, - opened: boolean -} | { - type: typeof WALLET.ONLINE_STATUS, - online: boolean -} | { - type: typeof WALLET.SET_SELECTED_DEVICE, - device: ?TrezorDevice -} | { - type: typeof WALLET.UPDATE_SELECTED_DEVICE, - device: TrezorDevice -} | { - type: typeof WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA, - devices: Array -} | { - type: typeof WALLET.SHOW_BETA_DISCLAIMER | typeof WALLET.HIDE_BETA_DISCLAIMER | typeof WALLET.SET_FIRST_LOCATION_CHANGE, -} | { - type: typeof WALLET.TOGGLE_SIDEBAR, -} | { - type: typeof WALLET.SET_LANGUAGE, - locale: string, - messages: { [string]: string }, -} +export type WalletAction = + | { + type: typeof WALLET.SET_INITIAL_URL, + state?: RouterLocationState, + pathname?: string, + } + | { + type: typeof WALLET.TOGGLE_DEVICE_DROPDOWN, + opened: boolean, + } + | { + type: typeof WALLET.ONLINE_STATUS, + online: boolean, + } + | { + type: typeof WALLET.SET_SELECTED_DEVICE, + device: ?TrezorDevice, + } + | { + type: typeof WALLET.UPDATE_SELECTED_DEVICE, + device: TrezorDevice, + } + | { + type: typeof WALLET.CLEAR_UNAVAILABLE_DEVICE_DATA, + devices: Array, + } + | { + type: + | typeof WALLET.SHOW_BETA_DISCLAIMER + | typeof WALLET.HIDE_BETA_DISCLAIMER + | typeof WALLET.SET_FIRST_LOCATION_CHANGE, + } + | { + type: typeof WALLET.TOGGLE_SIDEBAR, + } + | { + type: typeof WALLET.SET_LANGUAGE, + locale: string, + messages: { [string]: string }, + }; export const init = (): ThunkAction => (dispatch: Dispatch): void => { const updateOnlineStatus = () => { @@ -75,7 +87,7 @@ export const toggleSidebar = (): WalletAction => ({ export const fetchLocale = (locale: string): ThunkAction => (dispatch: Dispatch): void => { fetch(`/l10n/${locale}.json`) .then(response => response.json()) - .then((messages) => { + .then(messages => { dispatch({ type: WALLET.SET_LANGUAGE, locale, @@ -89,12 +101,18 @@ export const fetchLocale = (locale: string): ThunkAction => (dispatch: Dispatch) // 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): 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 - && d.features.device_id === device.features.device_id - && d.features.passphrase_protection !== device.features.passphrase_protection); + const affectedDevices = prevState.devices.filter( + d => + d.features && + device.features && + d.features.device_id === device.features.device_id && + d.features.passphrase_protection !== device.features.passphrase_protection + ); if (affectedDevices.length > 0) { dispatch({ @@ -114,15 +132,21 @@ const actions = [ ]; /* -* Called from WalletService -*/ -export const observe = (prevState: State, action: Action): PayloadAction => (dispatch: Dispatch, getState: GetState): boolean => { + * Called from WalletService + */ +export const observe = (prevState: State, action: Action): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): boolean => { // ignore not listed actions if (actions.indexOf(action.type) < 0) return false; const state: State = getState(); - const locationChanged = reducerUtils.observeChanges(prevState.router.location, state.router.location); + const locationChanged = reducerUtils.observeChanges( + prevState.router.location, + state.router.location + ); const device = reducerUtils.getSelectedDevice(state); const selectedDeviceChanged = reducerUtils.observeChanges(state.wallet.selectedDevice, device); @@ -142,4 +166,4 @@ export const observe = (prevState: State, action: Action): PayloadAction => async (dispatch: Dispatch, getState: GetState): Promise => new Promise(async (resolve, reject) => { - // check if requested web was initialized before - const instance = getState().web3.find(w3 => w3.network === network); - if (instance && instance.web3.currentProvider.connected) { - resolve(instance); - return; - } - - // requested web3 wasn't initialized or is disconnected - // initialize again - const { config, ERC20Abi } = getState().localStorage; - const networkData = config.networks.find(c => c.shortcut === network); - if (!networkData) { - // network not found - reject(new Error(`Network ${network} not found in application config.`)); - return; - } +export type Web3Action = + | { + type: typeof WEB3.READY, + } + | { + type: typeof WEB3.START, + } + | { + type: typeof WEB3.CREATE | typeof WEB3.DISCONNECT, + instance: Web3Instance, + } + | Web3UpdateBlockAction + | Web3UpdateGasPriceAction; + +export const initWeb3 = ( + network: string, + urlIndex: number = 0 +): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => + new Promise(async (resolve, reject) => { + // check if requested web was initialized before + const instance = getState().web3.find(w3 => w3.network === network); + if (instance && instance.web3.currentProvider.connected) { + resolve(instance); + return; + } - // get first url - const url = networkData.web3[urlIndex]; - if (!url) { - reject(new Error('Web3 backend is not responding')); - return; - } + // requested web3 wasn't initialized or is disconnected + // initialize again + const { config, ERC20Abi } = getState().localStorage; + const networkData = config.networks.find(c => c.shortcut === network); + if (!networkData) { + // network not found + reject(new Error(`Network ${network} not found in application config.`)); + return; + } - const web3 = new Web3(new Web3.providers.WebsocketProvider(url)); + // get first url + const url = networkData.web3[urlIndex]; + if (!url) { + reject(new Error('Web3 backend is not responding')); + return; + } - const onConnect = async () => { - const latestBlock = await web3.eth.getBlockNumber(); - const gasPrice = await web3.eth.getGasPrice(); + const web3 = new Web3(new Web3.providers.WebsocketProvider(url)); - const newInstance = { - network, - web3, - chainId: networkData.chainId, - erc20: new web3.eth.Contract(ERC20Abi), - latestBlock, - gasPrice, - }; + const onConnect = async () => { + const latestBlock = await web3.eth.getBlockNumber(); + const gasPrice = await web3.eth.getGasPrice(); - dispatch({ - type: WEB3.CREATE, - instance: newInstance, - }); + const newInstance = { + network, + web3, + chainId: networkData.chainId, + erc20: new web3.eth.Contract(ERC20Abi), + latestBlock, + gasPrice, + }; - resolve(newInstance); - }; + dispatch({ + type: WEB3.CREATE, + instance: newInstance, + }); - const onEnd = async () => { - web3.currentProvider.reset(); - const oldInstance = getState().web3.find(w3 => w3.network === network); + resolve(newInstance); + }; - if (oldInstance && oldInstance.web3.currentProvider.connected) { - // backend disconnects - // dispatch({ - // type: 'WEB3.DISCONNECT', - // network - // }); - } else { - // backend initialization error for given url, try next one - try { - const otherWeb3 = await dispatch(initWeb3(network, urlIndex + 1)); - resolve(otherWeb3); - } catch (error) { - reject(error); + const onEnd = async () => { + web3.currentProvider.reset(); + const oldInstance = getState().web3.find(w3 => w3.network === network); + + if (oldInstance && oldInstance.web3.currentProvider.connected) { + // backend disconnects + // dispatch({ + // type: 'WEB3.DISCONNECT', + // network + // }); + } else { + // backend initialization error for given url, try next one + try { + const otherWeb3 = await dispatch(initWeb3(network, urlIndex + 1)); + resolve(otherWeb3); + } catch (error) { + reject(error); + } } - } - }; + }; - web3.currentProvider.on('connect', onConnect); - web3.currentProvider.on('end', onEnd); - web3.currentProvider.on('error', onEnd); -}); + web3.currentProvider.on('connect', onConnect); + web3.currentProvider.on('end', onEnd); + web3.currentProvider.on('error', onEnd); + }); -export const discoverAccount = (descriptor: string, network: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const discoverAccount = ( + descriptor: string, + network: string +): PromiseAction => async (dispatch: Dispatch): Promise => { const instance: Web3Instance = await dispatch(initWeb3(network)); const balance = await instance.web3.eth.getBalance(descriptor); const nonce = await instance.web3.eth.getTransactionCount(descriptor); @@ -134,10 +143,13 @@ export const discoverAccount = (descriptor: string, network: string): PromiseAct }; }; -export const resolvePendingTransactions = (network: string): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const resolvePendingTransactions = (network: string): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const instance: Web3Instance = await dispatch(initWeb3(network)); const pending = getState().pending.filter(p => p.network === network); - pending.forEach(async (tx) => { + pending.forEach(async tx => { const status = await instance.web3.eth.getTransaction(tx.hash); if (!status) { dispatch({ @@ -192,39 +204,49 @@ export const getTxInput = (): PromiseAction => async (dispatch: Dispatch): }; */ -export const updateAccount = (account: Account, newAccount: EthereumAccount, network: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const updateAccount = ( + account: Account, + newAccount: EthereumAccount, + network: string +): PromiseAction => async (dispatch: Dispatch): Promise => { const instance: Web3Instance = await dispatch(initWeb3(network)); const balance = await instance.web3.eth.getBalance(account.descriptor); const nonce = await instance.web3.eth.getTransactionCount(account.descriptor); - dispatch(AccountsActions.update({ - networkType: 'ethereum', - ...account, - ...newAccount, - nonce, - balance: EthereumjsUnits.convert(balance, 'wei', 'ether'), - availableBalance: EthereumjsUnits.convert(balance, 'wei', 'ether'), - })); + dispatch( + AccountsActions.update({ + networkType: 'ethereum', + ...account, + ...newAccount, + nonce, + balance: EthereumjsUnits.convert(balance, 'wei', 'ether'), + availableBalance: EthereumjsUnits.convert(balance, 'wei', 'ether'), + }) + ); // update tokens for this account dispatch(updateAccountTokens(account)); }; -export const updateAccountTokens = (account: Account): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const tokens = getState().tokens.filter(t => t.network === account.network && t.ethAddress === account.descriptor); - tokens.forEach(async (token) => { +export const updateAccountTokens = (account: Account): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { + const tokens = getState().tokens.filter( + t => t.network === account.network && t.ethAddress === account.descriptor + ); + tokens.forEach(async token => { const balance = await dispatch(getTokenBalance(token)); // const newBalance: string = balance.dividedBy(Math.pow(10, token.decimals)).toString(10); if (balance !== token.balance) { - dispatch(TokenActions.setBalance( - token.address, - token.ethAddress, - balance, - )); + dispatch(TokenActions.setBalance(token.address, token.ethAddress, balance)); } }); }; -export const getTokenInfo = (address: string, network: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const getTokenInfo = ( + address: string, + network: string +): PromiseAction => async (dispatch: Dispatch): Promise => { const instance: Web3Instance = await dispatch(initWeb3(network)); const contract = instance.erc20.clone(); contract.options.address = address; @@ -241,7 +263,9 @@ export const getTokenInfo = (address: string, network: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const getTokenBalance = (token: Token): PromiseAction => async ( + dispatch: Dispatch +): Promise => { const instance = await dispatch(initWeb3(token.network)); const contract = instance.erc20.clone(); contract.options.address = token.address; @@ -250,7 +274,10 @@ export const getTokenBalance = (token: Token): PromiseAction => async (d return new BigNumber(balance).dividedBy(10 ** token.decimals).toString(10); }; -export const getCurrentGasPrice = (network: string): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const getCurrentGasPrice = (network: string): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const instance = getState().web3.find(w3 => w3.network === network); if (instance) { return EthereumjsUnits.convert(instance.gasPrice, 'wei', 'gwei'); @@ -258,7 +285,9 @@ export const getCurrentGasPrice = (network: string): PromiseAction => as return '0'; }; -export const updateGasPrice = (network: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const updateGasPrice = (network: string): PromiseAction => async ( + dispatch: Dispatch +): Promise => { try { const instance = await dispatch(initWeb3(network)); const gasPrice = await instance.web3.eth.getGasPrice(); @@ -275,23 +304,32 @@ export const updateGasPrice = (network: string): PromiseAction => async (d } }; - -export const estimateGasLimit = (network: string, $options: EstimateGasOptions): PromiseAction => async (dispatch: Dispatch): Promise => { +export const estimateGasLimit = ( + network: string, + $options: EstimateGasOptions +): PromiseAction => async (dispatch: Dispatch): Promise => { const instance = await dispatch(initWeb3(network)); const data = ethUtils.sanitizeHex($options.data); const options = { ...$options, to: '0x0000000000000000000000000000000000000000', data, - value: instance.web3.utils.toHex(EthereumjsUnits.convert($options.value || '0', 'ether', 'wei')), - gasPrice: instance.web3.utils.toHex(EthereumjsUnits.convert($options.gasPrice, 'gwei', 'wei')), + value: instance.web3.utils.toHex( + EthereumjsUnits.convert($options.value || '0', 'ether', 'wei') + ), + gasPrice: instance.web3.utils.toHex( + EthereumjsUnits.convert($options.gasPrice, 'gwei', 'wei') + ), }; const limit = await instance.web3.eth.estimateGas(options); return limit.toString(); }; -export const disconnect = (network: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +export const disconnect = (network: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { // check if Web3 was already initialized const instance = getState().web3.find(w3 => w3.network === network); if (instance) { diff --git a/src/actions/constants/TrezorConnect.js b/src/actions/constants/TrezorConnect.js index 53b27c4f..38809aa4 100644 --- a/src/actions/constants/TrezorConnect.js +++ b/src/actions/constants/TrezorConnect.js @@ -1,13 +1,11 @@ /* @flow */ - //regExp1 : string = '(.*)' //regExp2 : '$1' = '$1' export const READY: 'connect__ready' = 'connect__ready'; export const INITIALIZATION_ERROR: 'connect__init_error' = 'connect__init_error'; - export const DEVICE_FROM_STORAGE: 'connect__device_from_storage' = 'connect__device_from_storage'; export const AUTH_DEVICE: 'connect__auth_device' = 'connect__auth_device'; export const NETWORK_CHANGED: 'connect__network_changed' = 'connect__network_changed'; @@ -23,11 +21,12 @@ export const REMEMBER: 'connect__remember' = 'connect__remember'; export const TRY_TO_DUPLICATE: 'connect__try_to_duplicate' = 'connect__try_to_duplicate'; export const DUPLICATE: 'connect__duplicate' = 'connect__duplicate'; -export const DEVICE_STATE_EXCEPTION: 'connect__device_state_exception' = 'connect__device_state_exception'; +export const DEVICE_STATE_EXCEPTION: 'connect__device_state_exception' = + 'connect__device_state_exception'; export const START_ACQUIRING: 'connect__start_acquiring' = 'connect__start_acquiring'; export const STOP_ACQUIRING: 'connect__stop_acquiring' = 'connect__stop_acquiring'; export const REQUEST_WALLET_TYPE: 'connect__request_wallet_type' = 'connect__request_wallet_type'; export const RECEIVE_WALLET_TYPE: 'connect__receive_wallet_type' = 'connect__receive_wallet_type'; -export const UPDATE_WALLET_TYPE: 'connect__update_wallet_type' = 'connect__update_wallet_type'; \ No newline at end of file +export const UPDATE_WALLET_TYPE: 'connect__update_wallet_type' = 'connect__update_wallet_type'; diff --git a/src/actions/constants/account.js b/src/actions/constants/account.js index d1baa199..c1edd2f0 100644 --- a/src/actions/constants/account.js +++ b/src/actions/constants/account.js @@ -1,6 +1,5 @@ /* @flow */ - export const INIT: 'account__init' = 'account__init'; export const DISPOSE: 'account__dispose' = 'account__dispose'; @@ -11,4 +10,5 @@ export const SET_BALANCE: 'account__set_balance' = 'account__set_balance'; export const SET_NONCE: 'account__set_nonce' = 'account__set_nonce'; export const FROM_STORAGE: 'account__from_storage' = 'account__from_storage'; -export const UPDATE_SELECTED_ACCOUNT: 'account__update_selected_account' = 'account__update_selected_account'; \ No newline at end of file +export const UPDATE_SELECTED_ACCOUNT: 'account__update_selected_account' = + 'account__update_selected_account'; diff --git a/src/actions/constants/blockchain.js b/src/actions/constants/blockchain.js index 7e1baeab..3cdd0a2e 100644 --- a/src/actions/constants/blockchain.js +++ b/src/actions/constants/blockchain.js @@ -2,4 +2,4 @@ export const START_SUBSCRIBE: 'blockchain__start_subscribe' = 'blockchain__start_subscribe'; export const READY: 'blockchain__ready' = 'blockchain__ready'; -export const UPDATE_FEE: 'blockchain__update_fee' = 'blockchain__update_fee'; \ No newline at end of file +export const UPDATE_FEE: 'blockchain__update_fee' = 'blockchain__update_fee'; diff --git a/src/actions/constants/discovery.js b/src/actions/constants/discovery.js index a1b2a65e..605799f4 100644 --- a/src/actions/constants/discovery.js +++ b/src/actions/constants/discovery.js @@ -1,11 +1,11 @@ /* @flow */ - export const START: 'discovery__start' = 'discovery__start'; export const STOP: 'discovery__stop' = 'discovery__stop'; export const FIRMWARE_NOT_SUPPORTED: 'discovery__fw_not_supported' = 'discovery__fw_not_supported'; export const FIRMWARE_OUTDATED: 'discovery__fw_outdated' = 'discovery__fw_outdated'; export const COMPLETE: 'discovery__complete' = 'discovery__complete'; export const WAITING_FOR_DEVICE: 'discovery__waiting_for_device' = 'discovery__waiting_for_device'; -export const WAITING_FOR_BLOCKCHAIN: 'discovery__waiting_for_blockchain' = 'discovery__waiting_for_blockchain'; -export const FROM_STORAGE: 'discovery__from_storage' = 'discovery__from_storage'; \ No newline at end of file +export const WAITING_FOR_BLOCKCHAIN: 'discovery__waiting_for_blockchain' = + 'discovery__waiting_for_blockchain'; +export const FROM_STORAGE: 'discovery__from_storage' = 'discovery__from_storage'; diff --git a/src/actions/constants/localStorage.js b/src/actions/constants/localStorage.js index 402a23a8..a09a4ce7 100644 --- a/src/actions/constants/localStorage.js +++ b/src/actions/constants/localStorage.js @@ -1,6 +1,5 @@ /* @flow */ - export const SAVE: 'storage__save' = 'storage__save'; export const READY: 'storage__ready' = 'storage__ready'; -export const ERROR: 'storage__error' = 'storage__error'; \ No newline at end of file +export const ERROR: 'storage__error' = 'storage__error'; diff --git a/src/actions/constants/log.js b/src/actions/constants/log.js index a08a84cf..35117873 100644 --- a/src/actions/constants/log.js +++ b/src/actions/constants/log.js @@ -1,6 +1,5 @@ /* @flow */ - export const OPEN: 'log__open' = 'log__open'; export const CLOSE: 'log__close' = 'log__close'; -export const ADD: 'log__add' = 'log__add'; \ No newline at end of file +export const ADD: 'log__add' = 'log__add'; diff --git a/src/actions/constants/notification.js b/src/actions/constants/notification.js index c9147409..d395f578 100644 --- a/src/actions/constants/notification.js +++ b/src/actions/constants/notification.js @@ -1,6 +1,5 @@ /* @flow */ - export const ADD: 'notification__add' = 'notification__add'; export const CLOSE: 'notification__close' = 'notification__close'; -export const REMOVE: 'account__remove' = 'account__remove'; \ No newline at end of file +export const REMOVE: 'account__remove' = 'account__remove'; diff --git a/src/actions/constants/pendingTx.js b/src/actions/constants/pendingTx.js index 08376cc4..b1fd152d 100644 --- a/src/actions/constants/pendingTx.js +++ b/src/actions/constants/pendingTx.js @@ -1,8 +1,7 @@ /* @flow */ - export const FROM_STORAGE: 'pending__from_storage' = 'pending__from_storage'; export const ADD: 'pending__add' = 'pending__add'; export const TX_RESOLVED: 'pending__tx_resolved' = 'pending__tx_resolved'; export const TX_REJECTED: 'pending__tx_rejected' = 'pending__tx_rejected'; -export const TX_TOKEN_ERROR: 'pending__tx_token_error' = 'pending__tx_token_error'; \ No newline at end of file +export const TX_TOKEN_ERROR: 'pending__tx_token_error' = 'pending__tx_token_error'; diff --git a/src/actions/constants/receive.js b/src/actions/constants/receive.js index 21d39d15..1183f87a 100644 --- a/src/actions/constants/receive.js +++ b/src/actions/constants/receive.js @@ -1,6 +1,5 @@ /* @flow */ - export const INIT: 'receive__init' = 'receive__init'; export const DISPOSE: 'receive__dispose' = 'receive__dispose'; export const REQUEST_UNVERIFIED: 'receive__request_unverified' = 'receive__request_unverified'; diff --git a/src/actions/constants/send.js b/src/actions/constants/send.js index 577bdac2..37549cf9 100644 --- a/src/actions/constants/send.js +++ b/src/actions/constants/send.js @@ -7,4 +7,4 @@ export const TX_SENDING: 'send__tx_sending' = 'send__tx_sending'; export const TX_COMPLETE: 'send__tx_complete' = 'send__tx_complete'; export const TX_ERROR: 'send__tx_error' = 'send__tx_error'; export const TOGGLE_ADVANCED: 'send__toggle_advanced' = 'send__toggle_advanced'; -export const CLEAR: 'send__clear' = 'send__clear'; \ No newline at end of file +export const CLEAR: 'send__clear' = 'send__clear'; diff --git a/src/actions/constants/signVerify.js b/src/actions/constants/signVerify.js index e48d615b..877f4db7 100644 --- a/src/actions/constants/signVerify.js +++ b/src/actions/constants/signVerify.js @@ -4,4 +4,4 @@ export const INPUT_CHANGE: 'sign__verify__input__change' = 'sign__verify__input_ export const TOUCH: 'sign__verify__input__touch' = 'sign__verify__input__touch'; export const CLEAR_SIGN: 'sign__verify__sign__clear' = 'sign__verify__sign__clear'; export const CLEAR_VERIFY: 'sign__verify__verify__clear' = 'sign__verify__verify__clear'; -export const ERROR: 'sign__verify__error' = 'sign__verify__error'; \ No newline at end of file +export const ERROR: 'sign__verify__error' = 'sign__verify__error'; diff --git a/src/actions/constants/summary.js b/src/actions/constants/summary.js index ae002582..bf795af7 100644 --- a/src/actions/constants/summary.js +++ b/src/actions/constants/summary.js @@ -1,6 +1,5 @@ /* @flow */ - export const INIT: 'summary__init' = 'summary__init'; export const DISPOSE: 'summary__dispose' = 'summary__dispose'; export const ADD_TOKEN: 'summary__add_token' = 'summary__add_token'; diff --git a/src/actions/constants/token.js b/src/actions/constants/token.js index f2ce1ebc..3420b8e1 100644 --- a/src/actions/constants/token.js +++ b/src/actions/constants/token.js @@ -1,7 +1,6 @@ /* @flow */ - export const ADD: 'token__add' = 'token__add'; export const REMOVE: 'token__remove' = 'token__remove'; export const SET_BALANCE: 'token__set_balance' = 'token__set_balance'; -export const FROM_STORAGE: 'token__from_storage' = 'token__from_storage'; \ No newline at end of file +export const FROM_STORAGE: 'token__from_storage' = 'token__from_storage'; diff --git a/src/actions/constants/wallet.js b/src/actions/constants/wallet.js index b4213abd..d1d8bb0a 100644 --- a/src/actions/constants/wallet.js +++ b/src/actions/constants/wallet.js @@ -2,15 +2,18 @@ export const TOGGLE_DEVICE_DROPDOWN: 'wallet__toggle_dropdown' = 'wallet__toggle_dropdown'; export const SET_INITIAL_URL: 'wallet__set_initial_url' = 'wallet__set_initial_url'; -export const SET_FIRST_LOCATION_CHANGE: 'wallet__set_first_location_change' = 'wallet__set_first_location_change'; +export const SET_FIRST_LOCATION_CHANGE: 'wallet__set_first_location_change' = + 'wallet__set_first_location_change'; export const ONLINE_STATUS: 'wallet__online_status' = 'wallet__online_status'; export const SET_SELECTED_DEVICE: 'wallet__set_selected_device' = 'wallet__set_selected_device'; -export const UPDATE_SELECTED_DEVICE: 'wallet__update_selected_device' = 'wallet__update_selected_device'; +export const UPDATE_SELECTED_DEVICE: 'wallet__update_selected_device' = + 'wallet__update_selected_device'; export const SHOW_BETA_DISCLAIMER: 'wallet__show_beta_disclaimer' = 'wallet__show_beta_disclaimer'; export const HIDE_BETA_DISCLAIMER: 'wallet__hide_beta_disclaimer' = 'wallet__hide_beta_disclaimer'; -export const CLEAR_UNAVAILABLE_DEVICE_DATA: 'wallet__clear_unavailable_device_data' = 'wallet__clear_unavailable_device_data'; +export const CLEAR_UNAVAILABLE_DEVICE_DATA: 'wallet__clear_unavailable_device_data' = + 'wallet__clear_unavailable_device_data'; export const TOGGLE_SIDEBAR: 'wallet__toggle_sidebar' = 'wallet__toggle_sidebar'; -export const SET_LANGUAGE: 'wallet__set_language' = 'wallet__set_language'; \ No newline at end of file +export const SET_LANGUAGE: 'wallet__set_language' = 'wallet__set_language'; diff --git a/src/actions/constants/web3.js b/src/actions/constants/web3.js index bed19c1f..dbbefdfa 100644 --- a/src/actions/constants/web3.js +++ b/src/actions/constants/web3.js @@ -1,10 +1,9 @@ /* @flow */ - export const START: 'web3__start' = 'web3__start'; export const STOP: 'web3__stop' = 'web3__stop'; export const CREATE: 'web3__create' = 'web3__create'; export const READY: 'web3__ready' = 'web3__ready'; export const BLOCK_UPDATED: 'web3__block_updated' = 'web3__block_updated'; export const GAS_PRICE_UPDATED: 'web3__gas_price_updated' = 'web3__gas_price_updated'; -export const DISCONNECT: 'web3__disconnect' = 'web3__disconnect'; \ No newline at end of file +export const DISCONNECT: 'web3__disconnect' = 'web3__disconnect'; diff --git a/src/actions/ethereum/BlockchainActions.js b/src/actions/ethereum/BlockchainActions.js index 087420aa..ce61a1d4 100644 --- a/src/actions/ethereum/BlockchainActions.js +++ b/src/actions/ethereum/BlockchainActions.js @@ -4,19 +4,18 @@ import TrezorConnect from 'trezor-connect'; import BigNumber from 'bignumber.js'; import * as PENDING from 'actions/constants/pendingTx'; -import type { - TrezorDevice, - Dispatch, - GetState, - PromiseAction, -} from 'flowtype'; +import type { TrezorDevice, Dispatch, GetState, PromiseAction } from 'flowtype'; import type { EthereumAccount, BlockchainNotification } from 'trezor-connect'; import type { Token } from 'reducers/TokensReducer'; import type { NetworkToken } from 'reducers/LocalStorageReducer'; import * as Web3Actions from 'actions/Web3Actions'; import * as AccountsActions from 'actions/AccountsActions'; -export const discoverAccount = (device: TrezorDevice, descriptor: string, network: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const discoverAccount = ( + device: TrezorDevice, + descriptor: string, + network: string +): PromiseAction => async (dispatch: Dispatch): Promise => { // get data from connect const txs = await TrezorConnect.ethereumGetAccountInfo({ account: { @@ -46,11 +45,18 @@ export const discoverAccount = (device: TrezorDevice, descriptor: string, networ }; }; -export const getTokenInfo = (input: string, network: string): PromiseAction => async (dispatch: Dispatch): Promise => dispatch(Web3Actions.getTokenInfo(input, network)); +export const getTokenInfo = (input: string, network: string): PromiseAction => async ( + dispatch: Dispatch +): Promise => dispatch(Web3Actions.getTokenInfo(input, network)); -export const getTokenBalance = (token: Token): PromiseAction => async (dispatch: Dispatch): Promise => dispatch(Web3Actions.getTokenBalance(token)); +export const getTokenBalance = (token: Token): PromiseAction => async ( + dispatch: Dispatch +): Promise => dispatch(Web3Actions.getTokenBalance(token)); -export const getGasPrice = (network: string, defaultGasPrice: number): PromiseAction => async (dispatch: Dispatch): Promise => { +export const getGasPrice = ( + network: string, + defaultGasPrice: number +): PromiseAction => async (dispatch: Dispatch): Promise => { try { const gasPrice = await dispatch(Web3Actions.getCurrentGasPrice(network)); return gasPrice === '0' ? new BigNumber(defaultGasPrice) : new BigNumber(gasPrice); @@ -60,7 +66,12 @@ export const getGasPrice = (network: string, defaultGasPrice: number): PromiseAc }; const estimateProxy: Array> = []; -export const estimateGasLimit = (network: string, data: string, value: string, gasPrice: string): PromiseAction => async (dispatch: Dispatch): Promise => { +export const estimateGasLimit = ( + network: string, + data: string, + value: string, + gasPrice: string +): PromiseAction => async (dispatch: Dispatch): Promise => { // Since this method could be called multiple times in short period of time // check for pending calls in proxy and if there more than two (first is current running and the second is waiting for result of first) // TODO: should reject second call immediately? @@ -69,12 +80,14 @@ export const estimateGasLimit = (network: string, data: string, value: string, g await estimateProxy[0]; } - const call = dispatch(Web3Actions.estimateGasLimit(network, { - to: '', - data, - value, - gasPrice, - })); + const call = dispatch( + Web3Actions.estimateGasLimit(network, { + to: '', + data, + value, + gasPrice, + }) + ); // add current call to proxy estimateProxy.push(call); // wait for result @@ -85,8 +98,13 @@ export const estimateGasLimit = (network: string, data: string, value: string, g return result; }; -export const subscribe = (network: string): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const accounts: Array = getState().accounts.filter(a => a.network === network).map(a => a.descriptor); // eslint-disable-line no-unused-vars +export const subscribe = (network: string): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { + const accounts: Array = getState() + .accounts.filter(a => a.network === network) + .map(a => a.descriptor); // eslint-disable-line no-unused-vars const response = await TrezorConnect.blockchainSubscribe({ accounts, coin: network, @@ -96,7 +114,10 @@ export const subscribe = (network: string): PromiseAction => async (dispat await dispatch(Web3Actions.initWeb3(network)); }; -export const onBlockMined = (network: string): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onBlockMined = (network: string): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { // TODO: handle rollback, // check latest saved transaction blockhash against blockhheight @@ -131,7 +152,9 @@ export const onBlockMined = (network: string): PromiseAction => async (dis } }; -export const onNotification = (payload: $ElementType): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onNotification = ( + payload: $ElementType +): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const { notification } = payload; const account = getState().accounts.find(a => a.descriptor === notification.descriptor); if (!account) return; @@ -148,6 +171,8 @@ export const onNotification = (payload: $ElementType => async (dispatch: Dispatch): Promise => { +export const onError = (network: string): PromiseAction => async ( + dispatch: Dispatch +): Promise => { dispatch(Web3Actions.disconnect(network)); }; diff --git a/src/actions/ethereum/DiscoveryActions.js b/src/actions/ethereum/DiscoveryActions.js index e1f71f83..d7b8bc03 100644 --- a/src/actions/ethereum/DiscoveryActions.js +++ b/src/actions/ethereum/DiscoveryActions.js @@ -5,14 +5,7 @@ import EthereumjsUtil from 'ethereumjs-util'; import * as DISCOVERY from 'actions/constants/discovery'; import * as BlockchainActions from 'actions/ethereum/BlockchainActions'; -import type { - PromiseAction, - Dispatch, - GetState, - TrezorDevice, - Network, - Account, -} from 'flowtype'; +import type { PromiseAction, Dispatch, GetState, TrezorDevice, Network, Account } from 'flowtype'; import type { Discovery } from 'reducers/DiscoveryReducer'; export type DiscoveryStartAction = { @@ -28,7 +21,10 @@ export type DiscoveryStartAction = { // first iteration // generate public key for this account // start discovery process -export const begin = (device: TrezorDevice, network: Network): PromiseAction => async (): Promise => { +export const begin = ( + device: TrezorDevice, + network: Network +): PromiseAction => async (): Promise => { // get xpub from TREZOR const response = await TrezorConnect.getPublicKey({ device: { @@ -61,18 +57,26 @@ export const begin = (device: TrezorDevice, network: Network): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const discoverAccount = ( + device: TrezorDevice, + discoveryProcess: Discovery +): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const { config } = getState().localStorage; const network = config.networks.find(c => c.shortcut === discoveryProcess.network); if (!network) throw new Error('Discovery network not found'); const derivedKey = discoveryProcess.hdKey.derive(`m/${discoveryProcess.accountIndex}`); const path = discoveryProcess.basePath.concat(discoveryProcess.accountIndex); - const publicAddress: string = EthereumjsUtil.publicToAddress(derivedKey.publicKey, true).toString('hex'); + const publicAddress: string = EthereumjsUtil.publicToAddress( + derivedKey.publicKey, + true + ).toString('hex'); const ethAddress: string = EthereumjsUtil.toChecksumAddress(publicAddress); // TODO: check if address was created before - const account = await dispatch(BlockchainActions.discoverAccount(device, ethAddress, network.shortcut)); + const account = await dispatch( + BlockchainActions.discoverAccount(device, ethAddress, network.shortcut) + ); // const accountIsEmpty = account.transactions <= 0 && account.nonce <= 0 && account.balance === '0'; const empty = account.nonce <= 0 && account.balance === '0'; @@ -95,4 +99,4 @@ export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discover networkType: 'ethereum', nonce: account.nonce, }; -}; \ No newline at end of file +}; diff --git a/src/actions/ethereum/SendFormActions.js b/src/actions/ethereum/SendFormActions.js index 52894fd0..e48a13d0 100644 --- a/src/actions/ethereum/SendFormActions.js +++ b/src/actions/ethereum/SendFormActions.js @@ -36,9 +36,12 @@ const actions = [ ]; /* -* Called from WalletService -*/ -export const observe = (prevState: ReducersState, action: Action): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from WalletService + */ +export const observe = (prevState: ReducersState, action: Action): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { // ignore not listed actions if (actions.indexOf(action.type) < 0) return; @@ -68,14 +71,26 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction = let shouldUpdate: boolean = false; // check if "selectedAccount" reducer changed - shouldUpdate = reducerUtils.observeChanges(prevState.selectedAccount, currentState.selectedAccount, { - account: ['balance', 'nonce', 'tokens'], - }); - if (shouldUpdate && currentState.sendFormEthereum.currency !== currentState.sendFormEthereum.networkSymbol) { + shouldUpdate = reducerUtils.observeChanges( + prevState.selectedAccount, + currentState.selectedAccount, + { + account: ['balance', 'nonce', 'tokens'], + } + ); + if ( + shouldUpdate && + currentState.sendFormEthereum.currency !== currentState.sendFormEthereum.networkSymbol + ) { // make sure that this token is added into account const { account, tokens } = getState().selectedAccount; if (!account) return; - const token = reducerUtils.findToken(tokens, account.descriptor, currentState.sendFormEthereum.currency, account.deviceState); + const token = reducerUtils.findToken( + tokens, + account.descriptor, + currentState.sendFormEthereum.currency, + account.deviceState + ); if (!token) { // token not found, re-init form dispatch(init()); @@ -85,7 +100,10 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction = // check if "sendFormEthereum" reducer changed if (!shouldUpdate) { - shouldUpdate = reducerUtils.observeChanges(prevState.sendFormEthereum, currentState.sendFormEthereum); + shouldUpdate = reducerUtils.observeChanges( + prevState.sendFormEthereum, + currentState.sendFormEthereum + ); } if (shouldUpdate) { @@ -99,15 +117,15 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction = }; /* -* Called from "observe" action -* Initialize "sendFormEthereum" reducer data -* Get data either from session storage or "selectedAccount" reducer -*/ -export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const { - account, - network, - } = getState().selectedAccount; + * Called from "observe" action + * Initialize "sendFormEthereum" reducer data + * Get data either from session storage or "selectedAccount" reducer + */ +export const init = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { + const { account, network } = getState().selectedAccount; if (!account || !network) return; @@ -122,10 +140,15 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS return; } - const gasPrice: BigNumber = await dispatch(BlockchainActions.getGasPrice(network.shortcut, network.defaultGasPrice)); + const gasPrice: BigNumber = await dispatch( + BlockchainActions.getGasPrice(network.shortcut, network.defaultGasPrice) + ); const gasLimit = network.defaultGasLimit.toString(); const feeLevels = ValidationActions.getFeeLevels(network.symbol, gasPrice, gasLimit); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, initialState.selectedFeeLevel); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + initialState.selectedFeeLevel + ); dispatch({ type: SEND.INIT, @@ -145,17 +168,20 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS }; /* -* Called from UI from "advanced" button -*/ + * Called from UI from "advanced" button + */ export const toggleAdvanced = (): Action => ({ type: SEND.TOGGLE_ADVANCED, networkType: 'ethereum', }); /* -* Called from UI from "clear" button -*/ -export const onClear = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { + * Called from UI from "clear" button + */ +export const onClear = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const { network } = getState().selectedAccount; const { advanced } = getState().sendFormEthereum; @@ -164,10 +190,15 @@ export const onClear = (): AsyncAction => async (dispatch: Dispatch, getState: G // clear transaction draft from session storage dispatch(SessionStorageActions.clear()); - const gasPrice: BigNumber = await dispatch(BlockchainActions.getGasPrice(network.shortcut, network.defaultGasPrice)); + const gasPrice: BigNumber = await dispatch( + BlockchainActions.getGasPrice(network.shortcut, network.defaultGasPrice) + ); const gasLimit = network.defaultGasLimit.toString(); const feeLevels = ValidationActions.getFeeLevels(network.symbol, gasPrice, gasLimit); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, initialState.selectedFeeLevel); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + initialState.selectedFeeLevel + ); dispatch({ type: SEND.CLEAR, @@ -188,9 +219,12 @@ export const onClear = (): AsyncAction => async (dispatch: Dispatch, getState: G }; /* -* Called from UI on "address" field change -*/ -export const onAddressChange = (address: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "address" field change + */ +export const onAddressChange = (address: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().sendFormEthereum; dispatch({ type: SEND.CHANGE, @@ -205,9 +239,12 @@ export const onAddressChange = (address: string): ThunkAction => (dispatch: Disp }; /* -* Called from UI on "amount" field change -*/ -export const onAmountChange = (amount: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "amount" field change + */ +export const onAmountChange = (amount: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state = getState().sendFormEthereum; dispatch({ type: SEND.CHANGE, @@ -223,22 +260,32 @@ export const onAmountChange = (amount: string): ThunkAction => (dispatch: Dispat }; /* -* Called from UI on "currency" selection change -*/ -export const onCurrencyChange = (currency: { value: string, label: string }): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { - const { - account, - network, - } = getState().selectedAccount; + * Called from UI on "currency" selection change + */ +export const onCurrencyChange = (currency: { value: string, label: string }): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { + const { account, network } = getState().selectedAccount; if (!account || !network) return; const state = getState().sendFormEthereum; const isToken = currency.value !== state.networkSymbol; - const gasLimit = isToken ? network.defaultGasLimitTokens.toString() : network.defaultGasLimit.toString(); - - const feeLevels = ValidationActions.getFeeLevels(network.symbol, state.recommendedGasPrice, gasLimit, state.selectedFeeLevel); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, state.selectedFeeLevel); + const gasLimit = isToken + ? network.defaultGasLimitTokens.toString() + : network.defaultGasLimit.toString(); + + const feeLevels = ValidationActions.getFeeLevels( + network.symbol, + state.recommendedGasPrice, + gasLimit, + state.selectedFeeLevel + ); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + state.selectedFeeLevel + ); dispatch({ type: SEND.CHANGE, @@ -254,8 +301,8 @@ export const onCurrencyChange = (currency: { value: string, label: string }): Th }; /* -* Called from UI from "set max" button -*/ + * Called from UI from "set max" button + */ export const onSetMax = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { const state = getState().sendFormEthereum; dispatch({ @@ -271,9 +318,12 @@ export const onSetMax = (): ThunkAction => (dispatch: Dispatch, getState: GetSta }; /* -* Called from UI on "fee" selection change -*/ -export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "fee" selection change + */ +export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state = getState().sendFormEthereum; const isCustom = feeLevel.value === 'Custom'; @@ -292,7 +342,9 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => (dispatch: } else { // corner case: gas limit was changed by user OR by "estimateGasPrice" action // leave gasLimit as it is - newGasLimit = state.touched.gasLimit ? state.gasLimit : network.defaultGasLimit.toString(); + newGasLimit = state.touched.gasLimit + ? state.gasLimit + : network.defaultGasLimit.toString(); } newGasPrice = feeLevel.gasPrice; } @@ -311,18 +363,26 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => (dispatch: }; /* -* Called from UI from "update recommended fees" button -*/ -export const updateFeeLevels = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { - const { - account, - network, - } = getState().selectedAccount; + * Called from UI from "update recommended fees" button + */ +export const updateFeeLevels = (): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { + const { account, network } = getState().selectedAccount; if (!account || !network) return; const state: State = getState().sendFormEthereum; - const feeLevels = ValidationActions.getFeeLevels(network.symbol, state.recommendedGasPrice, state.gasLimit, state.selectedFeeLevel); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, state.selectedFeeLevel); + const feeLevels = ValidationActions.getFeeLevels( + network.symbol, + state.recommendedGasPrice, + state.gasLimit, + state.selectedFeeLevel + ); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + state.selectedFeeLevel + ); dispatch({ type: SEND.CHANGE, @@ -338,13 +398,17 @@ export const updateFeeLevels = (): ThunkAction => (dispatch: Dispatch, getState: }; /* -* Called from UI on "gas price" field change -*/ -export const onGasPriceChange = (gasPrice: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "gas price" field change + */ +export const onGasPriceChange = (gasPrice: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().sendFormEthereum; // switch to custom fee level let newSelectedFeeLevel = state.selectedFeeLevel; - if (state.selectedFeeLevel.value !== 'Custom') newSelectedFeeLevel = state.feeLevels.find(f => f.value === 'Custom'); + if (state.selectedFeeLevel.value !== 'Custom') + newSelectedFeeLevel = state.feeLevels.find(f => f.value === 'Custom'); dispatch({ type: SEND.CHANGE, @@ -360,16 +424,27 @@ export const onGasPriceChange = (gasPrice: string): ThunkAction => (dispatch: Di }; /* -* Called from UI on "data" field change -* OR from "estimateGasPrice" action -*/ -export const onGasLimitChange = (gasLimit: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "data" field change + * OR from "estimateGasPrice" action + */ +export const onGasLimitChange = (gasLimit: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const { network } = getState().selectedAccount; if (!network) return; const state: State = getState().sendFormEthereum; // recalculate feeLevels with recommended gasPrice - const feeLevels = ValidationActions.getFeeLevels(network.symbol, state.recommendedGasPrice, gasLimit, state.selectedFeeLevel); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, state.selectedFeeLevel); + const feeLevels = ValidationActions.getFeeLevels( + network.symbol, + state.recommendedGasPrice, + gasLimit, + state.selectedFeeLevel + ); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + state.selectedFeeLevel + ); dispatch({ type: SEND.CHANGE, @@ -387,9 +462,12 @@ export const onGasLimitChange = (gasLimit: string): ThunkAction => (dispatch: Di }; /* -* Called from UI on "nonce" field change -*/ -export const onNonceChange = (nonce: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "nonce" field change + */ +export const onNonceChange = (nonce: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().sendFormEthereum; dispatch({ type: SEND.CHANGE, @@ -404,9 +482,12 @@ export const onNonceChange = (nonce: string): ThunkAction => (dispatch: Dispatch }; /* -* Called from UI on "data" field change -*/ -export const onDataChange = (data: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "data" field change + */ +export const onDataChange = (data: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().sendFormEthereum; dispatch({ type: SEND.CHANGE, @@ -423,13 +504,18 @@ export const onDataChange = (data: string): ThunkAction => (dispatch: Dispatch, dispatch(estimateGasPrice()); }; -export const setDefaultGasLimit = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { +export const setDefaultGasLimit = (): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().sendFormEthereum; const { network } = getState().selectedAccount; if (!network) return; const isToken = state.currency !== state.networkSymbol; - const gasLimit = isToken ? network.defaultGasLimitTokens.toString() : network.defaultGasLimit.toString(); + const gasLimit = isToken + ? network.defaultGasLimitTokens.toString() + : network.defaultGasLimit.toString(); dispatch({ type: SEND.CHANGE, @@ -445,11 +531,14 @@ export const setDefaultGasLimit = (): ThunkAction => (dispatch: Dispatch, getSta }; /* -* Internal method -* Called from "onDataChange" action -* try to asynchronously download data from backend -*/ -const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { + * Internal method + * Called from "onDataChange" action + * try to asynchronously download data from backend + */ +const estimateGasPrice = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const state: State = getState().sendFormEthereum; const { network } = getState().selectedAccount; if (!network) { @@ -461,7 +550,11 @@ const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: const requestedData = state.data; if (!ethUtils.isHex(requestedData)) { // stop "calculatingGasLimit" process - dispatch(onGasLimitChange(requestedData.length > 0 ? state.gasLimit : network.defaultGasLimit.toString())); + dispatch( + onGasLimitChange( + requestedData.length > 0 ? state.gasLimit : network.defaultGasLimit.toString() + ) + ); return; } @@ -471,7 +564,14 @@ const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: return; } - const gasLimit = await dispatch(BlockchainActions.estimateGasLimit(network.shortcut, state.data, state.amount, state.gasPrice)); + const gasLimit = await dispatch( + BlockchainActions.estimateGasLimit( + network.shortcut, + state.data, + state.amount, + state.gasPrice + ) + ); // double check "data" field // possible race condition when data changed before backend respond @@ -481,14 +581,13 @@ const estimateGasPrice = (): AsyncAction => async (dispatch: Dispatch, getState: }; /* -* Called from UI from "send" button -*/ -export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const { - account, - network, - pending, - } = getState().selectedAccount; + * Called from UI from "send" button + */ +export const onSend = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { + const { account, network, pending } = getState().selectedAccount; if (!account || account.networkType !== 'ethereum' || !network) return; @@ -498,17 +597,26 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge const pendingNonce: number = reducerUtils.getPendingSequence(pending); const nonce = pendingNonce > 0 && pendingNonce >= account.nonce ? pendingNonce : account.nonce; - const txData = await dispatch(prepareEthereumTx({ - network: network.shortcut, - token: isToken ? reducerUtils.findToken(getState().tokens, account.descriptor, currentState.currency, account.deviceState) : null, - from: account.descriptor, - to: currentState.address, - amount: currentState.amount, - data: currentState.data, - gasLimit: currentState.gasLimit, - gasPrice: currentState.gasPrice, - nonce, - })); + const txData = await dispatch( + prepareEthereumTx({ + network: network.shortcut, + token: isToken + ? reducerUtils.findToken( + getState().tokens, + account.descriptor, + currentState.currency, + account.deviceState + ) + : null, + from: account.descriptor, + to: currentState.address, + amount: currentState.amount, + data: currentState.data, + gasLimit: currentState.gasLimit, + gasPrice: currentState.gasPrice, + nonce, + }) + ); const selected: ?TrezorDevice = getState().wallet.selectedDevice; if (!selected) return; @@ -586,22 +694,28 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge total: currentState.total, sequence: nonce, - tokens: isToken ? [{ - name: currentState.currency, - shortcut: currentState.currency, - value: currentState.amount, - }] : undefined, + tokens: isToken + ? [ + { + name: currentState.currency, + shortcut: currentState.currency, + value: currentState.amount, + }, + ] + : undefined, blockHeight: 0, blockHash: undefined, timestamp: undefined, }; - dispatch(BlockchainActions.onNotification({ - // $FlowIssue: missing coinInfo declaration - coin: {}, - notification: blockchainNotification, - })); + dispatch( + BlockchainActions.onNotification({ + // $FlowIssue: missing coinInfo declaration + coin: {}, + notification: blockchainNotification, + }) + ); // workaround end // clear session storage @@ -615,7 +729,11 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge payload: { type: 'success', title: 'Transaction success', - message: See transaction detail, + message: ( + + See transaction detail + + ), cancelable: true, actions: [], }, @@ -649,4 +767,4 @@ export default { onDataChange, onSend, onClear, -}; \ No newline at end of file +}; diff --git a/src/actions/ethereum/SendFormValidationActions.js b/src/actions/ethereum/SendFormValidationActions.js index 66d787d4..4ff4a5f6 100644 --- a/src/actions/ethereum/SendFormValidationActions.js +++ b/src/actions/ethereum/SendFormValidationActions.js @@ -7,30 +7,33 @@ import { findDevice, getPendingAmount, findToken } from 'reducers/utils'; import * as SEND from 'actions/constants/send'; import * as ethUtils from 'utils/ethUtils'; -import type { - Dispatch, - GetState, - PayloadAction, -} from 'flowtype'; +import type { Dispatch, GetState, PayloadAction } from 'flowtype'; import type { State, FeeLevel } from 'reducers/SendFormEthereumReducer'; // general regular expressions const NUMBER_RE: RegExp = new RegExp('^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)?|\\.[0-9]+)$'); const UPPERCASE_RE = new RegExp('^(.*[A-Z].*)$'); const ABS_RE = new RegExp('^[0-9]+$'); -const ETH_18_RE = new RegExp('^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$'); +const ETH_18_RE = new RegExp( + '^(0|0\\.([0-9]{0,18})?|[1-9][0-9]*\\.?([0-9]{0,18})?|\\.[0-9]{0,18})$' +); const dynamicRegexp = (decimals: number): RegExp => { if (decimals > 0) { - return new RegExp(`^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?|\\.[0-9]{1,${decimals}})$`); + return new RegExp( + `^(0|0\\.([0-9]{0,${decimals}})?|[1-9][0-9]*\\.?([0-9]{0,${decimals}})?|\\.[0-9]{1,${decimals}})$` + ); } return ABS_RE; }; /* -* Called from SendFormActions.observe -* Reaction for WEB3.GAS_PRICE_UPDATED action -*/ -export const onGasPriceUpdated = (network: string, gasPrice: string): PayloadAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from SendFormActions.observe + * Reaction for WEB3.GAS_PRICE_UPDATED action + */ +export const onGasPriceUpdated = (network: string, gasPrice: string): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): void => { // testing random data // function getRandomInt(min, max) { // return Math.floor(Math.random() * (max - min + 1)) + min; @@ -77,9 +80,12 @@ export const onGasPriceUpdated = (network: string, gasPrice: string): PayloadAct }; /* -* Recalculate amount, total and fees -*/ -export const validation = (): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Recalculate amount, total and fees + */ +export const validation = (): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { // clone deep nested object // to avoid overrides across state history let state: State = JSON.parse(JSON.stringify(getState().sendFormEthereum)); @@ -99,12 +105,11 @@ export const validation = (): PayloadAction => (dispatch: Dispatch, getSt return state; }; -export const recalculateTotalAmount = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { - const { - account, - tokens, - pending, - } = getState().selectedAccount; +export const recalculateTotalAmount = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { + const { account, tokens, pending } = getState().selectedAccount; if (!account) return $state; const state = { ...$state }; @@ -113,7 +118,12 @@ export const recalculateTotalAmount = ($state: State): PayloadAction => ( if (state.setMax) { const pendingAmount = getPendingAmount(pending, state.currency, isToken); if (isToken) { - const token = findToken(tokens, account.descriptor, state.currency, account.deviceState); + const token = findToken( + tokens, + account.descriptor, + state.currency, + account.deviceState + ); if (token) { state.amount = new BigNumber(token.balance).minus(pendingAmount).toFixed(); } @@ -140,8 +150,8 @@ export const updateCustomFeeLabel = ($state: State): PayloadAction => (): }; /* -* Address value validation -*/ + * Address value validation + */ export const addressValidation = ($state: State): PayloadAction => (): State => { const state = { ...$state }; if (!state.touched.address) return state; @@ -159,36 +169,52 @@ export const addressValidation = ($state: State): PayloadAction => (): St }; /* -* Address label assignation -*/ -export const addressLabel = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Address label assignation + */ +export const addressLabel = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.address || state.errors.address) return state; - const { - account, - network, - } = getState().selectedAccount; + const { account, network } = getState().selectedAccount; if (!account || !network) return state; const { address } = state; - const savedAccounts = getState().accounts.filter(a => a.descriptor.toLowerCase() === address.toLowerCase()); + const savedAccounts = getState().accounts.filter( + a => a.descriptor.toLowerCase() === address.toLowerCase() + ); if (savedAccounts.length > 0) { // check if found account belongs to this network const currentNetworkAccount = savedAccounts.find(a => a.network === network.shortcut); if (currentNetworkAccount) { - const device = findDevice(getState().devices, currentNetworkAccount.deviceID, currentNetworkAccount.deviceState); + const device = findDevice( + getState().devices, + currentNetworkAccount.deviceID, + currentNetworkAccount.deviceState + ); if (device) { - state.infos.address = `${device.instanceLabel} Account #${(currentNetworkAccount.index + 1)}`; + state.infos.address = `${ + device.instanceLabel + } Account #${currentNetworkAccount.index + 1}`; } } else { // corner-case: the same derivation path is used on different networks const otherNetworkAccount = savedAccounts[0]; - const device = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState); + const device = findDevice( + getState().devices, + otherNetworkAccount.deviceID, + otherNetworkAccount.deviceState + ); const { networks } = getState().localStorage.config; const otherNetwork = networks.find(c => c.shortcut === otherNetworkAccount.network); if (device && otherNetwork) { - state.warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`; + state.warnings.address = `Looks like it's ${ + device.instanceLabel + } Account #${otherNetworkAccount.index + 1} address of ${ + otherNetwork.name + } network`; } } } @@ -197,17 +223,16 @@ export const addressLabel = ($state: State): PayloadAction => (dispatch: }; /* -* Amount value validation -*/ -export const amountValidation = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Amount value validation + */ +export const amountValidation = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.amount) return state; - const { - account, - tokens, - pending, - } = getState().selectedAccount; + const { account, tokens, pending } = getState().selectedAccount; if (!account) return state; const { amount } = state; @@ -220,7 +245,12 @@ export const amountValidation = ($state: State): PayloadAction => (dispat const pendingAmount: BigNumber = getPendingAmount(pending, state.currency, isToken); if (isToken) { - const token = findToken(tokens, account.descriptor, state.currency, account.deviceState); + const token = findToken( + tokens, + account.descriptor, + state.currency, + account.deviceState + ); if (!token) return state; const decimalRegExp = dynamicRegexp(parseInt(token.decimals, 0)); @@ -228,14 +258,22 @@ export const amountValidation = ($state: State): PayloadAction => (dispat state.errors.amount = `Maximum ${token.decimals} decimals allowed`; } else if (new BigNumber(state.total).isGreaterThan(account.balance)) { state.errors.amount = `Not enough ${state.networkSymbol} to cover transaction fee`; - } else if (new BigNumber(state.amount).isGreaterThan(new BigNumber(token.balance).minus(pendingAmount))) { + } else if ( + new BigNumber(state.amount).isGreaterThan( + new BigNumber(token.balance).minus(pendingAmount) + ) + ) { state.errors.amount = 'Not enough funds'; } else if (new BigNumber(state.amount).isLessThanOrEqualTo('0')) { state.errors.amount = 'Amount is too low'; } } else if (!state.amount.match(ETH_18_RE)) { state.errors.amount = 'Maximum 18 decimals allowed'; - } else if (new BigNumber(state.total).isGreaterThan(new BigNumber(account.balance).minus(pendingAmount))) { + } else if ( + new BigNumber(state.total).isGreaterThan( + new BigNumber(account.balance).minus(pendingAmount) + ) + ) { state.errors.amount = 'Not enough funds'; } } @@ -243,15 +281,16 @@ export const amountValidation = ($state: State): PayloadAction => (dispat }; /* -* Gas limit value validation -*/ -export const gasLimitValidation = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Gas limit value validation + */ +export const gasLimitValidation = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.gasLimit) return state; - const { - network, - } = getState().selectedAccount; + const { network } = getState().selectedAccount; if (!network) return state; const { gasLimit } = state; @@ -263,7 +302,13 @@ export const gasLimitValidation = ($state: State): PayloadAction => (disp const gl: BigNumber = new BigNumber(gasLimit); if (gl.isLessThan(1)) { state.errors.gasLimit = 'Gas limit is too low'; - } else if (gl.isLessThan(state.currency !== state.networkSymbol ? network.defaultGasLimitTokens : network.defaultGasLimit)) { + } else if ( + gl.isLessThan( + state.currency !== state.networkSymbol + ? network.defaultGasLimitTokens + : network.defaultGasLimit + ) + ) { state.warnings.gasLimit = 'Gas limit is below recommended'; } } @@ -271,8 +316,8 @@ export const gasLimitValidation = ($state: State): PayloadAction => (disp }; /* -* Gas price value validation -*/ + * Gas price value validation + */ export const gasPriceValidation = ($state: State): PayloadAction => (): State => { const state = { ...$state }; if (!state.touched.gasPrice) return state; @@ -294,15 +339,16 @@ export const gasPriceValidation = ($state: State): PayloadAction => (): S }; /* -* Nonce value validation -*/ -export const nonceValidation = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Nonce value validation + */ +export const nonceValidation = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.nonce) return state; - const { - account, - } = getState().selectedAccount; + const { account } = getState().selectedAccount; if (!account || account.networkType !== 'ethereum') return state; const { nonce } = state; @@ -322,8 +368,8 @@ export const nonceValidation = ($state: State): PayloadAction => (dispatc }; /* -* Gas price value validation -*/ + * Gas price value validation + */ export const dataValidation = ($state: State): PayloadAction => (): State => { const state = { ...$state }; if (!state.touched.data || state.data.length === 0) return state; @@ -334,12 +380,16 @@ export const dataValidation = ($state: State): PayloadAction => (): State }; /* -* UTILITIES -*/ + * UTILITIES + */ export const calculateFee = (gasPrice: string, gasLimit: string): string => { try { - return EthereumjsUnits.convert(new BigNumber(gasPrice).times(gasLimit).toFixed(), 'gwei', 'ether'); + return EthereumjsUnits.convert( + new BigNumber(gasPrice).times(gasLimit).toFixed(), + 'gwei', + 'ether' + ); } catch (error) { return '0'; } @@ -358,7 +408,11 @@ export const calculateTotal = (amount: string, gasPrice: string, gasLimit: strin } }; -export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimit: string): string => { +export const calculateMaxAmount = ( + balance: BigNumber, + gasPrice: string, + gasLimit: string +): string => { try { // TODO - minus pendings const fee = calculateFee(gasPrice, gasLimit); @@ -370,22 +424,30 @@ export const calculateMaxAmount = (balance: BigNumber, gasPrice: string, gasLimi } }; -export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLimit: string, selected?: FeeLevel): Array => { +export const getFeeLevels = ( + symbol: string, + gasPrice: BigNumber | string, + gasLimit: string, + selected?: FeeLevel +): Array => { const price: BigNumber = typeof gasPrice === 'string' ? new BigNumber(gasPrice) : gasPrice; const quarter: BigNumber = price.dividedBy(4); const high: string = price.plus(quarter.times(2)).toFixed(); const low: string = price.minus(quarter.times(2)).toFixed(); - const customLevel: FeeLevel = selected && selected.value === 'Custom' ? { - value: 'Custom', - gasPrice: selected.gasPrice, - // label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }` - label: `${calculateFee(selected.gasPrice, gasLimit)} ${symbol}`, - } : { - value: 'Custom', - gasPrice: low, - label: '', - }; + const customLevel: FeeLevel = + selected && selected.value === 'Custom' + ? { + value: 'Custom', + gasPrice: selected.gasPrice, + // label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }` + label: `${calculateFee(selected.gasPrice, gasLimit)} ${symbol}`, + } + : { + value: 'Custom', + gasPrice: low, + label: '', + }; return [ { @@ -416,4 +478,4 @@ export const getSelectedFeeLevel = (feeLevels: Array, selected: FeeLev selectedFeeLevel = feeLevels.find(f => f.value === 'Normal'); } return selectedFeeLevel || selected; -}; \ No newline at end of file +}; diff --git a/src/actions/ripple/BlockchainActions.js b/src/actions/ripple/BlockchainActions.js index 72d898e3..cdf15610 100644 --- a/src/actions/ripple/BlockchainActions.js +++ b/src/actions/ripple/BlockchainActions.js @@ -17,8 +17,13 @@ import type { BlockchainFeeLevel, } from 'flowtype'; -export const subscribe = (network: string): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const accounts: Array = getState().accounts.filter(a => a.network === network).map(a => a.descriptor); +export const subscribe = (network: string): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { + const accounts: Array = getState() + .accounts.filter(a => a.network === network) + .map(a => a.descriptor); await TrezorConnect.blockchainSubscribe({ accounts, coin: network, @@ -28,7 +33,10 @@ export const subscribe = (network: string): PromiseAction => async (dispat // Get current known fee // Use default values from appConfig.json if it wasn't downloaded from blockchain yet // update them later, after onBlockMined event -export const getFeeLevels = (network: Network): PayloadAction> => (dispatch: Dispatch, getState: GetState): Array => { +export const getFeeLevels = (network: Network): PayloadAction> => ( + dispatch: Dispatch, + getState: GetState +): Array => { const blockchain = getState().blockchain.find(b => b.shortcut === network.shortcut); if (!blockchain || blockchain.feeLevels.length < 1) { return network.fee.levels.map(level => ({ @@ -39,7 +47,10 @@ export const getFeeLevels = (network: Network): PayloadAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onBlockMined = (network: string): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const blockchain = getState().blockchain.find(b => b.shortcut === network); if (!blockchain) return; // flowtype fallback @@ -69,9 +80,7 @@ export const onBlockMined = (network: string): PromiseAction => async (dis // level: 'transactions', // coin: network, // }); - // if (!response.success) return; - // response.payload.forEach((a, i) => { // if (a.transactions.length > 0) { // console.warn('APDEJTED!', a, i); @@ -87,7 +96,9 @@ export const onBlockMined = (network: string): PromiseAction => async (dis } }; -export const onNotification = (payload: $ElementType): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const onNotification = ( + payload: $ElementType +): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const { notification } = payload; const account = getState().accounts.find(a => a.descriptor === notification.descriptor); if (!account) return; @@ -103,7 +114,10 @@ export const onNotification = (payload: $ElementType => async (): Promise => ({ +export const begin = ( + device: TrezorDevice, + network: Network +): PromiseAction => async (): Promise => ({ type: DISCOVERY.START, networkType: 'ripple', network, device, }); -export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discovery): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { +export const discoverAccount = ( + device: TrezorDevice, + discoveryProcess: Discovery +): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { const { config } = getState().localStorage; const network = config.networks.find(c => c.shortcut === discoveryProcess.network); if (!network) throw new Error('Discovery network not found'); @@ -78,4 +77,4 @@ export const discoverAccount = (device: TrezorDevice, discoveryProcess: Discover sequence: account.sequence, reserve: toDecimalAmount(account.reserve, network.decimals), }; -}; \ No newline at end of file +}; diff --git a/src/actions/ripple/SendFormActions.js b/src/actions/ripple/SendFormActions.js index fc34916a..ffe9d54c 100644 --- a/src/actions/ripple/SendFormActions.js +++ b/src/actions/ripple/SendFormActions.js @@ -23,9 +23,12 @@ import * as BlockchainActions from './BlockchainActions'; import * as ValidationActions from './SendFormValidationActions'; /* -* Called from WalletService -*/ -export const observe = (prevState: ReducersState, action: Action): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from WalletService + */ +export const observe = (prevState: ReducersState, action: Action): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const currentState = getState(); // if action type is SEND.VALIDATION which is called as result of this process @@ -50,13 +53,20 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction = let shouldUpdate: boolean = false; // check if "selectedAccount" reducer changed - shouldUpdate = reducerUtils.observeChanges(prevState.selectedAccount, currentState.selectedAccount, { - account: ['balance', 'sequence'], - }); + shouldUpdate = reducerUtils.observeChanges( + prevState.selectedAccount, + currentState.selectedAccount, + { + account: ['balance', 'sequence'], + } + ); // check if "sendForm" reducer changed if (!shouldUpdate) { - shouldUpdate = reducerUtils.observeChanges(prevState.sendFormRipple, currentState.sendFormRipple); + shouldUpdate = reducerUtils.observeChanges( + prevState.sendFormRipple, + currentState.sendFormRipple + ); } if (shouldUpdate) { @@ -70,15 +80,15 @@ export const observe = (prevState: ReducersState, action: Action): ThunkAction = }; /* -* Called from "observe" action -* Initialize "sendFormRipple" reducer data -* Get data either from session storage or "selectedAccount" reducer -*/ -export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const { - account, - network, - } = getState().selectedAccount; + * Called from "observe" action + * Initialize "sendFormRipple" reducer data + * Get data either from session storage or "selectedAccount" reducer + */ +export const init = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { + const { account, network } = getState().selectedAccount; if (!account || account.networkType !== 'ripple' || !network) return; @@ -94,7 +104,10 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS const blockchainFeeLevels = dispatch(BlockchainActions.getFeeLevels(network)); const feeLevels = dispatch(ValidationActions.getFeeLevels(blockchainFeeLevels)); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, initialState.selectedFeeLevel); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + initialState.selectedFeeLevel + ); dispatch({ type: SEND.INIT, @@ -112,17 +125,20 @@ export const init = (): AsyncAction => async (dispatch: Dispatch, getState: GetS }; /* -* Called from UI from "advanced" button -*/ + * Called from UI from "advanced" button + */ export const toggleAdvanced = (): Action => ({ type: SEND.TOGGLE_ADVANCED, networkType: 'ripple', }); /* -* Called from UI from "clear" button -*/ -export const onClear = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { + * Called from UI from "clear" button + */ +export const onClear = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const { network } = getState().selectedAccount; const { advanced } = getState().sendFormRipple; @@ -133,7 +149,10 @@ export const onClear = (): AsyncAction => async (dispatch: Dispatch, getState: G const blockchainFeeLevels = dispatch(BlockchainActions.getFeeLevels(network)); const feeLevels = dispatch(ValidationActions.getFeeLevels(blockchainFeeLevels)); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, initialState.selectedFeeLevel); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + initialState.selectedFeeLevel + ); dispatch({ type: SEND.CLEAR, @@ -152,9 +171,12 @@ export const onClear = (): AsyncAction => async (dispatch: Dispatch, getState: G }; /* -* Called from UI on "address" field change -*/ -export const onAddressChange = (address: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "address" field change + */ +export const onAddressChange = (address: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().sendFormRipple; dispatch({ type: SEND.CHANGE, @@ -169,9 +191,12 @@ export const onAddressChange = (address: string): ThunkAction => (dispatch: Disp }; /* -* Called from UI on "amount" field change -*/ -export const onAmountChange = (amount: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "amount" field change + */ +export const onAmountChange = (amount: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state = getState().sendFormRipple; dispatch({ type: SEND.CHANGE, @@ -187,8 +212,8 @@ export const onAmountChange = (amount: string): ThunkAction => (dispatch: Dispat }; /* -* Called from UI from "set max" button -*/ + * Called from UI from "set max" button + */ export const onSetMax = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { const state = getState().sendFormRipple; dispatch({ @@ -204,9 +229,12 @@ export const onSetMax = (): ThunkAction => (dispatch: Dispatch, getState: GetSta }; /* -* Called from UI on "fee" selection change -*/ -export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "fee" selection change + */ +export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state = getState().sendFormRipple; const isCustom = feeLevel.value === 'Custom'; @@ -225,19 +253,24 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => (dispatch: }; /* -* Called from UI from "update recommended fees" button -*/ -export const updateFeeLevels = (): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { - const { - account, - network, - } = getState().selectedAccount; + * Called from UI from "update recommended fees" button + */ +export const updateFeeLevels = (): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { + const { account, network } = getState().selectedAccount; if (!account || !network) return; const blockchainFeeLevels = dispatch(BlockchainActions.getFeeLevels(network)); const state: State = getState().sendFormRipple; - const feeLevels = dispatch(ValidationActions.getFeeLevels(blockchainFeeLevels, state.selectedFeeLevel)); - const selectedFeeLevel = ValidationActions.getSelectedFeeLevel(feeLevels, state.selectedFeeLevel); + const feeLevels = dispatch( + ValidationActions.getFeeLevels(blockchainFeeLevels, state.selectedFeeLevel) + ); + const selectedFeeLevel = ValidationActions.getSelectedFeeLevel( + feeLevels, + state.selectedFeeLevel + ); dispatch({ type: SEND.CHANGE, @@ -252,16 +285,20 @@ export const updateFeeLevels = (): ThunkAction => (dispatch: Dispatch, getState: }; /* -* Called from UI on "advanced / fee" field change -*/ -export const onFeeChange = (fee: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "advanced / fee" field change + */ +export const onFeeChange = (fee: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const { network } = getState().selectedAccount; if (!network) return; const state: State = getState().sendFormRipple; // switch to custom fee level let newSelectedFeeLevel = state.selectedFeeLevel; - if (state.selectedFeeLevel.value !== 'Custom') newSelectedFeeLevel = state.feeLevels.find(f => f.value === 'Custom'); + if (state.selectedFeeLevel.value !== 'Custom') + newSelectedFeeLevel = state.feeLevels.find(f => f.value === 'Custom'); dispatch({ type: SEND.CHANGE, @@ -277,9 +314,12 @@ export const onFeeChange = (fee: string): ThunkAction => (dispatch: Dispatch, ge }; /* -* Called from UI on "advanced / destination tag" field change -*/ -export const onDestinationTagChange = (destinationTag: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from UI on "advanced / destination tag" field change + */ +export const onDestinationTagChange = (destinationTag: string): ThunkAction => ( + dispatch: Dispatch, + getState: GetState +): void => { const state: State = getState().sendFormRipple; dispatch({ type: SEND.CHANGE, @@ -294,13 +334,13 @@ export const onDestinationTagChange = (destinationTag: string): ThunkAction => ( }; /* -* Called from UI from "send" button -*/ -export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: GetState): Promise => { - const { - account, - network, - } = getState().selectedAccount; + * Called from UI from "send" button + */ +export const onSend = (): AsyncAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { + const { account, network } = getState().selectedAccount; const selected: ?TrezorDevice = getState().wallet.selectedDevice; if (!selected) return; @@ -401,4 +441,4 @@ export default { onDestinationTagChange, onSend, onClear, -}; \ No newline at end of file +}; diff --git a/src/actions/ripple/SendFormValidationActions.js b/src/actions/ripple/SendFormValidationActions.js index 6ef5bbca..4b2055ff 100644 --- a/src/actions/ripple/SendFormValidationActions.js +++ b/src/actions/ripple/SendFormValidationActions.js @@ -21,10 +21,13 @@ const NUMBER_RE: RegExp = new RegExp('^(0|0\\.([0-9]+)?|[1-9][0-9]*\\.?([0-9]+)? const XRP_6_RE = new RegExp('^(0|0\\.([0-9]{0,6})?|[1-9][0-9]*\\.?([0-9]{0,6})?|\\.[0-9]{0,6})$'); /* -* Called from SendFormActions.observe -* Reaction for BLOCKCHAIN.FEE_UPDATED action -*/ -export const onFeeUpdated = (network: string, feeLevels: Array): PayloadAction => (dispatch: Dispatch, getState: GetState): void => { + * Called from SendFormActions.observe + * Reaction for BLOCKCHAIN.FEE_UPDATED action + */ +export const onFeeUpdated = ( + network: string, + feeLevels: Array +): PayloadAction => (dispatch: Dispatch, getState: GetState): void => { const state = getState().sendFormRipple; if (network === state.networkSymbol) return; @@ -58,9 +61,12 @@ export const onFeeUpdated = (network: string, feeLevels: Array => (dispatch: Dispatch, getState: GetState): State => { + * Recalculate amount, total and fees + */ +export const validation = (prevState: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { // clone deep nested object // to avoid overrides across state history let state: State = JSON.parse(JSON.stringify(getState().sendFormRipple)); @@ -81,12 +87,11 @@ export const validation = (prevState: State): PayloadAction => (dispatch: return state; }; -const recalculateTotalAmount = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { - const { - account, - network, - pending, - } = getState().selectedAccount; +const recalculateTotalAmount = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { + const { account, network, pending } = getState().selectedAccount; if (!account || account.networkType !== 'ripple' || !network) return $state; const state = { ...$state }; @@ -94,7 +99,9 @@ const recalculateTotalAmount = ($state: State): PayloadAction => (dispatc if (state.setMax) { const pendingAmount = getPendingAmount(pending, state.networkSymbol, false); - const availableBalance = new BigNumber(account.balance).minus(account.reserve).minus(pendingAmount); + const availableBalance = new BigNumber(account.balance) + .minus(account.reserve) + .minus(pendingAmount); state.amount = calculateMaxAmount(availableBalance, fee); } @@ -102,7 +109,10 @@ const recalculateTotalAmount = ($state: State): PayloadAction => (dispatc return state; }; -const updateCustomFeeLabel = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { +const updateCustomFeeLabel = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const { network } = getState().selectedAccount; if (!network) return $state; // flowtype fallback @@ -118,9 +128,12 @@ const updateCustomFeeLabel = ($state: State): PayloadAction => (dispatch: }; /* -* Address value validation -*/ -const addressValidation = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Address value validation + */ +const addressValidation = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.address) return state; @@ -140,10 +153,13 @@ const addressValidation = ($state: State): PayloadAction => (dispatch: Di }; /* -* Address balance validation -* Fetch data from trezor-connect and set minimum required amount in reducer -*/ -const addressBalanceValidation = ($state: State): PromiseAction => async (dispatch: Dispatch, getState: GetState): Promise => { + * Address balance validation + * Fetch data from trezor-connect and set minimum required amount in reducer + */ +const addressBalanceValidation = ($state: State): PromiseAction => async ( + dispatch: Dispatch, + getState: GetState +): Promise => { const { network } = getState().selectedAccount; if (!network) return; @@ -177,36 +193,52 @@ const addressBalanceValidation = ($state: State): PromiseAction => async ( }; /* -* Address label assignation -*/ -const addressLabel = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Address label assignation + */ +const addressLabel = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.address || state.errors.address) return state; - const { - account, - network, - } = getState().selectedAccount; + const { account, network } = getState().selectedAccount; if (!account || !network) return state; const { address } = state; - const savedAccounts = getState().accounts.filter(a => a.descriptor.toLowerCase() === address.toLowerCase()); + const savedAccounts = getState().accounts.filter( + a => a.descriptor.toLowerCase() === address.toLowerCase() + ); if (savedAccounts.length > 0) { // check if found account belongs to this network const currentNetworkAccount = savedAccounts.find(a => a.network === network.shortcut); if (currentNetworkAccount) { - const device = findDevice(getState().devices, currentNetworkAccount.deviceID, currentNetworkAccount.deviceState); + const device = findDevice( + getState().devices, + currentNetworkAccount.deviceID, + currentNetworkAccount.deviceState + ); if (device) { - state.infos.address = `${device.instanceLabel} Account #${(currentNetworkAccount.index + 1)}`; + state.infos.address = `${ + device.instanceLabel + } Account #${currentNetworkAccount.index + 1}`; } } else { // corner-case: the same derivation path is used on different networks const otherNetworkAccount = savedAccounts[0]; - const device = findDevice(getState().devices, otherNetworkAccount.deviceID, otherNetworkAccount.deviceState); + const device = findDevice( + getState().devices, + otherNetworkAccount.deviceID, + otherNetworkAccount.deviceState + ); const { networks } = getState().localStorage.config; const otherNetwork = networks.find(c => c.shortcut === otherNetworkAccount.network); if (device && otherNetwork) { - state.warnings.address = `Looks like it's ${device.instanceLabel} Account #${(otherNetworkAccount.index + 1)} address of ${otherNetwork.name} network`; + state.warnings.address = `Looks like it's ${ + device.instanceLabel + } Account #${otherNetworkAccount.index + 1} address of ${ + otherNetwork.name + } network`; } } } @@ -215,16 +247,16 @@ const addressLabel = ($state: State): PayloadAction => (dispatch: Dispatc }; /* -* Amount value validation -*/ -const amountValidation = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Amount value validation + */ +const amountValidation = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.amount) return state; - const { - account, - pending, - } = getState().selectedAccount; + const { account, pending } = getState().selectedAccount; if (!account || account.networkType !== 'ripple') return state; const { amount } = state; @@ -236,32 +268,44 @@ const amountValidation = ($state: State): PayloadAction => (dispatch: Dis const pendingAmount: BigNumber = getPendingAmount(pending, state.networkSymbol); if (!state.amount.match(XRP_6_RE)) { state.errors.amount = 'Maximum 6 decimals allowed'; - } else if (new BigNumber(state.total).isGreaterThan(new BigNumber(account.balance).minus(pendingAmount))) { + } else if ( + new BigNumber(state.total).isGreaterThan( + new BigNumber(account.balance).minus(pendingAmount) + ) + ) { state.errors.amount = 'Not enough funds'; } } - if (!state.errors.amount && new BigNumber(account.balance).minus(state.total).lt(account.reserve)) { - state.errors.amount = `Not enough funds. Reserved amount for this account is ${account.reserve} ${state.networkSymbol}`; + if ( + !state.errors.amount && + new BigNumber(account.balance).minus(state.total).lt(account.reserve) + ) { + state.errors.amount = `Not enough funds. Reserved amount for this account is ${ + account.reserve + } ${state.networkSymbol}`; } if (!state.errors.amount && new BigNumber(state.amount).lt(state.minAmount)) { - state.errors.amount = `Amount is too low. Minimum amount for creating a new account is ${state.minAmount} ${state.networkSymbol}`; + state.errors.amount = `Amount is too low. Minimum amount for creating a new account is ${ + state.minAmount + } ${state.networkSymbol}`; } return state; }; /* -* Fee value validation -*/ -export const feeValidation = ($state: State): PayloadAction => (dispatch: Dispatch, getState: GetState): State => { + * Fee value validation + */ +export const feeValidation = ($state: State): PayloadAction => ( + dispatch: Dispatch, + getState: GetState +): State => { const state = { ...$state }; if (!state.touched.fee) return state; - const { - network, - } = getState().selectedAccount; + const { network } = getState().selectedAccount; if (!network) return state; const { fee } = state; @@ -280,8 +324,8 @@ export const feeValidation = ($state: State): PayloadAction => (dispatch: return state; }; /* -* Destination Tag value validation -*/ + * Destination Tag value validation + */ export const destinationTagValidation = ($state: State): PayloadAction => (): State => { const state = { ...$state }; if (!state.touched.destinationTag) return state; @@ -293,10 +337,9 @@ export const destinationTagValidation = ($state: State): PayloadAction => return state; }; - /* -* UTILITIES -*/ + * UTILITIES + */ const calculateTotal = (amount: string, fee: string): string => { try { @@ -323,7 +366,10 @@ const calculateMaxAmount = (balance: BigNumber, fee: string): string => { }; // Generate FeeLevel dataset for "fee" select -export const getFeeLevels = (feeLevels: Array, selected?: FeeLevel): PayloadAction> => (dispatch: Dispatch, getState: GetState): Array => { +export const getFeeLevels = ( + feeLevels: Array, + selected?: FeeLevel +): PayloadAction> => (dispatch: Dispatch, getState: GetState): Array => { const { network } = getState().selectedAccount; if (!network) return []; // flowtype fallback @@ -335,15 +381,18 @@ export const getFeeLevels = (feeLevels: Array, selected?: Fe })); // add "Custom" level - const customLevel = selected && selected.value === 'Custom' ? { - value: 'Custom', - fee: selected.fee, - label: `${toDecimalAmount(selected.fee, network.decimals)} ${network.symbol}`, - } : { - value: 'Custom', - fee: '0', - label: '', - }; + const customLevel = + selected && selected.value === 'Custom' + ? { + value: 'Custom', + fee: selected.fee, + label: `${toDecimalAmount(selected.fee, network.decimals)} ${network.symbol}`, + } + : { + value: 'Custom', + fee: '0', + label: '', + }; return levels.concat([customLevel]); }; diff --git a/src/components/Backdrop/index.js b/src/components/Backdrop/index.js index 8752d94b..4b75732a 100644 --- a/src/components/Backdrop/index.js +++ b/src/components/Backdrop/index.js @@ -3,7 +3,6 @@ import styled, { css } from 'styled-components'; import PropTypes from 'prop-types'; import { FADE_IN } from 'config/animations'; - const StyledBackdrop = styled.div` width: 100%; height: 100%; @@ -11,21 +10,17 @@ const StyledBackdrop = styled.div` z-index: 100; left: 0; top: 0; - background-color: rgba(0,0,0,0.5); + background-color: rgba(0, 0, 0, 0.5); - ${props => props.animated && css` - animation: ${FADE_IN} 0.3s; - `}; + ${props => + props.animated && + css` + animation: ${FADE_IN} 0.3s; + `}; `; -const Backdrop = ({ - className, - show, - animated, - onClick, -}) => ( - show ? : null -); +const Backdrop = ({ className, show, animated, onClick }) => + show ? : null; Backdrop.propTypes = { show: PropTypes.bool, diff --git a/src/components/Button/index.js b/src/components/Button/index.js index 65f56aa9..b7b57b22 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -17,8 +17,8 @@ type Props = { isWhite?: boolean, isWebUsb?: boolean, isTransparent?: boolean, - dataTest?: string -} + dataTest?: string, +}; const Wrapper = styled.button` padding: ${props => (props.icon ? '4px 24px 4px 15px' : '11px 24px')}; @@ -43,97 +43,105 @@ const Wrapper = styled.button` box-shadow: 0 0px 6px 0 ${colors.INPUT_FOCUSED_SHADOW}; } - ${props => props.isDisabled && css` - pointer-events: none; - color: ${colors.TEXT_SECONDARY}; - background: ${colors.GRAY_LIGHT}; - `} - - ${props => props.isWhite && css` - background: ${colors.WHITE}; - color: ${colors.TEXT_SECONDARY}; - border: 1px solid ${colors.DIVIDER}; - - &:focus { - border-color: ${colors.INPUT_FOCUSED_BORDER}; - } - - &:hover { - color: ${colors.TEXT_PRIMARY}; - background: ${colors.DIVIDER}; - } - - &:active { - color: ${colors.TEXT_PRIMARY}; - background: ${colors.DIVIDER}; - } - `} - - ${props => props.isTransparent && css` - background: transparent; - border: 0px; - color: ${colors.TEXT_SECONDARY}; - - &:focus { - color: ${colors.TEXT_PRIMARY}; - box-shadow: none; - } - - &:hover, - &:active { - color: ${colors.TEXT_PRIMARY}; + ${props => + props.isDisabled && + css` + pointer-events: none; + color: ${colors.TEXT_SECONDARY}; + background: ${colors.GRAY_LIGHT}; + `} + + ${props => + props.isWhite && + css` + background: ${colors.WHITE}; + color: ${colors.TEXT_SECONDARY}; + border: 1px solid ${colors.DIVIDER}; + + &:focus { + border-color: ${colors.INPUT_FOCUSED_BORDER}; + } + + &:hover { + color: ${colors.TEXT_PRIMARY}; + background: ${colors.DIVIDER}; + } + + &:active { + color: ${colors.TEXT_PRIMARY}; + background: ${colors.DIVIDER}; + } + `} + + ${props => + props.isTransparent && + css` background: transparent; - } - `} - - ${props => props.isWebUsb && css` - position: relative; - padding: 12px 24px 12px 40px; - background: transparent; - color: ${colors.GREEN_PRIMARY}; - border: 1px solid ${colors.GREEN_PRIMARY}; - transition: ${TRANSITION.HOVER}; - - &:before, - &:after { - content: ''; - position: absolute; - background: ${colors.GREEN_PRIMARY}; - top: 0; - bottom: 0; - margin: auto; - transition: ${TRANSITION.HOVER}; - } + border: 0px; + color: ${colors.TEXT_SECONDARY}; - &:before { - width: 12px; - height: 2px; - left: 18px; - } + &:focus { + color: ${colors.TEXT_PRIMARY}; + box-shadow: none; + } - &:after { - width: 2px; - height: 12px; - left: 23px; - } + &:hover, + &:active { + color: ${colors.TEXT_PRIMARY}; + background: transparent; + } + `} - &:hover { - background: ${colors.GREEN_PRIMARY}; - color: ${colors.WHITE}; + ${props => + props.isWebUsb && + css` + position: relative; + padding: 12px 24px 12px 40px; + background: transparent; + color: ${colors.GREEN_PRIMARY}; + border: 1px solid ${colors.GREEN_PRIMARY}; + transition: ${TRANSITION.HOVER}; &:before, &:after { - background: ${colors.WHITE}; + content: ''; + position: absolute; + background: ${colors.GREEN_PRIMARY}; + top: 0; + bottom: 0; + margin: auto; + transition: ${TRANSITION.HOVER}; + } + + &:before { + width: 12px; + height: 2px; + left: 18px; + } + + &:after { + width: 2px; + height: 12px; + left: 23px; + } + + &:hover { + background: ${colors.GREEN_PRIMARY}; + color: ${colors.WHITE}; + + &:before, + &:after { + background: ${colors.WHITE}; + } + } + + iframe { + position: absolute; + top: 0; + left: 0; + z-index: 1; } - } - - iframe { - position: absolute; - top: 0; - left: 0; - z-index: 1; - } - `} + `} `; const Button = ({ @@ -182,4 +190,4 @@ Button.propTypes = { dataTest: PropTypes.string, }; -export default Button; \ No newline at end of file +export default Button; diff --git a/src/components/Checkbox/index.js b/src/components/Checkbox/index.js index 23b134a9..21dac698 100644 --- a/src/components/Checkbox/index.js +++ b/src/components/Checkbox/index.js @@ -12,7 +12,7 @@ type Props = { onClick: (event: KeyboardEvent) => void, isChecked: boolean, children: React.Node, -} +}; const Wrapper = styled.div` display: flex; @@ -26,8 +26,7 @@ const Wrapper = styled.div` } `; -const Tick = styled.div` -`; +const Tick = styled.div``; const IconWrapper = styled.div` display: flex; @@ -42,9 +41,11 @@ const IconWrapper = styled.div` &:hover, &:focus { - ${props => !props.isChecked && css` - border: 1px solid ${colors.GREEN_PRIMARY}; - `} + ${props => + !props.isChecked && + css` + border: 1px solid ${colors.GREEN_PRIMARY}; + `} background: ${props => (props.isChecked ? colors.GREEN_PRIMARY : colors.WHITE)}; } `; @@ -70,17 +71,9 @@ class Checkbox extends React.PureComponent { } render() { - const { - isChecked, - children, - onClick, - } = this.props; + const { isChecked, children, onClick } = this.props; return ( - this.handleKeyboard(event)} - tabIndex={0} - > + this.handleKeyboard(event)} tabIndex={0}> {isChecked && ( @@ -91,8 +84,7 @@ class Checkbox extends React.PureComponent { icon={icons.SUCCESS} /> - ) - } + )} diff --git a/src/components/DeviceHeader/index.js b/src/components/DeviceHeader/index.js index 93edaebe..ab21dc26 100644 --- a/src/components/DeviceHeader/index.js +++ b/src/components/DeviceHeader/index.js @@ -2,12 +2,7 @@ import React from 'react'; import styled, { css } from 'styled-components'; import PropTypes from 'prop-types'; -import { - getStatusColor, - getStatusName, - getStatus, - getVersion, -} from 'utils/device'; +import { getStatusColor, getStatusName, getStatus, getVersion } from 'utils/device'; import TrezorImage from 'components/images/TrezorImage'; import colors from 'config/colors'; import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; @@ -27,19 +22,26 @@ const Wrapper = styled.div` border-radius: 4px 0 0 0; box-shadow: ${props => (props.disabled ? 'none' : '0 3px 8px rgba(0, 0, 0, 0.04)')}; - ${props => (props.isOpen || !props.isSelected) && css` - box-shadow: none; - `} + ${props => + (props.isOpen || !props.isSelected) && + css` + box-shadow: none; + `} - ${props => props.disabled && css` - cursor: default; - `} + ${props => + props.disabled && + css` + cursor: default; + `} - ${props => props.isHoverable && !props.disabled && css` - &:hover { - background: ${colors.GRAY_LIGHT}; - } - `} + ${props => + props.isHoverable && + !props.disabled && + css` + &:hover { + background: ${colors.GRAY_LIGHT}; + } + `} `; const LabelWrapper = styled.div` @@ -88,7 +90,6 @@ const Dot = styled.div` height: 10px; `; - const DeviceHeader = ({ isOpen, icon, @@ -120,9 +121,7 @@ const DeviceHeader = ({ {device.instanceLabel} {getStatusName(status)} - - {icon && !disabled && isAccessible && icon} - + {icon && !disabled && isAccessible && icon} ); }; diff --git a/src/components/Footer/index.js b/src/components/Footer/index.js index f2cf344b..87ea347b 100644 --- a/src/components/Footer/index.js +++ b/src/components/Footer/index.js @@ -9,7 +9,6 @@ import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { FormattedMessage } from 'react-intl'; - import colors from 'config/colors'; import { FONT_SIZE } from 'config/variables'; import * as LogActions from 'actions/LogActions'; @@ -19,7 +18,7 @@ type Props = { opened: boolean, isLanding: boolean, toggle: () => any, -} +}; const Wrapper = styled.div` width: 100%; @@ -62,17 +61,27 @@ const Footer = ({ opened, toggle, isLanding }: Props) => ( © {getYear(new Date())} - SatoshiLabs + + SatoshiLabs + - { opened ? 'Hide Log' : 'Show Log' } + + {opened ? 'Hide Log' : 'Show Log'} + {!isLanding && ( Coingecko }} + values={{ + service: ( + + Coingecko + + ), + }} /> )} @@ -93,4 +102,7 @@ const mapDispatchToProps = dispatch => ({ toggle: bindActionCreators(LogActions.toggle, dispatch), }); -export default connect(mapStateToProps, mapDispatchToProps)(Footer); +export default connect( + mapStateToProps, + mapDispatchToProps +)(Footer); diff --git a/src/components/Footer/index.messages.js b/src/components/Footer/index.messages.js index cd41c958..74b84241 100644 --- a/src/components/Footer/index.messages.js +++ b/src/components/Footer/index.messages.js @@ -14,4 +14,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/Header/components/LanguagePicker/Container.js b/src/components/Header/components/LanguagePicker/Container.js index 78287134..d811287c 100644 --- a/src/components/Header/components/LanguagePicker/Container.js +++ b/src/components/Header/components/LanguagePicker/Container.js @@ -11,26 +11,31 @@ import LanguagePicker from './index'; type StateProps = { language: string, -} +}; type DispatchProps = { fetchLocale: typeof WalletActions.fetchLocale, }; -type OwnProps = { - -} +type OwnProps = {}; export type Props = StateProps & DispatchProps; -const mapStateToProps: MapStateToProps = (state: State): StateProps => ({ +const mapStateToProps: MapStateToProps = ( + state: State +): StateProps => ({ language: state.wallet.language, }); -const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ +const mapDispatchToProps: MapDispatchToProps = ( + dispatch: Dispatch +): DispatchProps => ({ fetchLocale: bindActionCreators(WalletActions.fetchLocale, dispatch), }); export default withRouter( - connect(mapStateToProps, mapDispatchToProps)(LanguagePicker), + connect( + mapStateToProps, + mapDispatchToProps + )(LanguagePicker) ); diff --git a/src/components/Header/components/LanguagePicker/index.js b/src/components/Header/components/LanguagePicker/index.js index 590721a6..a833f8ac 100644 --- a/src/components/Header/components/LanguagePicker/index.js +++ b/src/components/Header/components/LanguagePicker/index.js @@ -80,8 +80,7 @@ const styles = { }), }; - -const buildOption = (langCode) => { +const buildOption = langCode => { const lang = LANGUAGE.find(l => l.code === langCode); return { value: lang.code, label: lang.name }; }; @@ -99,9 +98,7 @@ const LanguagePicker = ({ language, fetchLocale }: Props) => ( isClearable={false} onChange={option => fetchLocale(option.value)} value={buildOption(language)} - options={ - LANGUAGE.map(lang => buildOption(lang.code)) - } + options={LANGUAGE.map(lang => buildOption(lang.code))} /> ); diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 47e63098..19433cef 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -53,16 +53,14 @@ const MenuToggler = styled.div` cursor: pointer; user-select: none; padding: 10px 0px; - transition: all .1s ease-in; + transition: all 0.1s ease-in; @media screen and (max-width: ${SCREEN_SIZE.SM}) { display: flex; } `; -const TogglerText = styled.div` - -`; +const TogglerText = styled.div``; const TREZOR = styled.div``; const T = styled.div``; @@ -75,11 +73,11 @@ const Logo = styled.div` ${T} { display: none; width: 20px; - } + } ${TREZOR} { width: 100px; - } + } svg { fill: ${colors.WHITE}; @@ -95,11 +93,11 @@ const Logo = styled.div` /* hides full width trezor logo, shows only trezor icon */ ${TREZOR} { display: none; - } + } ${T} { display: inherit; - } + } } `; @@ -131,7 +129,7 @@ const Projects = styled.div` const A = styled.a` color: ${colors.WHITE}; margin-left: 24px; - transition: all .1s ease-in; + transition: all 0.1s ease-in; white-space: nowrap; &:visited { @@ -153,33 +151,24 @@ type Props = { sidebarEnabled?: boolean, sidebarOpened?: ?boolean, toggleSidebar?: toggleSidebarType, - }; const Header = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: Props) => ( - { sidebarEnabled && ( + {sidebarEnabled && ( {sidebarOpened ? ( <> - + ) : ( <> - + @@ -191,7 +180,15 @@ const Header = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: Props) => ( - + @@ -202,7 +199,15 @@ const Header = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: Props) => ( - + @@ -210,10 +215,18 @@ const Header = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: Props) => ( - - - - + + + + + + + + + + + + @@ -221,4 +234,4 @@ const Header = ({ sidebarEnabled, sidebarOpened, toggleSidebar }: Props) => ( ); -export default Header; \ No newline at end of file +export default Header; diff --git a/src/components/Header/index.messages.js b/src/components/Header/index.messages.js index 7c809494..d9f5f048 100644 --- a/src/components/Header/index.messages.js +++ b/src/components/Header/index.messages.js @@ -35,4 +35,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/Heading/index.js b/src/components/Heading/index.js index 84e43280..6265414e 100644 --- a/src/components/Heading/index.js +++ b/src/components/Heading/index.js @@ -22,8 +22,9 @@ const H2 = styled.h2` font-size: ${FONT_SIZE.H2}; padding-bottom: 10px; - ${props => props.claim - && css` + ${props => + props.claim && + css` font-size: ${FONT_SIZE.HUGE}; padding-bottom: 24px; `}; @@ -41,6 +42,4 @@ const H4 = styled.h4` padding-bottom: 10px; `; -export { - H1, H2, H3, H4, -}; +export { H1, H2, H3, H4 }; diff --git a/src/components/Icon/index.js b/src/components/Icon/index.js index 7773a6d3..df18cb1c 100644 --- a/src/components/Icon/index.js +++ b/src/components/Icon/index.js @@ -17,7 +17,7 @@ type Props = { onMouseLeave?: () => void, onFocus?: () => void, onClick?: () => void, -} +}; const chooseIconAnimationType = (canAnimate, isActive) => { if (canAnimate) { @@ -49,11 +49,12 @@ const rotate180down = keyframes` `; const SvgWrapper = styled.svg` - animation: ${props => chooseIconAnimationType(props.canAnimate, props.isActive)} 0.2s linear 1 forwards; + animation: ${props => chooseIconAnimationType(props.canAnimate, props.isActive)} 0.2s linear 1 + forwards; :hover { path { - fill: ${props => props.hoverColor} + fill: ${props => props.hoverColor}; } } `; @@ -93,12 +94,7 @@ const Icon = ({ onClick={onClick} > {icon.map(path => ( - + ))} ); @@ -117,4 +113,4 @@ Icon.propTypes = { onClick: PropTypes.func, }; -export default Icon; \ No newline at end of file +export default Icon; diff --git a/src/components/Link/index.js b/src/components/Link/index.js index 49278e3a..38cbd4dc 100644 --- a/src/components/Link/index.js +++ b/src/components/Link/index.js @@ -11,25 +11,33 @@ const A = styled.a` transition: ${TRANSITION.HOVER}; font-size: ${FONT_SIZE.SMALL}; - ${props => props.isGreen && css` - text-decoration: underline; - text-decoration-color: ${colors.GREEN_PRIMARY}; - `} - ${props => props.isGray && css` - text-decoration: underline; - text-decoration-color: ${colors.TEXT_SECONDARY}; - `} + ${props => + props.isGreen && + css` + text-decoration: underline; + text-decoration-color: ${colors.GREEN_PRIMARY}; + `} + ${props => + props.isGray && + css` + text-decoration: underline; + text-decoration-color: ${colors.TEXT_SECONDARY}; + `} &, &:visited, &:active, &:hover { - ${props => props.isGreen && css` - color: ${colors.GREEN_PRIMARY}; - `} - ${props => props.isGray && css` - color: ${colors.TEXT_SECONDARY}; - `} + ${props => + props.isGreen && + css` + color: ${colors.GREEN_PRIMARY}; + `} + ${props => + props.isGray && + css` + color: ${colors.TEXT_SECONDARY}; + `} } &:hover { @@ -38,13 +46,17 @@ const A = styled.a` `; const StyledNavLink = styled(NavLink)` - ${props => props.isGreen && css` - color: ${colors.GREEN_PRIMARY}; - `} + ${props => + props.isGreen && + css` + color: ${colors.GREEN_PRIMARY}; + `} - ${props => props.isGray && css` - color: ${colors.TEXT_SECONDARY}; - `} + ${props => + props.isGray && + css` + color: ${colors.TEXT_SECONDARY}; + `} `; class Link extends PureComponent { @@ -52,8 +64,7 @@ class Link extends PureComponent { const shouldRenderRouterLink = this.props.to; let LinkComponent; if (shouldRenderRouterLink) { - LinkComponent = ( - {this.props.children}); + LinkComponent = {this.props.children}; } else { LinkComponent = ( {this.props.children} + > + {this.props.children} ); } @@ -86,4 +98,4 @@ Link.propTypes = { isGray: PropTypes.bool, }; -export default Link; \ No newline at end of file +export default Link; diff --git a/src/components/Loader/index.js b/src/components/Loader/index.js index 0c0707c4..6fc847f1 100644 --- a/src/components/Loader/index.js +++ b/src/components/Loader/index.js @@ -24,17 +24,22 @@ const SvgWrapper = styled.svg` `; const CircleWrapper = styled.circle` - ${props => props.isRoute && css` - stroke: ${props.transparentRoute ? 'transparent' : colors.GRAY_LIGHT}; - `} + ${props => + props.isRoute && + css` + stroke: ${props.transparentRoute ? 'transparent' : colors.GRAY_LIGHT}; + `} - ${props => props.isPath && css` - stroke-width: ${props.transparentRoute ? '2px' : '1px'}; - stroke-dasharray: 1, 200; - stroke-dashoffset: 0; - animation: ${DASH} 1.5s ease-in-out infinite, ${props.animationColor || GREEN_COLOR} 6s ease-in-out infinite; - stroke-linecap: round; - `}; + ${props => + props.isPath && + css` + stroke-width: ${props.transparentRoute ? '2px' : '1px'}; + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + animation: ${DASH} 1.5s ease-in-out infinite, + ${props.animationColor || GREEN_COLOR} 6s ease-in-out infinite; + stroke-linecap: round; + `}; `; const StyledParagraph = styled(Paragraph)` @@ -43,10 +48,18 @@ const StyledParagraph = styled(Paragraph)` `; const Loader = ({ - className, text, isWhiteText = false, isSmallText, size = 100, animationColor, transparentRoute, + className, + text, + isWhiteText = false, + isSmallText, + size = 100, + animationColor, + transparentRoute, }) => ( - {text} + + {text} + , - toggle: typeof LogActions.toggle -} + toggle: typeof LogActions.toggle, +}; const Wrapper = styled.div` position: relative; @@ -86,5 +85,5 @@ export default connect( }), (dispatch: Dispatch) => ({ toggle: bindActionCreators(LogActions.toggle, dispatch), - }), -)(Log); \ No newline at end of file + }) +)(Log); diff --git a/src/components/Log/index.messages.js b/src/components/Log/index.messages.js index f0dd2940..859bca74 100644 --- a/src/components/Log/index.messages.js +++ b/src/components/Log/index.messages.js @@ -5,7 +5,8 @@ import type { Messages } from 'flowtype/npm/react-intl'; const definedMessages: Messages = defineMessages({ TR_ATTENTION_COLON_THE_LOG_CONTAINS: { id: 'TR_ATTENTION_COLON_THE_LOG_CONTAINS', - defaultMessage: 'Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.', + defaultMessage: + 'Attention: The log contains your XPUBs. Anyone with your XPUBs can see your account history.', }, TR_LOG: { id: 'TR_LOG', @@ -14,4 +15,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/Notification/components/NotificationButton/index.js b/src/components/Notification/components/NotificationButton/index.js index f2e9fcf6..73b99946 100644 --- a/src/components/Notification/components/NotificationButton/index.js +++ b/src/components/Notification/components/NotificationButton/index.js @@ -8,20 +8,18 @@ import colors from 'config/colors'; import { WHITE_COLOR } from 'config/animations'; import { getPrimaryColor } from 'utils/notification'; import Loader from 'components/Loader'; -import { - TRANSITION, FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE, -} from 'config/variables'; +import { TRANSITION, FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE } from 'config/variables'; type Props = { - type: string; + type: string, icon?: { - type: Array; - color: string; - size: number; - }; - onClick: () => void; - isLoading?: boolean; - children: React.Node; + type: Array, + color: string, + size: number, + }, + onClick: () => void, + isLoading?: boolean, + children: React.Node, }; const LoaderContent = styled.div` @@ -50,8 +48,8 @@ const Wrapper = styled.button` border: 1px solid ${props => getPrimaryColor(props.type)}; transition: ${TRANSITION.HOVER}; - @media screen and (max-width: ${SCREEN_SIZE.SM}){ - padding: 12px 24px; + @media screen and (max-width: ${SCREEN_SIZE.SM}) { + padding: 12px 24px; } &:hover { @@ -64,14 +62,8 @@ const IconWrapper = styled.span` margin-right: 8px; `; -const NotificationButton = ({ - type, icon, onClick, children, isLoading, -}: Props) => ( - +const NotificationButton = ({ type, icon, onClick, children, isLoading }: Props) => ( + {isLoading && ( @@ -79,11 +71,7 @@ const NotificationButton = ({ )} {icon && ( - + )} {children} @@ -102,4 +90,4 @@ NotificationButton.propTypes = { children: PropTypes.node.isRequired, }; -export default NotificationButton; \ No newline at end of file +export default NotificationButton; diff --git a/src/components/Notification/index.js b/src/components/Notification/index.js index 18d85d58..8b95d8ee 100644 --- a/src/components/Notification/index.js +++ b/src/components/Notification/index.js @@ -14,14 +14,14 @@ import NotificationButton from './components/NotificationButton'; type Props = { type: string, - cancelable?: boolean; - title: ?React.Node; - className?: string; - message?: ?React.Node; - actions?: Array; - isActionInProgress?: boolean; + cancelable?: boolean, + title: ?React.Node, + className?: string, + message?: ?React.Node, + actions?: Array, + isActionInProgress?: boolean, close?: typeof NotificationActions.close, - loading?: boolean + loading?: boolean, }; const Wrapper = styled.div` @@ -98,7 +98,7 @@ const Notification = (props: Props): React$Element => { return ( - {props.loading && } + {props.loading && } => { /> - { props.title } - { props.message ? {props.message} : '' } + {props.title} + {props.message ? {props.message} : ''} @@ -119,8 +119,12 @@ const Notification = (props: Props): React$Element => { key={action.label} type={props.type} isLoading={props.isActionInProgress} - onClick={() => { close(); action.callback(); }} - >{action.label} + onClick={() => { + close(); + action.callback(); + }} + > + {action.label} ))} @@ -128,11 +132,7 @@ const Notification = (props: Props): React$Element => { {props.cancelable && ( close()}> - + )} @@ -140,4 +140,4 @@ const Notification = (props: Props): React$Element => { ); }; -export default Notification; \ No newline at end of file +export default Notification; diff --git a/src/components/Paragraph/index.js b/src/components/Paragraph/index.js index 6cbf366a..68115a05 100644 --- a/src/components/Paragraph/index.js +++ b/src/components/Paragraph/index.js @@ -11,16 +11,16 @@ const Wrapper = styled.p` padding: 0; margin: 0; - ${props => props.isSmaller && css` - font-size: ${FONT_SIZE.SMALL}; - `} + ${props => + props.isSmaller && + css` + font-size: ${FONT_SIZE.SMALL}; + `} `; const P = ({ children, className, isSmaller = false }) => ( - {children} + + {children} ); diff --git a/src/components/Select/index.js b/src/components/Select/index.js index 4a06f51c..7aa4ad66 100644 --- a/src/components/Select/index.js +++ b/src/components/Select/index.js @@ -28,7 +28,7 @@ const styles = isSearchable => ({ }), dropdownIndicator: (base, { isDisabled }) => ({ ...base, - display: (isSearchable || isDisabled) ? 'none' : 'block', + display: isSearchable || isDisabled ? 'none' : 'block', color: colors.TEXT_SECONDARY, path: '', '&:hover': { @@ -61,7 +61,6 @@ const styles = isSearchable => ({ }), }); - const propTypes = { isAsync: PropTypes.bool, isSearchable: PropTypes.bool, @@ -71,7 +70,4 @@ const AsyncSelect = props => props.trezorAction && css` - z-index: 10001; /* bigger than modal container */ - border-color: ${colors.WHITE}; - border-width: 2px; - transform: translate(-1px, -1px); - background: ${colors.DIVIDER}; - pointer-events: none; - `} + ${props => + props.trezorAction && + css` + z-index: 10001; /* bigger than modal container */ + border-color: ${colors.WHITE}; + border-width: 2px; + transform: translate(-1px, -1px); + background: ${colors.DIVIDER}; + pointer-events: none; + `} `; const TopLabel = styled.span` @@ -119,7 +116,7 @@ const BottomText = styled.span` color: ${props => (props.color ? props.color : colors.TEXT_SECONDARY)}; `; -const getColor = (inputState) => { +const getColor = inputState => { let color = ''; if (inputState === 'success') { color = colors.SUCCESS_PRIMARY; @@ -179,9 +176,7 @@ const TextArea = ({ trezorAction = null, }) => ( - {topLabel && ( - {topLabel} - )} + {topLabel && {topLabel}} - {trezorAction} + + {trezorAction} - {bottomText && ( - - {bottomText} - - )} + {bottomText && {bottomText}} ); diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js index 3186f2a7..7edc9f5e 100644 --- a/src/components/Tooltip/index.js +++ b/src/components/Tooltip/index.js @@ -45,10 +45,11 @@ const Tooltip = ({ {content} {readMoreLink && ( - + + + - ) - } + )} )} > @@ -60,15 +61,9 @@ const Tooltip = ({ Tooltip.propTypes = { className: PropTypes.string, placement: PropTypes.string, - children: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.string, - ]), + children: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), maxWidth: PropTypes.number, - content: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.string, - ]), + content: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), readMoreLink: PropTypes.string, enterDelayMs: PropTypes.number, }; diff --git a/src/components/Tooltip/index.messages.js b/src/components/Tooltip/index.messages.js index 8105a958..3f8a287b 100644 --- a/src/components/Tooltip/index.messages.js +++ b/src/components/Tooltip/index.messages.js @@ -2,7 +2,6 @@ import { defineMessages } from 'react-intl'; import type { Messages } from 'flowtype/npm/react-intl'; -const definedMessages: Messages = defineMessages({ -}); +const definedMessages: Messages = defineMessages({}); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/Transaction/index.js b/src/components/Transaction/index.js index 33b87ac8..db923884 100644 --- a/src/components/Transaction/index.js +++ b/src/components/Transaction/index.js @@ -65,30 +65,55 @@ const Fee = styled.div` border: 1px; `; -const TransactionItem = ({ - tx, - network, -}: Props) => { +const TransactionItem = ({ tx, network }: Props) => { const url = `${network.explorer.tx}${tx.hash}`; const date = typeof tx.timestamp === 'string' ? tx.timestamp : undefined; // TODO: format date - const addresses = (tx.type === 'send' ? tx.outputs : tx.inputs).reduce((arr, item) => arr.concat(item.addresses), []); + const addresses = (tx.type === 'send' ? tx.outputs : tx.inputs).reduce( + (arr, item) => arr.concat(item.addresses), + [] + ); const operation = tx.type === 'send' ? '-' : '+'; - const amount = tx.tokens ? tx.tokens.map(t => ({operation}{t.value} {t.shortcut})) : {operation}{tx.total} {network.symbol}; + const amount = tx.tokens ? ( + tx.tokens.map(t => ( + + {operation} + {t.value} {t.shortcut} + + )) + ) : ( + + {operation} + {tx.total} {network.symbol} + + ); const fee = tx.tokens && tx.type === 'send' ? `${tx.fee} ${network.symbol}` : undefined; return ( - { date && ({ date })} + {date && ( + + {date} + + )} - { addresses.map(addr => (
{addr}
)) } - { !tx.blockHeight && ( - Transaction hash: {tx.hash} + {addresses.map(addr => ( +
{addr}
+ ))} + {!tx.blockHeight && ( + + Transaction hash: {tx.hash} + )}
{amount} - { fee && ({operation}{fee}) } + {fee && ( + + {operation} + {fee} + + )}
); @@ -99,4 +124,4 @@ TransactionItem.propTypes = { network: PropTypes.object.isRequired, }; -export default TransactionItem; \ No newline at end of file +export default TransactionItem; diff --git a/src/components/images/CoinLogo/index.js b/src/components/images/CoinLogo/index.js index 0cca9ba0..9a93522e 100644 --- a/src/components/images/CoinLogo/index.js +++ b/src/components/images/CoinLogo/index.js @@ -42,11 +42,7 @@ class CoinLogo extends PureComponent { return logo; } - return ( - - {logo} - - ); + return {logo}; } } diff --git a/src/components/images/DeviceIcon/index.js b/src/components/images/DeviceIcon/index.js index 4a1b7cc8..b52a452d 100644 --- a/src/components/images/DeviceIcon/index.js +++ b/src/components/images/DeviceIcon/index.js @@ -13,12 +13,12 @@ type Props = { color?: string, hoverColor?: string, onClick?: any, -} +}; const SvgWrapper = styled.svg` :hover { path { - fill: ${props => props.hoverColor} + fill: ${props => props.hoverColor}; } } `; @@ -54,11 +54,7 @@ const DeviceIcon = ({ viewBox="0 0 1024 1024" onClick={onClick} > - +
); }; @@ -71,4 +67,4 @@ DeviceIcon.propTypes = { onClick: PropTypes.func, }; -export default DeviceIcon; \ No newline at end of file +export default DeviceIcon; diff --git a/src/components/images/TrezorImage/index.js b/src/components/images/TrezorImage/index.js index 0e0c716b..5577d6a9 100644 --- a/src/components/images/TrezorImage/index.js +++ b/src/components/images/TrezorImage/index.js @@ -5,8 +5,8 @@ import styled from 'styled-components'; import PropTypes from 'prop-types'; type Props = { - model: string; -} + model: string, +}; const Wrapper = styled.div``; diff --git a/src/components/images/WalletType/index.js b/src/components/images/WalletType/index.js index 867243cb..7796bf2e 100644 --- a/src/components/images/WalletType/index.js +++ b/src/components/images/WalletType/index.js @@ -7,7 +7,7 @@ import ICONS from 'config/icons'; const SvgWrapper = styled.svg` :hover { path { - fill: ${props => props.hoverColor} + fill: ${props => props.hoverColor}; } } `; @@ -46,4 +46,4 @@ Icon.propTypes = { onClick: PropTypes.func, }; -export default Icon; \ No newline at end of file +export default Icon; diff --git a/src/components/inputs/Input/index.js b/src/components/inputs/Input/index.js index ee2aa96a..a9fd56a0 100644 --- a/src/components/inputs/Input/index.js +++ b/src/components/inputs/Input/index.js @@ -4,13 +4,7 @@ import styled, { css } from 'styled-components'; import colors from 'config/colors'; import ICONS from 'config/icons'; import Icon from 'components/Icon'; -import { - FONT_SIZE, - FONT_FAMILY, - FONT_WEIGHT, - LINE_HEIGHT, - TRANSITION, -} from 'config/variables'; +import { FONT_SIZE, FONT_FAMILY, FONT_WEIGHT, LINE_HEIGHT, TRANSITION } from 'config/variables'; const Wrapper = styled.div` width: 100%; @@ -48,10 +42,12 @@ const StyledInput = styled.input` border-radius: 2px; - ${props => props.hasAddon && css` - border-top-right-radius: 0; - border-bottom-right-radius: 0; - `} + ${props => + props.hasAddon && + css` + border-top-right-radius: 0; + border-bottom-right-radius: 0; + `} border: 1px solid ${colors.DIVIDER}; border-color: ${props => props.borderColor}; @@ -75,14 +71,16 @@ const StyledInput = styled.input` color: ${colors.TEXT_SECONDARY}; } - ${props => props.trezorAction && css` - z-index: 10001; - position: relative; /* bigger than modal container */ - border-color: ${colors.WHITE}; - border-width: 2px; - transform: translate(-1px, -1px); - background: ${colors.DIVIDER}; - `}; + ${props => + props.trezorAction && + css` + z-index: 10001; + position: relative; /* bigger than modal container */ + border-color: ${colors.WHITE}; + border-width: 2px; + transform: translate(-1px, -1px); + background: ${colors.DIVIDER}; + `}; `; const StyledIcon = styled(Icon)` @@ -99,18 +97,21 @@ const BottomText = styled.span` `; const Overlay = styled.div` - ${props => props.isPartiallyHidden && css` - bottom: 0; - border: 1px solid ${colors.DIVIDER}; - border-radius: 2px; - position: absolute; - width: 100%; - height: 100%; - background-image: linear-gradient(to right, - rgba(0,0,0, 0) 0%, - rgba(249,249,249, 1) 220px - ); - `} + ${props => + props.isPartiallyHidden && + css` + bottom: 0; + border: 1px solid ${colors.DIVIDER}; + border-radius: 2px; + position: absolute; + width: 100%; + height: 100%; + background-image: linear-gradient( + to right, + rgba(0, 0, 0, 0) 0%, + rgba(249, 249, 249, 1) 220px + ); + `} `; const TrezorAction = styled.div` @@ -168,12 +169,8 @@ class Input extends PureComponent { render() { return ( - - {this.props.topLabel && ( - {this.props.topLabel} - )} + + {this.props.topLabel && {this.props.topLabel}} {this.props.state && ( @@ -208,15 +205,14 @@ class Input extends PureComponent { data-lpignore="true" /> - {this.props.trezorAction} + + {this.props.trezorAction} {this.props.sideAddons && this.props.sideAddons.map(sideAddon => sideAddon)} {this.props.bottomText && ( - + {this.props.bottomText} )} diff --git a/src/components/modals/Container.js b/src/components/modals/Container.js index fa95094e..a18433c8 100644 --- a/src/components/modals/Container.js +++ b/src/components/modals/Container.js @@ -33,7 +33,9 @@ type DispatchProps = { export type Props = StateProps & DispatchProps; -const mapStateToProps: MapStateToProps = (state: State): StateProps => ({ +const mapStateToProps: MapStateToProps = ( + state: State +): StateProps => ({ modal: state.modal, accounts: state.accounts, devices: state.devices, @@ -46,12 +48,17 @@ const mapStateToProps: MapStateToProps = (state: St wallet: state.wallet, }); -const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ +const mapDispatchToProps: MapDispatchToProps = ( + dispatch: Dispatch +): DispatchProps => ({ modalActions: bindActionCreators(ModalActions, dispatch), receiveActions: bindActionCreators(ReceiveActions, dispatch), }); // export default connect(mapStateToProps, mapDispatchToProps)(Modal); export default withRouter( - connect(mapStateToProps, mapDispatchToProps)(Modal), -); \ No newline at end of file + connect( + mapStateToProps, + mapDispatchToProps + )(Modal) +); diff --git a/src/components/modals/QrModal/index.js b/src/components/modals/QrModal/index.js index e95d1104..03453dfe 100644 --- a/src/components/modals/QrModal/index.js +++ b/src/components/modals/QrModal/index.js @@ -63,7 +63,7 @@ type Props = { onError?: (error: any) => any, onCancel?: $ElementType<$ElementType, 'onCancel'>, intl: any, -} +}; type State = { readerLoaded: boolean, @@ -83,7 +83,7 @@ class QrModal extends Component { this.setState({ readerLoaded: true, }); - } + }; handleScan = (data: string) => { if (data) { @@ -102,7 +102,7 @@ class QrModal extends Component { this.handleError(error); } } - } + }; handleError = (err: any) => { // log thrown error @@ -111,8 +111,12 @@ class QrModal extends Component { this.props.onError(err); } - if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError' - || err.name === 'NotReadableError' || err.name === 'TrackStartError') { + if ( + err.name === 'NotAllowedError' || + err.name === 'PermissionDeniedError' || + err.name === 'NotReadableError' || + err.name === 'TrackStartError' + ) { this.setState({ error: this.props.intl.formatMessage(l10nMessages.TR_CAMERA_PERMISSION_DENIED), }); @@ -125,31 +129,34 @@ class QrModal extends Component { error: this.props.intl.formatMessage(l10nMessages.TR_UNKOWN_ERROR_SEE_CONSOLE), }); } - } + }; handleCancel = () => { if (this.props.onCancel) { this.props.onCancel(); } - } - + }; render() { return ( - + -

- {!this.state.readerLoaded && !this.state.error && } +

+ +

+ {!this.state.readerLoaded && !this.state.error && ( + + + + )} {this.state.error && ( - + + + {this.state.error.toString()} )} diff --git a/src/components/modals/QrModal/index.messages.js b/src/components/modals/QrModal/index.messages.js index c9e26487..2b188323 100644 --- a/src/components/modals/QrModal/index.messages.js +++ b/src/components/modals/QrModal/index.messages.js @@ -30,4 +30,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/confirm/Action/index.js b/src/components/modals/confirm/Action/index.js index a20f26b3..62a8c4eb 100644 --- a/src/components/modals/confirm/Action/index.js +++ b/src/components/modals/confirm/Action/index.js @@ -11,8 +11,8 @@ import { FormattedMessage } from 'react-intl'; import l10nMessages from './index.messages'; type Props = { - device: TrezorDevice; -} + device: TrezorDevice, +}; const Wrapper = styled.div``; @@ -35,5 +35,4 @@ ConfirmAction.propTypes = { device: PropTypes.object.isRequired, }; - -export default ConfirmAction; \ No newline at end of file +export default ConfirmAction; diff --git a/src/components/modals/confirm/Action/index.messages.js b/src/components/modals/confirm/Action/index.messages.js index 1d74460e..d98eed09 100644 --- a/src/components/modals/confirm/Action/index.messages.js +++ b/src/components/modals/confirm/Action/index.messages.js @@ -9,4 +9,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/confirm/Address/index.js b/src/components/modals/confirm/Address/index.js index d56ffb90..f0ee049a 100644 --- a/src/components/modals/confirm/Address/index.js +++ b/src/components/modals/confirm/Address/index.js @@ -37,10 +37,7 @@ const Label = styled.div` `; const ConfirmAddress = (props: Props) => { - const { - account, - network, - } = props.selectedAccount; + const { account, network } = props.selectedAccount; if (!account || !network) return null; return ( @@ -54,9 +51,13 @@ const ConfirmAddress = (props: Props) => {

-

{ account.descriptor }

-
@@ -67,4 +68,4 @@ ConfirmAddress.propTypes = { selectedAccount: PropTypes.object.isRequired, }; -export default ConfirmAddress; \ No newline at end of file +export default ConfirmAddress; diff --git a/src/components/modals/confirm/Address/index.messages.js b/src/components/modals/confirm/Address/index.messages.js index 1c16ede4..c1d0f4b3 100644 --- a/src/components/modals/confirm/Address/index.messages.js +++ b/src/components/modals/confirm/Address/index.messages.js @@ -13,4 +13,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/confirm/NoBackup/index.js b/src/components/modals/confirm/NoBackup/index.js index 45fafef1..8cf08208 100644 --- a/src/components/modals/confirm/NoBackup/index.js +++ b/src/components/modals/confirm/NoBackup/index.js @@ -18,9 +18,12 @@ import type { TrezorDevice } from 'flowtype'; import type { Props as BaseProps } from '../../Container'; type Props = { - onReceiveConfirmation: $ElementType<$ElementType, 'onReceiveConfirmation'>; - device: ?TrezorDevice; -} + onReceiveConfirmation: $ElementType< + $ElementType, + 'onReceiveConfirmation' + >, + device: ?TrezorDevice, +}; const Wrapper = styled.div` max-width: 370px; @@ -68,12 +71,19 @@ const Confirmation = (props: Props) => (

Your Trezor is not backed up

- If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events. + + If your device is ever lost or damaged, your funds will be lost. Backup your device + first, to protect your coins against such events. + - props.onReceiveConfirmation(false)}>Create a backup in 3 minutes + props.onReceiveConfirmation(false)}> + Create a backup in 3 minutes + - props.onReceiveConfirmation(true)}>Show address, I will take the risk + props.onReceiveConfirmation(true)}> + Show address, I will take the risk +
); @@ -82,4 +92,4 @@ Confirmation.propTypes = { onReceiveConfirmation: PropTypes.func.isRequired, }; -export default Confirmation; \ No newline at end of file +export default Confirmation; diff --git a/src/components/modals/confirm/SignTx/index.js b/src/components/modals/confirm/SignTx/index.js index 8422e67e..d25788e0 100644 --- a/src/components/modals/confirm/SignTx/index.js +++ b/src/components/modals/confirm/SignTx/index.js @@ -15,11 +15,10 @@ import { FormattedMessage } from 'react-intl'; import type { TrezorDevice, State } from 'flowtype'; import l10nMessages from './index.messages'; - type Props = { - device: TrezorDevice; - sendForm: $ElementType | $ElementType; -} + device: TrezorDevice, + sendForm: $ElementType | $ElementType, +}; const Wrapper = styled.div` max-width: 390px; @@ -62,20 +61,22 @@ const FeeLevelName = styled(StyledP)` `; const ConfirmSignTx = (props: Props) => { - const { - amount, - address, - selectedFeeLevel, - } = props.sendForm; + const { amount, address, selectedFeeLevel } = props.sendForm; - const currency: string = typeof props.sendForm.currency === 'string' ? props.sendForm.currency : props.sendForm.networkSymbol; + const currency: string = + typeof props.sendForm.currency === 'string' + ? props.sendForm.currency + : props.sendForm.networkSymbol; return (

- +

@@ -85,16 +86,16 @@ const ConfirmSignTx = (props: Props) => { - {`${amount} ${currency}` } + {`${amount} ${currency}`} -

{ address }
+
{address}
{selectedFeeLevel.value} - { selectedFeeLevel.label } + {selectedFeeLevel.label} ); @@ -105,4 +106,4 @@ ConfirmSignTx.propTypes = { sendForm: PropTypes.object.isRequired, }; -export default ConfirmSignTx; \ No newline at end of file +export default ConfirmSignTx; diff --git a/src/components/modals/confirm/SignTx/index.messages.js b/src/components/modals/confirm/SignTx/index.messages.js index 3ebd513a..a712178e 100644 --- a/src/components/modals/confirm/SignTx/index.messages.js +++ b/src/components/modals/confirm/SignTx/index.messages.js @@ -14,7 +14,7 @@ const definedMessages: Messages = defineMessages({ TR_TO_LABEL: { id: 'TR_TO_LABEL', defaultMessage: 'To', - description: 'Label for recepeint\'s address', + description: "Label for recepeint's address", }, TR_SEND_LABEL: { id: 'TR_SEND_LABEL', @@ -28,4 +28,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/confirm/UnverifiedAddress/index.js b/src/components/modals/confirm/UnverifiedAddress/index.js index 889415f4..18609720 100644 --- a/src/components/modals/confirm/UnverifiedAddress/index.js +++ b/src/components/modals/confirm/UnverifiedAddress/index.js @@ -19,12 +19,15 @@ import l10nMessages from './index.messages'; import type { Props as BaseProps } from '../../Container'; type Props = { - device: TrezorDevice; - account: $ElementType<$ElementType, 'account'>; - showAddress: $ElementType<$ElementType, 'showAddress'>; - showUnverifiedAddress: $ElementType<$ElementType, 'showUnverifiedAddress'>; - onCancel: $ElementType<$ElementType, 'onCancel'>; -} + device: TrezorDevice, + account: $ElementType<$ElementType, 'account'>, + showAddress: $ElementType<$ElementType, 'showAddress'>, + showUnverifiedAddress: $ElementType< + $ElementType, + 'showUnverifiedAddress' + >, + onCancel: $ElementType<$ElementType, 'onCancel'>, +}; const StyledLink = styled(Link)` position: absolute; @@ -35,7 +38,6 @@ const StyledLink = styled(Link)` const Wrapper = styled.div` max-width: 370px; padding: 30px 0px; - `; const Content = styled.div` @@ -57,7 +59,7 @@ const Row = styled.div` display: flex; flex-direction: column; - Button + Button { + button + button { margin-top: 10px; } `; @@ -119,15 +121,27 @@ class ConfirmUnverifiedAddress extends PureComponent { let claim; if (!device.connected) { - deviceStatus = ; + deviceStatus = ( + + ); claim = ; } else { // corner-case where device is connected but it is unavailable because it was created with different "passphrase_protection" settings const enable: boolean = !!(device.features && device.features.passphrase_protection); - deviceStatus = ; - claim = enable - ? - : ; + deviceStatus = ( + + ); + claim = enable ? ( + + ) : ( + + ); } const needsBackup = device.features && device.features.needs_backup; @@ -138,7 +152,7 @@ class ConfirmUnverifiedAddress extends PureComponent { -

{ deviceStatus }

+

{deviceStatus}

{ - - this.showUnverifiedAddress()}> + + this.showUnverifiedAddress()}> + + {needsBackup && } @@ -157,7 +175,10 @@ class ConfirmUnverifiedAddress extends PureComponent { <>

Device {device.label} is not backed up

- If your device is ever lost or damaged, your funds will be lost. Backup your device first, to protect your coins against such events. + + If your device is ever lost or damaged, your funds will be lost. + Backup your device first, to protect your coins against such events. +
@@ -181,4 +202,4 @@ ConfirmUnverifiedAddress.propTypes = { onCancel: PropTypes.func.isRequired, }; -export default ConfirmUnverifiedAddress; \ No newline at end of file +export default ConfirmUnverifiedAddress; diff --git a/src/components/modals/confirm/UnverifiedAddress/index.messages.js b/src/components/modals/confirm/UnverifiedAddress/index.messages.js index 9791039e..c89c8e59 100644 --- a/src/components/modals/confirm/UnverifiedAddress/index.messages.js +++ b/src/components/modals/confirm/UnverifiedAddress/index.messages.js @@ -17,11 +17,13 @@ const definedMessages: Messages = defineMessages({ }, TR_PLEASE_ENABLE_PASSPHRASE: { id: 'TR_PLEASE_ENABLE_PASSPHRASE', - defaultMessage: 'Please enable passphrase settings to continue with the verification process.', + defaultMessage: + 'Please enable passphrase settings to continue with the verification process.', }, TR_PLEASE_DISABLE_PASSPHRASE: { id: 'TR_PLEASE_DISABLE_PASSPHRASE', - defaultMessage: 'Please disable passphrase settings to continue with the verification process.', + defaultMessage: + 'Please disable passphrase settings to continue with the verification process.', }, TR_SHOW_UNVERIFIED_ADDRESS: { id: 'TR_SHOW_UNVERIFIED_ADDRESS', @@ -34,8 +36,9 @@ const definedMessages: Messages = defineMessages({ }, TR_TO_PREVENT_PHISHING_ATTACKS_COMMA: { id: 'TR_TO_PREVENT_PHISHING_ATTACKS_COMMA', - defaultMessage: 'To prevent phishing attacks, you should verify the address on your Trezor first. {claim}', + defaultMessage: + 'To prevent phishing attacks, you should verify the address on your Trezor first. {claim}', }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/device/Duplicate/index.js b/src/components/modals/device/Duplicate/index.js index 75aebb38..0b696a95 100644 --- a/src/components/modals/device/Duplicate/index.js +++ b/src/components/modals/device/Duplicate/index.js @@ -20,18 +20,18 @@ import type { TrezorDevice } from 'flowtype'; import type { Props as BaseProps } from '../../Container'; type Props = { - device: TrezorDevice; - devices: $ElementType; - onDuplicateDevice: $ElementType<$ElementType, 'onDuplicateDevice'>; - onCancel: $ElementType<$ElementType, 'onCancel'>; -} + device: TrezorDevice, + devices: $ElementType, + onDuplicateDevice: $ElementType<$ElementType, 'onDuplicateDevice'>, + onCancel: $ElementType<$ElementType, 'onCancel'>, +}; type State = { - defaultName: string; - instance: number; - instanceName: ?string; - isUsed: boolean; -} + defaultName: string, + instance: number, + instanceName: ?string, + isUsed: boolean, +}; const StyledLink = styled(Link)` position: absolute; @@ -102,14 +102,14 @@ class DuplicateDevice extends PureComponent { onNameChange = (value: string): void => { let isUsed: boolean = false; if (value.length > 0) { - isUsed = (this.props.devices.find(d => d.instanceName === value) !== undefined); + isUsed = this.props.devices.find(d => d.instanceName === value) !== undefined; } this.setState({ instanceName: value.length > 0 ? value : null, isUsed, }); - } + }; input: ?HTMLInputElement; @@ -123,25 +123,27 @@ class DuplicateDevice extends PureComponent { keyboardHandler: (event: KeyboardEvent) => void; submit() { - const extended: Object = { instanceName: this.state.instanceName, instance: this.state.instance }; + const extended: Object = { + instanceName: this.state.instanceName, + instance: this.state.instance, + }; this.props.onDuplicateDevice({ ...this.props.device, ...extended }); } render() { const { device, onCancel } = this.props; - const { - defaultName, - instanceName, - isUsed, - } = this.state; + const { defaultName, instanceName, isUsed } = this.state; return ( -

Clone { device.label }?

- This will create new instance of device which can be used with different passphrase +

Clone {device.label}?

+ + This will create new instance of device which can be used with different + passphrase + { autoCapitalize="off" spellCheck="false" placeholder={defaultName} - innerRef={(element) => { this.input = element; }} + innerRef={element => { + this.input = element; + }} onChange={event => this.onNameChange(event.currentTarget.value)} value={instanceName} /> - { isUsed && Instance name is already in use } + {isUsed && Instance name is already in use} - this.submit()} - >Create new instance + this.submit()}> + Create new instance - Cancel + + Cancel
@@ -181,4 +181,4 @@ DuplicateDevice.propTypes = { onCancel: PropTypes.func.isRequired, }; -export default DuplicateDevice; \ No newline at end of file +export default DuplicateDevice; diff --git a/src/components/modals/device/Forget/index.js b/src/components/modals/device/Forget/index.js index 374b982b..79339bf8 100644 --- a/src/components/modals/device/Forget/index.js +++ b/src/components/modals/device/Forget/index.js @@ -17,10 +17,13 @@ import l10nMessages from './index.messages'; import type { Props as BaseProps } from '../../Container'; type Props = { - device: TrezorDevice; - onForgetSingleDevice: $ElementType<$ElementType, 'onForgetSingleDevice'>; - onCancel: $ElementType<$ElementType, 'onCancel'>; -} + device: TrezorDevice, + onForgetSingleDevice: $ElementType< + $ElementType, + 'onForgetSingleDevice' + >, + onCancel: $ElementType<$ElementType, 'onCancel'>, +}; const Wrapper = styled.div` width: 360px; @@ -35,7 +38,7 @@ const Row = styled.div` display: flex; flex-direction: column; - Button + Button { + button + button { margin-top: 10px; } `; @@ -75,11 +78,17 @@ class ForgetDevice extends PureComponent { /> - + - - + + ); @@ -92,4 +101,4 @@ ForgetDevice.propTypes = { onCancel: PropTypes.func.isRequired, }; -export default ForgetDevice; \ No newline at end of file +export default ForgetDevice; diff --git a/src/components/modals/device/Forget/index.messages.js b/src/components/modals/device/Forget/index.messages.js index 1c310aab..44009d1a 100644 --- a/src/components/modals/device/Forget/index.messages.js +++ b/src/components/modals/device/Forget/index.messages.js @@ -5,13 +5,14 @@ import type { Messages } from 'flowtype/npm/react-intl'; const definedMessages: Messages = defineMessages({ TR_DONT_FORGET: { id: 'TR_DONT_FORGET', - defaultMessage: 'Don\'t forget', + defaultMessage: "Don't forget", description: 'Button in remember/forget dialog', }, TR_FORGETTING_ONLY_REMOVES_THE_DEVICE_FROM: { id: 'TR_FORGETTING_ONLY_REMOVES_THE_DEVICE_FROM', - defaultMessage: 'Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your Trezor again.', + defaultMessage: + 'Forgetting only removes the device from the list on the left, your coins are still safe and you can access them by reconnecting your Trezor again.', }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/device/Remember/index.js b/src/components/modals/device/Remember/index.js index ad6d03d4..604fc21c 100644 --- a/src/components/modals/device/Remember/index.js +++ b/src/components/modals/device/Remember/index.js @@ -17,16 +17,16 @@ import l10nMessages from './index.messages'; import type { Props as BaseProps } from '../../Container'; type Props = { - device: TrezorDevice; - instances: ?Array; - onRememberDevice: $ElementType<$ElementType, 'onRememberDevice'>; - onForgetDevice: $ElementType<$ElementType, 'onForgetDevice'>; -} + device: TrezorDevice, + instances: ?Array, + onRememberDevice: $ElementType<$ElementType, 'onRememberDevice'>, + onForgetDevice: $ElementType<$ElementType, 'onForgetDevice'>, +}; type State = { - countdown: number; - ticker?: number; -} + countdown: number, + ticker?: number, +}; const ButtonContent = styled.div` display: flex; @@ -52,7 +52,7 @@ const Column = styled.div` display: flex; flex-direction: column; - Button + Button { + button + button { margin-top: 10px; } `; @@ -120,7 +120,7 @@ class RememberDevice extends PureComponent { let { label } = device; const deviceCount = instances ? instances.length : 0; if (instances && instances.length > 0) { - label = instances.map(instance => (instance.instanceLabel)).join(','); + label = instances.map(instance => instance.instanceLabel).join(','); } return ( @@ -154,10 +154,7 @@ class RememberDevice extends PureComponent { /> - @@ -173,4 +170,4 @@ RememberDevice.propTypes = { onForgetDevice: PropTypes.func.isRequired, }; -export default RememberDevice; \ No newline at end of file +export default RememberDevice; diff --git a/src/components/modals/device/Remember/index.messages.js b/src/components/modals/device/Remember/index.messages.js index ae296de9..01527d70 100644 --- a/src/components/modals/device/Remember/index.messages.js +++ b/src/components/modals/device/Remember/index.messages.js @@ -5,7 +5,8 @@ import type { Messages } from 'flowtype/npm/react-intl'; const definedMessages: Messages = defineMessages({ TR_WOULD_YOU_LIKE_TREZOR_WALLET_TO: { id: 'TR_WOULD_YOU_LIKE_TREZOR_WALLET_TO', - defaultMessage: 'Would you like Trezor Wallet to forget your {deviceCount, plural, one {device} other {devices}} or to remember {deviceCount, plural, one {it} other {them}}, so that it is still visible even while disconnected?', + defaultMessage: + 'Would you like Trezor Wallet to forget your {deviceCount, plural, one {device} other {devices}} or to remember {deviceCount, plural, one {it} other {them}}, so that it is still visible even while disconnected?', }, TR_REMEMBER_DEVICE: { id: 'TR_REMEMBER_DEVICE', @@ -13,4 +14,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/device/WalletType/index.js b/src/components/modals/device/WalletType/index.js index 208f6d4e..ced1782d 100644 --- a/src/components/modals/device/WalletType/index.js +++ b/src/components/modals/device/WalletType/index.js @@ -23,13 +23,15 @@ import type { Props as BaseProps } from '../../Container'; type Props = { intl: any, - device: TrezorDevice; - onWalletTypeRequest: $ElementType<$ElementType, 'onWalletTypeRequest'>; - onCancel: $ElementType<$ElementType, 'onCancel'>; -} + device: TrezorDevice, + onWalletTypeRequest: $ElementType< + $ElementType, + 'onWalletTypeRequest' + >, + onCancel: $ElementType<$ElementType, 'onCancel'>, +}; -const Wrapper = styled.div` -`; +const Wrapper = styled.div``; const Header = styled.div` display: flex; @@ -71,10 +73,12 @@ const Content = styled.div` justify-content: center; align-items: center; - ${props => props.isTop && css` - padding-top: 40px; - border-bottom: 1px solid ${colors.DIVIDER}; - `} + ${props => + props.isTop && + css` + padding-top: 40px; + border-bottom: 1px solid ${colors.DIVIDER}; + `} `; class WalletType extends PureComponent { @@ -105,17 +109,13 @@ class WalletType extends PureComponent { return ( - { device.state && ( + {device.state && ( - + )} - { device.state - ? ( + + {device.state ? ( { - +
- +

- +

onWalletTypeRequest(true)}> @@ -178,4 +174,4 @@ WalletType.propTypes = { onCancel: PropTypes.func.isRequired, }; -export default injectIntl(WalletType); \ No newline at end of file +export default injectIntl(WalletType); diff --git a/src/components/modals/device/WalletType/index.messages.js b/src/components/modals/device/WalletType/index.messages.js index 3102c074..ee808795 100644 --- a/src/components/modals/device/WalletType/index.messages.js +++ b/src/components/modals/device/WalletType/index.messages.js @@ -25,7 +25,8 @@ const definedMessages: Messages = defineMessages({ }, TR_PASSPHRASE_IS_OPTIONAL_FEATURE: { id: 'TR_PASSPHRASE_IS_OPTIONAL_FEATURE', - defaultMessage: 'Passphrase is an optional feature of the Trezor device that is recommended for advanced users only. It is a word or a sentence of your choice. Its main purpose is to access a hidden wallet.', + defaultMessage: + 'Passphrase is an optional feature of the Trezor device that is recommended for advanced users only. It is a word or a sentence of your choice. Its main purpose is to access a hidden wallet.', }, TR_ASKED_ENTER_YOUR_PASSPHRASE_TO_UNLOCK: { id: 'TR_ASKED_ENTER_YOUR_PASSPHRASE_TO_UNLOCK', @@ -33,4 +34,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/device/common.messages.js b/src/components/modals/device/common.messages.js index 8f8c026a..dba13e76 100644 --- a/src/components/modals/device/common.messages.js +++ b/src/components/modals/device/common.messages.js @@ -9,4 +9,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/external/Cardano/index.js b/src/components/modals/external/Cardano/index.js index 043a191d..7bedb2bd 100644 --- a/src/components/modals/external/Cardano/index.js +++ b/src/components/modals/external/Cardano/index.js @@ -20,8 +20,8 @@ import CardanoImage from './images/cardano.png'; import type { Props as BaseProps } from '../../Container'; type Props = { - onCancel: $ElementType<$ElementType, 'onCancel'>; -} + onCancel: $ElementType<$ElementType, 'onCancel'>, +}; const Wrapper = styled.div` width: 100%; @@ -51,11 +51,7 @@ const Img = styled.img` const CardanoWallet = (props: Props) => ( - +

@@ -77,4 +73,4 @@ CardanoWallet.propTypes = { onCancel: PropTypes.func.isRequired, }; -export default CardanoWallet; \ No newline at end of file +export default CardanoWallet; diff --git a/src/components/modals/external/Cardano/index.messages.js b/src/components/modals/external/Cardano/index.messages.js index dfac0b56..46e56145 100644 --- a/src/components/modals/external/Cardano/index.messages.js +++ b/src/components/modals/external/Cardano/index.messages.js @@ -9,4 +9,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/external/Nem/index.js b/src/components/modals/external/Nem/index.js index aa026df5..554328fe 100644 --- a/src/components/modals/external/Nem/index.js +++ b/src/components/modals/external/Nem/index.js @@ -18,8 +18,8 @@ import NemImage from './images/nem-download.png'; import type { Props as BaseProps } from '../../Container'; type Props = { - onCancel: $ElementType<$ElementType, 'onCancel'>; -} + onCancel: $ElementType<$ElementType, 'onCancel'>, +}; const Wrapper = styled.div` width: 100%; @@ -47,11 +47,7 @@ const Img = styled.img` const NemWallet = (props: Props) => ( - +

@@ -75,4 +71,4 @@ NemWallet.propTypes = { onCancel: PropTypes.func.isRequired, }; -export default NemWallet; \ No newline at end of file +export default NemWallet; diff --git a/src/components/modals/external/Nem/index.messages.js b/src/components/modals/external/Nem/index.messages.js index dfc3bdcf..73edae65 100644 --- a/src/components/modals/external/Nem/index.messages.js +++ b/src/components/modals/external/Nem/index.messages.js @@ -9,7 +9,8 @@ const definedMessages: Messages = defineMessages({ }, TR_WE_HAVE_PARTNERED_UP_WITH_THE_NEM: { id: 'TR_WE_HAVE_PARTNERED_UP_WITH_THE_NEM', - defaultMessage: 'We have partnered up with the NEM Foundation to provide you with a full-fledged NEM Wallet.', + defaultMessage: + 'We have partnered up with the NEM Foundation to provide you with a full-fledged NEM Wallet.', }, TR_MAKE_SURE_YOU_DOWNLOAD_THE_UNIVERSAL: { id: 'TR_MAKE_SURE_YOU_DOWNLOAD_THE_UNIVERSAL', @@ -21,4 +22,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/external/Stellar/index.js b/src/components/modals/external/Stellar/index.js index 3555b5bf..2defc256 100644 --- a/src/components/modals/external/Stellar/index.js +++ b/src/components/modals/external/Stellar/index.js @@ -20,8 +20,8 @@ import StellarImage from './images/xlm.png'; import type { Props as BaseProps } from '../../Container'; type Props = { - onCancel: $ElementType<$ElementType, 'onCancel'>; -} + onCancel: $ElementType<$ElementType, 'onCancel'>, +}; const Wrapper = styled.div` width: 100%; @@ -51,11 +51,7 @@ const Img = styled.img` const StellarWallet = (props: Props) => ( - +

@@ -77,4 +73,4 @@ StellarWallet.propTypes = { onCancel: PropTypes.func.isRequired, }; -export default StellarWallet; \ No newline at end of file +export default StellarWallet; diff --git a/src/components/modals/external/Stellar/index.messages.js b/src/components/modals/external/Stellar/index.messages.js index 6dfa0064..1708c8a4 100644 --- a/src/components/modals/external/Stellar/index.messages.js +++ b/src/components/modals/external/Stellar/index.messages.js @@ -9,4 +9,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/external/common.messages.js b/src/components/modals/external/common.messages.js index cdb2713f..85079861 100644 --- a/src/components/modals/external/common.messages.js +++ b/src/components/modals/external/common.messages.js @@ -13,4 +13,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/index.js b/src/components/modals/index.js index 4c98ae9d..2f5d43b0 100644 --- a/src/components/modals/index.js +++ b/src/components/modals/index.js @@ -65,12 +65,7 @@ const getDeviceContextModal = (props: Props) => { switch (modal.windowType) { case UI.REQUEST_PIN: - return ( - - ); + return ; case UI.INVALID_PIN: return ; @@ -91,10 +86,13 @@ const getDeviceContextModal = (props: Props) => { if (!props.selectedAccount.network) return null; switch (props.selectedAccount.network.type) { case 'ethereum': - return ; + return ( + + ); case 'ripple': return ; - default: return null; + default: + return null; } } @@ -166,11 +164,11 @@ const getExternalContextModal = (props: Props) => { switch (modal.windowType) { case 'xem': - return (); + return ; case 'xlm': - return (); + return ; case 'ada': - return (); + return ; default: return null; } @@ -197,7 +195,12 @@ const getConfirmationModal = (props: Props) => { switch (modal.windowType) { case 'no-backup': - return (); + return ( + + ); default: return null; } @@ -228,9 +231,7 @@ const Modal = (props: Props) => { return ( - - { component } - + {component} ); }; diff --git a/src/components/modals/passphrase/Passphrase/index.js b/src/components/modals/passphrase/Passphrase/index.js index 937a6307..39243513 100644 --- a/src/components/modals/passphrase/Passphrase/index.js +++ b/src/components/modals/passphrase/Passphrase/index.js @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import styled from 'styled-components'; import { FormattedMessage } from 'react-intl'; - import colors from 'config/colors'; import { FONT_SIZE, TRANSITION } from 'config/variables'; @@ -20,10 +19,10 @@ import l10nMessages from './index.messages'; import type { Props as BaseProps } from '../../Container'; type Props = { - device: TrezorDevice; - selectedDevice: ?TrezorDevice; - onPassphraseSubmit: $ElementType<$ElementType, 'onPassphraseSubmit'>; -} + device: TrezorDevice, + selectedDevice: ?TrezorDevice, + onPassphraseSubmit: $ElementType<$ElementType, 'onPassphraseSubmit'>, +}; type State = { deviceLabel: string, @@ -165,7 +164,10 @@ class Passphrase extends PureComponent { handleCheckboxClick() { let match = false; - if (this.state.shouldShowSingleInput || this.state.passphraseInputValue === this.state.passphraseCheckInputValue) { + if ( + this.state.shouldShowSingleInput || + this.state.passphraseInputValue === this.state.passphraseCheckInputValue + ) { match = true; } else { match = !!this.state.isPassphraseHidden; @@ -215,8 +217,7 @@ class Passphrase extends PureComponent { />

- - {' '} + {' '}

@@ -224,7 +225,9 @@ class Passphrase extends PureComponent { { this.passphraseInput = input; }} + innerRef={input => { + this.passphraseInput = input; + }} name="passphraseInputValue" type={this.state.isPassphraseHidden ? 'password' : 'text'} autocorrect="off" @@ -278,11 +281,10 @@ class Passphrase extends PureComponent { {...l10nMessages.TR_CHANGED_YOUR_MIND} values={{ TR_GO_TO_STANDARD_WALLET: ( - this.submitPassphrase(true)} - > - + this.submitPassphrase(true)}> + ), }} @@ -300,4 +302,4 @@ Passphrase.propTypes = { onPassphraseSubmit: PropTypes.func.isRequired, }; -export default Passphrase; \ No newline at end of file +export default Passphrase; diff --git a/src/components/modals/passphrase/Passphrase/index.messages.js b/src/components/modals/passphrase/Passphrase/index.messages.js index ad6b996b..1c059114 100644 --- a/src/components/modals/passphrase/Passphrase/index.messages.js +++ b/src/components/modals/passphrase/Passphrase/index.messages.js @@ -13,7 +13,8 @@ const definedMessages: Messages = defineMessages({ }, TR_IF_YOU_FORGET_YOUR_PASSPHRASE_COMMA: { id: 'TR_IF_YOU_FORGET_YOUR_PASSPHRASE_COMMA', - defaultMessage: 'If you forget your passphrase, your wallet is lost for good. There is no way to recover your funds.', + defaultMessage: + 'If you forget your passphrase, your wallet is lost for good. There is no way to recover your funds.', }, TR_CONFIRM_PASSPHRASE: { id: 'TR_CONFIRM_PASSPHRASE', @@ -43,4 +44,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/passphrase/Type/index.js b/src/components/modals/passphrase/Type/index.js index f830b218..5538e0e2 100644 --- a/src/components/modals/passphrase/Type/index.js +++ b/src/components/modals/passphrase/Type/index.js @@ -13,8 +13,8 @@ import P from 'components/Paragraph'; import type { TrezorDevice } from 'flowtype'; type Props = { - device: TrezorDevice; -} + device: TrezorDevice, +}; const Wrapper = styled.div` max-width: 360px; @@ -27,8 +27,10 @@ const PassphraseType = (props: Props) => (
-

Complete the action on { props.device.label } device

-

If you enter a wrong passphrase, you will not unlock the desired hidden wallet.

+

Complete the action on {props.device.label} device

+

+ If you enter a wrong passphrase, you will not unlock the desired hidden wallet. +

); @@ -37,4 +39,4 @@ PassphraseType.propTypes = { device: PropTypes.object.isRequired, }; -export default PassphraseType; \ No newline at end of file +export default PassphraseType; diff --git a/src/components/modals/pin/Invalid/index.js b/src/components/modals/pin/Invalid/index.js index 4aac6fee..c6525738 100644 --- a/src/components/modals/pin/Invalid/index.js +++ b/src/components/modals/pin/Invalid/index.js @@ -12,8 +12,8 @@ import type { TrezorDevice } from 'flowtype'; import l10nMessages from './index.messages'; type Props = { - device: TrezorDevice; -} + device: TrezorDevice, +}; const Wrapper = styled.div` padding: 30px 48px; @@ -37,4 +37,4 @@ InvalidPin.propTypes = { device: PropTypes.object.isRequired, }; -export default InvalidPin; \ No newline at end of file +export default InvalidPin; diff --git a/src/components/modals/pin/Invalid/index.messages.js b/src/components/modals/pin/Invalid/index.messages.js index 809d1cf8..96c2deac 100644 --- a/src/components/modals/pin/Invalid/index.messages.js +++ b/src/components/modals/pin/Invalid/index.messages.js @@ -13,4 +13,4 @@ const definedMessages: Messages = defineMessages({ }, }); -export default definedMessages; \ No newline at end of file +export default definedMessages; diff --git a/src/components/modals/pin/Pin/components/Button/index.js b/src/components/modals/pin/Pin/components/Button/index.js index 9f73ff6d..99585f27 100644 --- a/src/components/modals/pin/Pin/components/Button/index.js +++ b/src/components/modals/pin/Pin/components/Button/index.js @@ -7,8 +7,8 @@ import colors from 'config/colors'; import { FONT_SIZE, FONT_WEIGHT, SCREEN_SIZE } from 'config/variables'; type Props = { - onClick: () => void; - children: React.Node; + onClick: () => void, + children: React.Node, }; const Wrapper = styled.button` @@ -46,15 +46,11 @@ const Wrapper = styled.button` } `; -const PinButton = ({ children, onClick }: Props) => ( - - { children } - -); +const PinButton = ({ children, onClick }: Props) => {children}; PinButton.propTypes = { children: PropTypes.string.isRequired, onClick: PropTypes.func, }; -export default PinButton; \ No newline at end of file +export default PinButton; diff --git a/src/components/modals/pin/Pin/components/Input/index.js b/src/components/modals/pin/Pin/components/Input/index.js index fd35bd44..9ef0bdca 100644 --- a/src/components/modals/pin/Pin/components/Input/index.js +++ b/src/components/modals/pin/Pin/components/Input/index.js @@ -9,8 +9,8 @@ import { FONT_SIZE, FONT_WEIGHT } from 'config/variables'; import icons from 'config/icons'; type Props = { - value: string; - onDeleteClick: () => void; + value: string, + onDeleteClick: () => void, }; const Wrapper = styled.div` @@ -37,13 +37,7 @@ const StyledIcon = styled(Icon)` const Input = ({ value, onDeleteClick }: Props) => ( - + ); diff --git a/src/components/modals/pin/Pin/index.js b/src/components/modals/pin/Pin/index.js index db8d656e..1d3b5a13 100644 --- a/src/components/modals/pin/Pin/index.js +++ b/src/components/modals/pin/Pin/index.js @@ -19,13 +19,13 @@ import PinInput from './components/Input'; import type { Props as BaseProps } from '../../Container'; type Props = { - device: TrezorDevice; - onPinSubmit: $ElementType<$ElementType, 'onPinSubmit'>; -} + device: TrezorDevice, + onPinSubmit: $ElementType<$ElementType, 'onPinSubmit'>, +}; type State = { - pin: string; -} + pin: string, +}; const Wrapper = styled.div` padding: 30px 48px; @@ -78,13 +78,13 @@ class Pin extends PureComponent { pin, }); } - } + }; onPinBackspace = (): void => { this.setState(previousState => ({ pin: previousState.pin.substring(0, previousState.pin.length - 1), })); - } + }; keyboardHandler(event: KeyboardEvent): void { const { onPinSubmit } = this.props; @@ -138,7 +138,8 @@ class Pin extends PureComponent { case 105: this.onPinAdd(9); break; - default: break; + default: + break; } } @@ -155,24 +156,44 @@ class Pin extends PureComponent { values={{ deviceLabel: device.label }} />

-

+

+ +

this.onPinBackspace()} /> - this.onPinAdd(7)}>• - this.onPinAdd(8)}>• - this.onPinAdd(9)}>• + this.onPinAdd(7)}> + •{' '} + + this.onPinAdd(8)}> + • + + this.onPinAdd(9)}> + • + - this.onPinAdd(4)}>• - this.onPinAdd(5)}>• - this.onPinAdd(6)}>• + this.onPinAdd(4)}> + •{' '} + + this.onPinAdd(5)}> + • + + this.onPinAdd(6)}> + • + - this.onPinAdd(1)}>• - this.onPinAdd(2)}>• - this.onPinAdd(3)}>• + this.onPinAdd(1)}> + •{' '} + + this.onPinAdd(2)}> + • + + this.onPinAdd(3)}> + • +