From 60ead5a4521e5fbe35a7518e64898581ab981b25 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 11 Apr 2018 12:06:46 +0200 Subject: [PATCH] device clone, device connect priority + BIG refactoring for components/containers --- src/data/appConfig.json | 2 +- ...ntActions.js => AbstractAccountActions.js} | 0 src/js/actions/DiscoveryActions.js | 0 src/js/actions/LocalStorageActions.js | 3 - src/js/actions/ModalActions.js | 7 +- src/js/actions/NotificationActions.js | 24 ++ src/js/actions/ReceiveActions.js | 5 +- src/js/actions/SendFormActions.js | 2 + src/js/actions/SummaryActions.js | 102 +------- src/js/actions/TrezorConnectActions.js | 223 ++++++++++------ .../{AppActions.js => WalletActions.js} | 14 +- src/js/actions/Web3Actions.js | 21 +- src/js/actions/constants/Modal.js | 1 + src/js/actions/constants/wallet.js | 6 + src/js/actions/index.js | 45 ---- src/js/components/common/Notification.js | 2 +- src/js/components/landing/ConnectDevice.js | 19 +- src/js/components/landing/InstallBridge.js | 23 +- src/js/components/landing/LandingPage.js | 2 +- .../landing/index.js} | 10 +- src/js/components/modal/ConfirmAddress.js | 52 ++-- src/js/components/modal/DuplicateDevice.js | 12 +- src/js/components/modal/Passphrase.js | 19 +- src/js/components/modal/RememberDevice.js | 17 +- .../modal/{ModalContainer.js => index.js} | 1 + src/js/components/wallet/Acquire.js | 28 ++- src/js/components/wallet/Bootloader.js | 15 +- src/js/components/wallet/Dashboard.js | 15 +- src/js/components/wallet/DeviceSettings.js | 27 ++ src/js/components/wallet/Receive.js | 43 +++- src/js/components/wallet/Settings.js | 12 - src/js/components/wallet/SignVerify.js | 17 +- src/js/components/wallet/WalletSettings.js | 27 ++ .../wallet/account/AbstractAccount.js | 91 ++++++- .../wallet/aside/AccountSelection.js | 3 +- src/js/components/wallet/aside/Aside.js | 4 +- .../wallet/aside/DeviceSelection.js | 80 +++++- .../wallet/aside/index.js} | 15 +- src/js/components/wallet/index.js | 58 +++++ src/js/components/wallet/send/SendForm.js | 27 +- .../wallet/send/index.js} | 14 +- src/js/components/wallet/summary/Summary.js | 17 +- .../summary/{SummaryContainer.js => index.js} | 4 +- src/js/containers/AcquireContainer.js | 23 -- src/js/containers/BootloaderContainer.js | 21 -- src/js/containers/ContentContainer.js | 46 ---- src/js/containers/DashboardContainer.js | 21 -- src/js/containers/HistoryContainer.js | 23 -- src/js/containers/ReceiveContainer.js | 31 --- src/js/containers/SettingsContainer.js | 20 -- src/js/containers/SignVerifyContainer.js | 20 -- src/js/containers/TopNavigationContainer.js | 31 --- src/js/containers/WalletContainer.js | 40 --- src/js/containers/index.js | 19 -- src/js/index.js | 2 +- ...ilReducer.js => AbstractAccountReducer.js} | 0 src/js/reducers/AppReducer.js | 49 ---- src/js/reducers/ModalReducer.js | 7 +- src/js/reducers/NotificationReducer.js | 12 +- src/js/reducers/ReceiveReducer.js | 4 + src/js/reducers/SendFormReducer.js | 14 +- src/js/reducers/SummaryReducer.js | 4 + src/js/reducers/TrezorConnectReducer.js | 138 +++++++--- src/js/reducers/WalletReducer.js | 36 ++- src/js/reducers/index.js | 10 +- src/js/router/index.js | 32 +-- src/js/services/EtherscanService.js | 174 ------------- src/js/services/LocalStorageService.js | 1 + src/js/services/RouterService.js | 28 ++- src/js/services/TrezorConnectService.js | 29 ++- src/js/services/Web3Service.js | 238 ------------------ src/js/services/index.js | 4 - src/js/utils/reducerUtils.js | 5 + src/styles/aside.less | 56 ++++- src/styles/fonts.less | 3 +- src/styles/landingPage.less | 14 +- src/styles/modal.less | 2 + src/styles/send.less | 15 +- src/styles/summary.less | 4 +- 79 files changed, 1020 insertions(+), 1265 deletions(-) rename src/js/actions/{AccountActions.js => AbstractAccountActions.js} (100%) create mode 100644 src/js/actions/DiscoveryActions.js create mode 100644 src/js/actions/NotificationActions.js rename src/js/actions/{AppActions.js => WalletActions.js} (54%) create mode 100644 src/js/actions/constants/wallet.js delete mode 100644 src/js/actions/index.js rename src/js/{containers/LandingPageContainer.js => components/landing/index.js} (60%) rename src/js/components/modal/{ModalContainer.js => index.js} (99%) create mode 100644 src/js/components/wallet/DeviceSettings.js delete mode 100644 src/js/components/wallet/Settings.js create mode 100644 src/js/components/wallet/WalletSettings.js rename src/js/{containers/AsideContainer.js => components/wallet/aside/index.js} (74%) create mode 100644 src/js/components/wallet/index.js rename src/js/{containers/SendFormContainer.js => components/wallet/send/index.js} (78%) rename src/js/components/wallet/summary/{SummaryContainer.js => index.js} (91%) delete mode 100644 src/js/containers/AcquireContainer.js delete mode 100644 src/js/containers/BootloaderContainer.js delete mode 100644 src/js/containers/ContentContainer.js delete mode 100644 src/js/containers/DashboardContainer.js delete mode 100644 src/js/containers/HistoryContainer.js delete mode 100644 src/js/containers/ReceiveContainer.js delete mode 100644 src/js/containers/SettingsContainer.js delete mode 100644 src/js/containers/SignVerifyContainer.js delete mode 100644 src/js/containers/TopNavigationContainer.js delete mode 100644 src/js/containers/WalletContainer.js delete mode 100644 src/js/containers/index.js rename src/js/reducers/{AccountDetailReducer.js => AbstractAccountReducer.js} (100%) delete mode 100644 src/js/reducers/AppReducer.js delete mode 100644 src/js/services/EtherscanService.js delete mode 100644 src/js/services/Web3Service.js diff --git a/src/data/appConfig.json b/src/data/appConfig.json index 9f51e822..b8525626 100644 --- a/src/data/appConfig.json +++ b/src/data/appConfig.json @@ -46,7 +46,7 @@ { "name": "Ethereum Classic", "symbol": "ETC", - "network": "ethereumclassic", + "network": "ethereum-classic", "bip44": "m/44'/61'/0'/0", "defaultGasPrice": 64, "defaultGasLimit": 21000, diff --git a/src/js/actions/AccountActions.js b/src/js/actions/AbstractAccountActions.js similarity index 100% rename from src/js/actions/AccountActions.js rename to src/js/actions/AbstractAccountActions.js diff --git a/src/js/actions/DiscoveryActions.js b/src/js/actions/DiscoveryActions.js new file mode 100644 index 00000000..e69de29b diff --git a/src/js/actions/LocalStorageActions.js b/src/js/actions/LocalStorageActions.js index faa0bbb2..d9cff04b 100644 --- a/src/js/actions/LocalStorageActions.js +++ b/src/js/actions/LocalStorageActions.js @@ -41,9 +41,6 @@ export function loadTokensFromJSON(): any { return collection; }, Promise.resolve({})); - console.log("JADE DAL") - - const devices: ?string = get('devices'); if (devices) { dispatch({ diff --git a/src/js/actions/ModalActions.js b/src/js/actions/ModalActions.js index d31559d9..f8122f83 100644 --- a/src/js/actions/ModalActions.js +++ b/src/js/actions/ModalActions.js @@ -2,7 +2,6 @@ 'use strict'; import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect'; -import * as ACTIONS from './index'; import * as MODAL from './constants/Modal'; import * as CONNECT from './constants/TrezorConnect'; @@ -10,7 +9,7 @@ import * as CONNECT from './constants/TrezorConnect'; export function onPinSubmit(value: string): any { TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value }); return { - type: ACTIONS.CLOSE_MODAL + type: MODAL.CLOSE } } @@ -25,7 +24,7 @@ export function onPassphraseSubmit(passphrase: string): any { }); dispatch({ - type: ACTIONS.CLOSE_MODAL + type: MODAL.CLOSE }); } } @@ -60,7 +59,7 @@ export const onForgetSingleDevice = (device: any) => { export const onCancel = () => { return { - type: ACTIONS.CLOSE_MODAL + type: MODAL.CLOSE } } diff --git a/src/js/actions/NotificationActions.js b/src/js/actions/NotificationActions.js new file mode 100644 index 00000000..6e2151f8 --- /dev/null +++ b/src/js/actions/NotificationActions.js @@ -0,0 +1,24 @@ +/* @flow */ +'use strict'; + +import * as NOTIFICATION from './constants/notification'; + + +// called from RouterService +export const clear = (currentParams, requestedParams): any => { + return async (dispatch, getState) => { + // 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 => { + dispatch({ + type: NOTIFICATION.CLOSE, + payload: { + devicePath: entry.devicePath + } + }) + }); + } + } +} diff --git a/src/js/actions/ReceiveActions.js b/src/js/actions/ReceiveActions.js index 39c4c757..6d3cabd9 100644 --- a/src/js/actions/ReceiveActions.js +++ b/src/js/actions/ReceiveActions.js @@ -21,6 +21,8 @@ export const init = (): any => { const state: State = { ...initialState, deviceState: selected.state, + deviceId: selected.features.device_id, + deviceInstance: selected.instance, accountIndex: parseInt(urlParams.address), network: urlParams.network, location: location.pathname, @@ -67,9 +69,10 @@ export const showAddress = (address_n: string): any => { const selected = findSelectedDevice(getState().connect); if (!selected) return; - if (selected && !selected.connected) { + if (selected && (!selected.connected || !selected.available)) { dispatch({ type: RECEIVE.REQUEST_UNVERIFIED, + device: selected }); return; } diff --git a/src/js/actions/SendFormActions.js b/src/js/actions/SendFormActions.js index a166da48..df551f7c 100644 --- a/src/js/actions/SendFormActions.js +++ b/src/js/actions/SendFormActions.js @@ -117,6 +117,8 @@ export const init = (): any => { const state: State = { ...initialState, deviceState: selected.state, + deviceId: selected.features.device_id, + deviceInstance: selected.instance, accountIndex: parseInt(urlParams.address), network: urlParams.network, coinSymbol: coin.symbol, diff --git a/src/js/actions/SummaryActions.js b/src/js/actions/SummaryActions.js index bee5e20b..d4d74a34 100644 --- a/src/js/actions/SummaryActions.js +++ b/src/js/actions/SummaryActions.js @@ -2,7 +2,6 @@ 'use strict'; import EthereumjsUtil from 'ethereumjs-util'; -import * as ACTIONS from './index'; import * as SUMMARY from './constants/summary'; import * as TOKEN from './constants/Token'; import * as ADDRESS from './constants/Address'; @@ -20,11 +19,13 @@ export const init = (): any => { const urlParams = location.params; const selected = findSelectedDevice( getState().connect ); - if (!selected) return; + if (!selected || !selected.state) return; const state: State = { ...initialState, deviceState: selected.state, + deviceId: selected.features.device_id, + deviceInstance: selected.instance, accountIndex: parseInt(urlParams.address), network: urlParams.network, location: location.pathname, @@ -46,7 +47,7 @@ export const update = (newProps: any): any => { } = getState(); const isLocationChanged: boolean = router.location.pathname !== summary.location; - if (isLocationChanged) { + if (isLocationChanged || !summary.deviceState) { dispatch( init() ); return; } @@ -137,98 +138,3 @@ export const removeToken = (token: any): any => { token } } - - - -export const onTokenSearch = (search: string): any => { - return { - type: ACTIONS.TOKENS_SEARCH, - search - } -} - -export const onCustomTokenToggle = (): any => { - return { - type: ACTIONS.TOKENS_CUSTOM_TOGGLE - } -} - -export const onCustomTokenAddressChange = (value: string): any => { - // todo: - // -validate address - // - if adresss is ok, try to fetch token info - // return { - // type: ACTIONS.TOKENS_CUSTOM_ADDRESS_CHANGE, - // value - // } - - return async (dispatch, getState) => { - - const valid: boolean = EthereumjsUtil.isValidAddress(value); - if (valid) { - - dispatch({ - type: ACTIONS.TOKENS_CUSTOM_ADDRESS_CHANGE, - value, - valid, - fetching: true - }); - - const { web3, abi } = getState().web3; - const contract = web3.eth.contract(abi).at(value); - - contract.name.call((error, name) => { - if (error) { - // TODO: skip - } - contract.symbol.call((error, symbol) => { - if (error) { - // TODO: skip - } - - contract.decimals.call((error, decimals) => { - console.log("fetched!", name, symbol, decimals) - }) - }); - - - }) - - } else { - dispatch({ - type: ACTIONS.TOKENS_CUSTOM_ADDRESS_CHANGE, - value, - valid - }); - } - - console.log("VALID!!!", valid); - } -} - -export const onCustomTokenNameChange = (value: string): any => { - return { - type: ACTIONS.TOKENS_CUSTOM_NAME_CHANGE, - value - } -} - -export const onCustomTokenShortcutChange = (value: string): any => { - return { - type: ACTIONS.TOKENS_CUSTOM_SHORTCUT_CHANGE, - value - } -} - -export const onCustomTokenDecimalChange = (value: string): any => { - return { - type: ACTIONS.TOKENS_CUSTOM_DECIMAL_CHANGE, - value - } -} - -export const onCustomTokenAdd = (): any => { - return { - type: ACTIONS.TOKENS_CUSTOM_ADD - } -} \ No newline at end of file diff --git a/src/js/actions/TrezorConnectActions.js b/src/js/actions/TrezorConnectActions.js index 5f26ba80..9810ad37 100644 --- a/src/js/actions/TrezorConnectActions.js +++ b/src/js/actions/TrezorConnectActions.js @@ -2,20 +2,18 @@ 'use strict'; import TrezorConnect, { UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT } from 'trezor-connect'; -import * as ACTIONS from './index'; import * as ADDRESS from './constants/Address'; import * as TOKEN from './constants/Token'; import * as CONNECT from './constants/TrezorConnect'; import * as DISCOVERY from './constants/Discovery'; import * as NOTIFICATION from './constants/notification'; +import * as WALLET from './constants/wallet'; import HDKey from 'hdkey'; import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsTx from 'ethereumjs-tx'; -//import { getBalance } from '../services/Web3Service'; import { getBalance } from './Web3Actions'; -import { getTransactionHistory } from '../services/EtherscanService'; import { push } from 'react-router-redux'; @@ -24,7 +22,7 @@ import { init as initWeb3, getNonceAsync, getBalanceAsync, getTokenBalanceAsync import type { Discovery } from '../reducers/DiscoveryReducer'; import { resolveAfter } from '../utils/promiseUtils'; import { getAccounts } from '../utils/reducerUtils'; -import { findSelectedDevice, isSavedDevice } from '../reducers/TrezorConnectReducer'; +import { findSelectedDevice, isSavedDevice, TrezorDevice } from '../reducers/TrezorConnectReducer'; export const init = (): any => { return async (dispatch, getState): Promise => { @@ -57,15 +55,18 @@ export const init = (): any => { await TrezorConnect.init({ transportReconnect: true, connectSrc: 'https://localhost:8088/', + // connectSrc: 'https://connect.trezor.io/tpm/', // connectSrc: 'https://sisyfos.trezor.io/', debug: true, popup: false, - webusb: false + webusb: true }); - setTimeout(() => { - dispatch( initWeb3() ); - }, 2000) + // wait for init + + // setTimeout(() => { + // dispatch( initWeb3() ); + //}, 2000) } catch (error) { dispatch({ @@ -102,37 +103,53 @@ export const postInit = (): any => { // devices were connected before Web3 initialized. force DEVICE.CONNECT event on them const { devices } = getState().connect; + + const { initialPathname, initialParams } = getState().wallet + if (initialPathname) { + dispatch({ + type: WALLET.SET_INITIAL_URL, + pathname: null, + params: null + }); + } + if (devices.length > 0) { const unacquired = devices.find(d => d.unacquired); if (unacquired) { - handleDeviceConnect(unacquired); + dispatch( onSelectDevice(unacquired) ); } else { - const latest = devices.sort((a, b) => { - if (!a.ts || !b.ts) { - return -1; + const latest: Array = sortDevices(devices); + const firstConnected: ?TrezorDevice = latest.find(d => d.connected); + dispatch( onSelectDevice(firstConnected || latest[0]) ); + + // TODO + if (initialParams) { + if (!initialParams.hasOwnProperty("network") && initialPathname !== getState().router.location.pathname) { + // dispatch( push(initialPathname) ); } else { - return a.ts > b.ts ? 1 : -1; + } - }); - - console.log("LATEST", latest) + } } } - for (let d of devices) { - handleDeviceConnect(d); - } } } -export const initConnectedDevice = (device: any): any => { - return (dispatch, getState): void => { +const sortDevices = (devices: Array): Array => { + return devices.sort((a, b) => { + if (!a.ts || !b.ts) { + return -1; + } else { + return a.ts > b.ts ? -1 : 1; + } + }); +} - //dispatch( onSelectDevice(device) ); +export const initConnectedDevice = (device: any, force): any => { + return (dispatch, getState): void => { const selected = findSelectedDevice(getState().connect); - if (selected && selected.state) { - dispatch( onSelectDevice(device) ); - } else if (!selected) { + if (!selected || (selected && selected.state)) { dispatch( onSelectDevice(device) ); } // if (device.unacquired && selected && selected.path !== device.path && !selected.connected) { @@ -143,11 +160,19 @@ export const initConnectedDevice = (device: any): any => { } } -// selection from Aside dropdown +// selection from Aside dropdown button +// after device_connect event // or after acquiring device -export function onSelectDevice(device: any): any { +// device type could be local TrezorDevice or Device (from trezor-connect device_connect event) +export const onSelectDevice = (device: any): any => { return (dispatch, getState): void => { // || device.isUsedElsewhere + + console.log("------> REDITTO", device, getState().wallet.initialUrl); + + // switch to initial url and reset this value + + if (device.unacquired) { dispatch( push(`/device/${ device.path }/acquire`) ); } else if (device.features.bootloader_mode) { @@ -155,58 +180,50 @@ export function onSelectDevice(device: any): any { } else if (device.instance) { dispatch( push(`/device/${ device.features.device_id }:${ device.instance }`) ); } else { + + const urlParams: any = getState().router.location.params; + // let url: string = `/device/${ device.features.device_id }/network/ethereum/address/0`; + let url: string = `/device/${ device.features.device_id }`; + let instance: ?string; + // check if device is not TrezorDevice type + if (!device.hasOwnProperty('ts')) { + // its device from trezor-connect (called in initConnectedDevice triggered by device_connect event) + // need to lookup if there are unavailable instances + const available: Array = getState().connect.devices.filter(d => d.path === device.path); + const latest: Array = sortDevices(available); + + if (latest.length > 0 && latest[0].instance) { + url += `:${ latest[0].instance }`; + instance = latest[0].instance; + } + } + // check if current location is not set to this device //dispatch( push(`/device/${ device.features.device_id }/network/etc/address/0`) ); - dispatch( push(`/device/${ device.features.device_id }`) ); + + if (urlParams.deviceInstance !== instance || urlParams.device !== device.features.device_id) { + dispatch( push(url) ); + } } } } -// TODO: as TrezorConnect method -const __getDeviceState = async (path, instance, state): Promise => { - // return await TrezorConnect.getPublicKey({ - // device: { - // path, - // instance - // }, - // path: "m/1'/0'/0'", - // confirmation: false - // }); - - return await TrezorConnect.getDeviceState({ - device: { - path, - instance, - state - }, - path: "m/1'/0'/0'", - confirmation: false - }); -} - export const switchToFirstAvailableDevice = (): any => { return async (dispatch, getState): Promise => { const { devices } = getState().connect; if (devices.length > 0) { // TODO: Priority: - // 1. Unacquired + // 1. First Unacquired // 2. First connected // 3. Saved with latest timestamp - // 4. First from the list const unacquired = devices.find(d => d.unacquired); if (unacquired) { dispatch( initConnectedDevice(unacquired) ); } else { - const latest = devices.sort((a, b) => { - if (!a.ts || !b.ts) { - return -1; - } else { - return a.ts > b.ts ? 1 : -1; - } - }); - dispatch( initConnectedDevice(devices[0]) ); + const latest: Array = sortDevices(devices); + const firstConnected: ?TrezorDevice = latest.find(d => d.connected); + dispatch( onSelectDevice(firstConnected || latest[0]) ); } - } else { dispatch( push('/') ); dispatch({ @@ -217,17 +234,24 @@ export const switchToFirstAvailableDevice = (): any => { } } + export const getSelectedDeviceState = (): any => { return async (dispatch, getState): Promise => { const selected = findSelectedDevice(getState().connect); console.warn("init selected", selected) if (selected && selected.connected - && !selected.unacquired + && selected.features && !selected.acquiring && !selected.state) { - const response = await __getDeviceState(selected.path, selected.instance, selected.state); + const response = await TrezorConnect.getDeviceState({ + device: { + path: selected.path, + instance: selected.instance, + state: selected.state + } + }); if (response && response.success) { dispatch({ @@ -239,20 +263,25 @@ export const getSelectedDeviceState = (): any => { dispatch({ type: NOTIFICATION.ADD, payload: { + devicePath: selected.path, type: 'error', - title: 'Authentification error', + title: 'Authentication error', message: response.payload.error, - cancelable: true, + cancelable: false, actions: [ { label: 'Try again', callback: () => { + dispatch( { + type: NOTIFICATION.CLOSE, + payload: { devicePath: selected.path } + }); dispatch( getSelectedDeviceState() ); } } ] } - }) + }); } } } @@ -273,7 +302,7 @@ export const deviceDisconnect = (device: any): any => { dispatch({ type: CONNECT.REMEMBER_REQUEST, device, - allInstances: affected + instances: affected }); } } @@ -296,22 +325,26 @@ export const coinChanged = (network: ?string): any => { } } +export function reload(): any { + return async (dispatch, getState) => { + } +} export function acquire(): any { return async (dispatch, getState) => { const selected = findSelectedDevice(getState().connect); - const saved = getState().connect.devices.map(d => { - if (d.state) { - return { - instance: d.instance, - state: d.state - } - } else { - return null; - } - }); + // const saved = getState().connect.devices.map(d => { + // if (d.state) { + // return { + // instance: d.instance, + // state: d.state + // } + // } else { + // return null; + // } + // }); //const response = await __acquire(selected.path, selected.instance); @@ -374,6 +407,12 @@ export const forgetDevice = (device: any) => { } } +export const gotoDeviceSettings = (device: any) => { + return (dispatch: any, getState: any): any => { + dispatch( push(`/device/${ device.features.device_id }/settings`) ); + } +} + // called from Aside - device menu (forget single instance) export const forget = (device: any) => { return { @@ -404,6 +443,12 @@ export const beginDiscoveryProcess = (device: any, network: string): any => { const { config } = getState().localStorage; const coinToDiscover = config.coins.find(c => c.network === network); + dispatch({ + type: DISCOVERY.WAITING, + device, + network + }); + // TODO: validate device deviceState // const deviceState = await __acquire(device.path, device.instance); // if (deviceState && deviceState.success) { @@ -425,9 +470,10 @@ export const beginDiscoveryProcess = (device: any, network: string): any => { keepSession: true // acquire and hold session }); + // handle TREZOR response error if (!response.success) { // TODO: check message - console.warn("DISCO ERROR", response) + console.warn("DISCOVERY ERROR", response) dispatch({ type: NOTIFICATION.ADD, payload: { @@ -448,9 +494,10 @@ export const beginDiscoveryProcess = (device: any, network: string): any => { return; } - // TODO: check for interruption + // check for interruption + let discoveryProcess: ?Discovery = getState().discovery.find(d => d.deviceState === device.state && d.network === network); + if (discoveryProcess && discoveryProcess.interrupted) return; - // TODO: handle response error const basePath: Array = response.payload.path; const hdKey = new HDKey(); hdKey.publicKey = new Buffer(response.payload.publicKey, 'hex'); @@ -502,6 +549,15 @@ export const discoverAddress = (device: any, discoveryProcess: Discovery): any = }); if (discoveryProcess.interrupted) return; + // const discoveryA = await TrezorConnect.accountDiscovery({ + // device: { + // path: device.path, + // instance: device.instance, + // state: device.state + // }, + // }); + // if (discoveryProcess.interrupted) return; + if (verifyAddress && verifyAddress.success) { //const trezorAddress: string = '0x' + verifyAddress.payload.address; const trezorAddress: string = EthereumjsUtil.toChecksumAddress(verifyAddress.payload.address); @@ -630,6 +686,9 @@ export function startDiscoveryProcess(device: any, network: string, ignoreComple } else if (!selected.state) { console.warn("Start discovery: Selected device wasn't authenticated yet...") return; + } else if (selected.connected && !selected.available) { + console.warn("Start discovery: Selected device is unavailable...") + return; } const discovery = getState().discovery; @@ -679,8 +738,8 @@ export const restoreDiscovery = (): any => { } // there is no discovery process but it should be -// this is possible race condition when "network" was changed in url but device wasn't authenticated yet -// try to discovery after CONNECT.AUTH_DEVICE action +// this is possible race condition when "network" was changed in url but device was not authenticated yet +// try to start discovery after CONNECT.AUTH_DEVICE action export const checkDiscoveryStatus = (): any => { return (dispatch, getState): void => { const selected = findSelectedDevice(getState().connect); diff --git a/src/js/actions/AppActions.js b/src/js/actions/WalletActions.js similarity index 54% rename from src/js/actions/AppActions.js rename to src/js/actions/WalletActions.js index 99d40154..10649ef8 100644 --- a/src/js/actions/AppActions.js +++ b/src/js/actions/WalletActions.js @@ -2,9 +2,8 @@ 'use strict'; export const ON_RESIZE: string = 'ON_RESIZE'; -export const ON_BEFORE_UNLOAD: string = 'app__on_before_unload'; export const TOGGLE_DEVICE_DROPDOWN: string = 'TOGGLE_DEVICE_DROPDOWN'; -export const RESIZE_CONTAINER: string = 'RESIZE_CONTAINER'; +import * as WALLET from './constants/wallet'; export const onResize = (): any => { return { @@ -14,20 +13,13 @@ export const onResize = (): any => { export const onBeforeUnload = (): any => { return { - type: ON_BEFORE_UNLOAD - } -} - -export const resizeAppContainer = (opened: boolean): any => { - return { - type: RESIZE_CONTAINER, - opened + type: WALLET.ON_BEFORE_UNLOAD } } export const toggleDeviceDropdown = (opened: boolean): any => { return { - type: TOGGLE_DEVICE_DROPDOWN, + type: WALLET.TOGGLE_DEVICE_DROPDOWN, opened } } diff --git a/src/js/actions/Web3Actions.js b/src/js/actions/Web3Actions.js index fb63b9ce..3c363bbf 100644 --- a/src/js/actions/Web3Actions.js +++ b/src/js/actions/Web3Actions.js @@ -7,10 +7,8 @@ import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsTx from 'ethereumjs-tx'; import TrezorConnect from 'trezor-connect'; import { strip } from '../utils/ethUtils'; -import * as ACTIONS from './index'; import * as ADDRESS from './constants/Address'; import * as WEB3 from './constants/Web3'; -import { loadHistory } from '../services/EtherscanService'; import { httpRequest } from '../utils/networkUtils'; type ActionMethod = (dispatch: any, getState: any) => Promise; @@ -192,9 +190,6 @@ export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod { } } -function initBlockTicker() { - -} export function initContracts(): ActionMethod { return async (dispatch, getState) => { @@ -241,14 +236,17 @@ export function getGasPrice(network: string): ActionMethod { const index: number = getState().web3.findIndex(w3 => w3.network === network); - const web3 = getState().web3[ index ].web3; + const web3instance = getState().web3[ index ]; + const web3 = web3instance.web3; web3.eth.getGasPrice((error, gasPrice) => { if (!error) { - dispatch({ - type: WEB3.GAS_PRICE_UPDATED, - network: network, - gasPrice - }); + if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) { + dispatch({ + type: WEB3.GAS_PRICE_UPDATED, + network: network, + gasPrice + }); + } } }); } @@ -313,7 +311,6 @@ export function getTransactionReceipt(tx: any): any { web3.eth.getBlock(receipt.blockHash, (error, block) => { console.log("---MAMM BLOCK", error, block, receipt, receipt.blockHash) dispatch({ - //type: ACTIONS.TX_CONFIRMED, type: WEB3.PENDING_TX_RESOLVED, tx, receipt, diff --git a/src/js/actions/constants/Modal.js b/src/js/actions/constants/Modal.js index f906b7b5..4f20c0fe 100644 --- a/src/js/actions/constants/Modal.js +++ b/src/js/actions/constants/Modal.js @@ -11,3 +11,4 @@ export const FORGET: string = 'modal__forget'; export const REMEMBER: string = 'modal__remember'; export const ON_FORGET: string = 'modal__on_forget'; export const ON_REMEMBER: string = 'modal__on_remember'; +export const CLOSE: string = 'modal__close'; diff --git a/src/js/actions/constants/wallet.js b/src/js/actions/constants/wallet.js new file mode 100644 index 00000000..675a0fc8 --- /dev/null +++ b/src/js/actions/constants/wallet.js @@ -0,0 +1,6 @@ +/* @flow */ +'use strict'; + +export const ON_BEFORE_UNLOAD: string = 'wallet__on_before_unload'; +export const TOGGLE_DEVICE_DROPDOWN: string = 'wallet_toggle_dropdown'; +export const SET_INITIAL_URL: string = 'wallet_set_initial_url'; \ No newline at end of file diff --git a/src/js/actions/index.js b/src/js/actions/index.js deleted file mode 100644 index c0e59632..00000000 --- a/src/js/actions/index.js +++ /dev/null @@ -1,45 +0,0 @@ -/* @flow */ -'use strict'; - -export const CLOSE_MODAL: string = 'action__close_modal'; - -export const ON_PIN_SUBMIT: string = 'action__on_pin_submit'; - -export const ON_PASSPHRASE_SUBMIT: string = 'action__on_passphrase_submit'; - - -export const ON_ADDRESS_CHANGE: string = 'send__on_address_change'; -export const ON_AMOUNT_CHANGE: string = 'send__on_amount_change'; -export const ON_FEE_LEVEL_CHANGE: string = 'send__on_fee_level_change'; -export const ON_GAS_PRICE_CHANGE: string = 'send__on_gas_price_change'; -export const ON_GAS_LIMIT_CHANGE: string = 'send__on_gas_limit_change'; -export const ON_TX_DATA_CHANGE: string = 'send__on_data_change'; -export const ON_TX_SEND: string = 'send__on_send'; -export const ON_GAS_PRICE_UPDATE: string = 'send__on_gas_price_update'; - - -export const ADDRESS_CREATE: string = 'address__create'; -export const ADDRESS_DELETE: string = 'address__delete'; -export const ADDRESS_SET_BALANCE: string = 'address2__set_balance'; -export const ADDRESS_SET_HISTORY: string = 'address__set_history'; -export const ADDRESS_UPDATE_BALANCE: string = 'address__update_balance'; -export const ADDRESS_ADD_TO_HISTORY: string = 'address__add_to_history'; - -export const TX_STATUS_OK: string = 'tx__status_ok'; -export const TX_STATUS_ERROR: string = 'tx__status_error'; -export const TX_STATUS_UNKNOWN: string = 'tx__status_unknown'; -export const TX_CONFIRMED: string = 'tx__confirmed'; - - - -export const TOKENS_TOGGLE_SUMMARY: string = 'tokens_toggle_summary'; - -export const TOKENS_SEARCH: string = 'tokens_search'; - - -export const TOKENS_CUSTOM_TOGGLE: string = 'tokens_custom_toggle'; -export const TOKENS_CUSTOM_ADDRESS_CHANGE: string = 'tokens_custom_address_change'; -export const TOKENS_CUSTOM_NAME_CHANGE: string = 'tokens_custom_name_change'; -export const TOKENS_CUSTOM_SHORTCUT_CHANGE: string = 'tokens_custom_shortcut_change'; -export const TOKENS_CUSTOM_DECIMAL_CHANGE: string = 'tokens_custom_decimal_change'; -export const TOKENS_CUSTOM_ADD: string = 'tokens_custom_add'; \ No newline at end of file diff --git a/src/js/components/common/Notification.js b/src/js/components/common/Notification.js index 21ecbff5..7dbac8fc 100644 --- a/src/js/components/common/Notification.js +++ b/src/js/components/common/Notification.js @@ -26,7 +26,7 @@ export const Notification = (props: any) => { ) : null }

{ props.title }

-

+ { props.message ? (

) : null }
{ props.actions && props.actions.length > 0 ? (
diff --git a/src/js/components/landing/ConnectDevice.js b/src/js/components/landing/ConnectDevice.js index e0e6816f..eb6e7621 100644 --- a/src/js/components/landing/ConnectDevice.js +++ b/src/js/components/landing/ConnectDevice.js @@ -6,19 +6,28 @@ import TrezorConnect from 'trezor-connect'; export default class InstallBridge extends Component { + + componentDidMount(): void { + const transport: any = this.props.transport; + if (transport && transport.version.indexOf('webusb') >= 0) + TrezorConnect.renderWebUSBButton(); + } + componentDidUpdate() { const transport = this.props.transport; - if (transport && transport.type.indexOf('webusb') >= 0) + if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton(); } render() { + let css = 'row'; let webusb = null; let connectClaim = 'Connect TREZOR to continue'; let and = null; let bridgeClaim = null; const transport = this.props.transport; - if (transport && transport.type.indexOf('webusb') >= 0) { + if (transport && transport.version.indexOf('webusb') >= 0) { + css = 'row webusb' webusb = ; connectClaim = 'Connect TREZOR'; and =

and

; @@ -30,7 +39,7 @@ export default class InstallBridge extends Component {

The private bank in your hands.

TREZOR Wallet is an easy-to-use interface for your TREZOR.

TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.

-
+

@@ -44,8 +53,8 @@ export default class InstallBridge extends Component { { connectClaim }

- { and } - { webusb } +

and

+

diff --git a/src/js/components/landing/InstallBridge.js b/src/js/components/landing/InstallBridge.js index a3c3fac4..dc73f2a1 100644 --- a/src/js/components/landing/InstallBridge.js +++ b/src/js/components/landing/InstallBridge.js @@ -2,6 +2,7 @@ 'use strict'; import React, { Component } from 'react'; +import Preloader from './Preloader'; import Select from 'react-select'; type State = { @@ -28,9 +29,9 @@ export default class InstallBridge extends Component { const currentTarget = installers.find(i => i.id === props.browserState.osname); this.state = { - version: '2.0.11', - target: currentTarget, - url: 'https://wallet.trezor.io/data/bridge/2.0.11/' + version: '2.0.12', + url: 'https://wallet.trezor.io/data/bridge/2.0.12/', + target: null, }; } @@ -40,11 +41,25 @@ export default class InstallBridge extends Component { }); } + + componentWillUpdate() { + if (this.props.browserState.osname && !this.state.target) { + const currentTarget = installers.find(i => i.id === this.props.browserState.osname); + this.setState({ + target: currentTarget + }) + } + } + render() { + if (!this.state.target) { + return ; + } + const url = `${ this.state.url }${ this.state.target.value }`; return (

-

TREZOR Bridge. Version 2.0.11

+

TREZOR Bridge. Version 2.0.12

New communication tool to facilitate the connection between your TREZOR and your internet browser.

- - +
+

Clone { device.instanceLabel }?

+

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

+ +
); } diff --git a/src/js/components/modal/Passphrase.js b/src/js/components/modal/Passphrase.js index 2dc09546..5fdfd90b 100644 --- a/src/js/components/modal/Passphrase.js +++ b/src/js/components/modal/Passphrase.js @@ -3,8 +3,10 @@ import React, { Component } from 'react'; import raf from 'raf'; +import { findSelectedDevice } from '../../reducers/TrezorConnectReducer'; type State = { + deviceLabel: string; singleInput: boolean; passphrase: string; passphraseRevision: string; @@ -25,10 +27,20 @@ export default class PinModal extends Component { super(props); // check if this device is already known - const isSavedDevice = props.devices.find(d => d.path === props.modal.device.path && d.remember); + // const isSavedDevice = props.devices.find(d => d.path === props.modal.device.path && d.remember); + const selected = findSelectedDevice(props.connect); + let deviceLabel = props.modal.device.label; + let singleInput = false; + if (selected && selected.path === props.modal.device.path) { + deviceLabel = selected.instanceLabel; + singleInput = selected.remember; + } + + console.warn("-----PASSS", selected) this.state = { - singleInput: isSavedDevice ? true : false, + deviceLabel, + singleInput, passphrase: '', passphraseRevision: '', passphraseFocused: false, @@ -220,6 +232,7 @@ export default class PinModal extends Component { } = this.props.modal; const { + deviceLabel, singleInput, passphrase, passphraseRevision, @@ -242,7 +255,7 @@ export default class PinModal extends Component { return (
{/* */} -

Enter { device.label } passphrase

+

Enter { deviceLabel } passphrase

Note that passphrase is case-sensitive.

diff --git a/src/js/components/modal/RememberDevice.js b/src/js/components/modal/RememberDevice.js index c361282f..c1b5e780 100644 --- a/src/js/components/modal/RememberDevice.js +++ b/src/js/components/modal/RememberDevice.js @@ -63,11 +63,20 @@ export default class RememberDevice extends Component { } render(): any { - const { device } = this.props.modal; + const { device, instances } = this.props.modal; const { onForgetDevice, onRememberDevice } = this.props.modalActions; + + let label = device.label; + let devicePlural = false; + if (instances && instances.length > 0) { + label = instances.map(i => { + return ({i.instanceLabel}); + }) + devicePlural = instances.length > 1; + } return (
-

Forget { device.label } ?

+

Forget {label}?

Would you like TREZOR Wallet to forget your device or to remember it, so that it is still visible even while disconnected?

@@ -81,7 +90,7 @@ export const ForgetDevice = (props: any): any => { const { onForgetSingleDevice, onCancel } = props.modalActions; return (
-

Forget { device.label } ?

+

Forget { device.instanceLabel } ?

Forgetting only removes the device from the list on the left, your bitcoins are still safe and you can access them by reconnecting your TREZOR again.

@@ -94,7 +103,7 @@ export const DisconnectDevice = (props: any): any => { const { onForgetSingleDevice, onCancel } = props.modalActions; return (
-

Unplug { device.label }

+

Unplug { device.instanceLabel }

TREZOR Wallet will forget your TREZOR right after you disconnect it.

TODO: its not true, actually i've already forget those data!!!
diff --git a/src/js/components/modal/ModalContainer.js b/src/js/components/modal/index.js similarity index 99% rename from src/js/components/modal/ModalContainer.js rename to src/js/components/modal/index.js index 3d3e29fe..fd94dd12 100644 --- a/src/js/components/modal/ModalContainer.js +++ b/src/js/components/modal/index.js @@ -105,6 +105,7 @@ const mapStateToProps = (state: any, own: any): any => { modal: state.modal, accounts: state.accounts, devices: state.connect.devices, + connect: state.connect, sendForm: state.sendForm, receive: state.receive, localStorage: state.localStorage diff --git a/src/js/components/wallet/Acquire.js b/src/js/components/wallet/Acquire.js index bb316f1f..2127c15e 100644 --- a/src/js/components/wallet/Acquire.js +++ b/src/js/components/wallet/Acquire.js @@ -2,7 +2,10 @@ 'use strict'; import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; import { Notification } from '../common/Notification'; +import * as TrezorConnectActions from '../../actions/TrezorConnectActions'; const Acquire = (props: any): any => { @@ -21,19 +24,24 @@ const Acquire = (props: any): any => { title="Device is used in other window" message="Do you want to use your device in this window?" className="info" - cancelable={false} - actions={actions} + cancelable={ false } + actions={ actions } close={ () => {} } /> - {/*
-
-

-

-
- -
*/} ); } -export default Acquire; +const mapStateToProps = (state, own) => { + return { + connect: state.connect + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Acquire); diff --git a/src/js/components/wallet/Bootloader.js b/src/js/components/wallet/Bootloader.js index c30a8fed..85c29585 100644 --- a/src/js/components/wallet/Bootloader.js +++ b/src/js/components/wallet/Bootloader.js @@ -2,6 +2,8 @@ 'use strict'; import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; const Bootloader = (props: any): any => { return ( @@ -11,4 +13,15 @@ const Bootloader = (props: any): any => { ); } -export default Bootloader; +const mapStateToProps = (state, own) => { + return { + + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Bootloader); diff --git a/src/js/components/wallet/Dashboard.js b/src/js/components/wallet/Dashboard.js index 6f0935f6..34a03673 100644 --- a/src/js/components/wallet/Dashboard.js +++ b/src/js/components/wallet/Dashboard.js @@ -2,6 +2,8 @@ 'use strict'; import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; const Dashboard = (props: any): any => { return ( @@ -16,4 +18,15 @@ const Dashboard = (props: any): any => { ); } -export default Dashboard; +const mapStateToProps = (state, own) => { + return { + + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Dashboard); diff --git a/src/js/components/wallet/DeviceSettings.js b/src/js/components/wallet/DeviceSettings.js new file mode 100644 index 00000000..e8adc08a --- /dev/null +++ b/src/js/components/wallet/DeviceSettings.js @@ -0,0 +1,27 @@ +/* @flow */ +'use strict'; + +import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +export const DeviceSettings = (props: any): any => { + return ( +
+ Device settings +
+ ); +} + +const mapStateToProps = (state, own) => { + return { + + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(DeviceSettings); diff --git a/src/js/components/wallet/Receive.js b/src/js/components/wallet/Receive.js index cd9ef2ef..d6c8b3ef 100644 --- a/src/js/components/wallet/Receive.js +++ b/src/js/components/wallet/Receive.js @@ -2,18 +2,23 @@ 'use strict'; import React, { Component } from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +import Tooltip from 'rc-tooltip'; import { QRCode } from 'react-qr-svg'; + import AbstractAccount from './account/AbstractAccount'; import { Notification } from '../common/Notification'; -import Tooltip from 'rc-tooltip'; +import * as ReceiveActions from '../../actions/ReceiveActions'; -export default class Receive extends AbstractAccount { +class Receive extends AbstractAccount { render() { - return super.render(this.props.receive) || _render(this.props); + return super.render(this.props.receive) || _render(this.props, this.device, this.account, this.deviceStatusNotification); } } -const _render = (props: any): any => { +const _render = (props: any, device, account, deviceStatusNotification): any => { const { network, @@ -23,8 +28,8 @@ const _render = (props: any): any => { addressUnverified, } = props.receive; - const device = props.devices.find(d => d.state === deviceState); - const account = props.accounts.find(a => a.deviceState === deviceState && a.index === accountIndex && a.network === network); + // const device = props.devices.find(d => d.state === deviceState); + // const account = props.accounts.find(a => a.deviceState === deviceState && a.index === accountIndex && a.network === network); let qrCode = null; let address = `${account.address.substring(0, 20)}...`; @@ -50,7 +55,7 @@ const _render = (props: any): any => { className = addressUnverified ? 'address unverified' : 'address'; const tooltip = addressUnverified ? - (
Unverified address.
{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }
) + (
Unverified address.
{ device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }
) : (
{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }
); @@ -68,9 +73,7 @@ const _render = (props: any): any => { return (
- { !device.connected ? ( - - ) : null } + { deviceStatusNotification }

Receive Ethereum or tokens

@@ -86,3 +89,23 @@ const _render = (props: any): any => { } +const mapStateToProps = (state, own) => { + return { + location: state.router.location, + devices: state.connect.devices, + accounts: state.accounts, + discovery: state.discovery, + receive: state.receive + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + initAccount: bindActionCreators(ReceiveActions.init, dispatch), + updateAccount: bindActionCreators(ReceiveActions.update, dispatch), + disposeAccount: bindActionCreators(ReceiveActions.dispose, dispatch), + showAddress: bindActionCreators(ReceiveActions.showAddress, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Receive); \ No newline at end of file diff --git a/src/js/components/wallet/Settings.js b/src/js/components/wallet/Settings.js deleted file mode 100644 index 27d5535e..00000000 --- a/src/js/components/wallet/Settings.js +++ /dev/null @@ -1,12 +0,0 @@ -/* @flow */ -'use strict'; - -import React from 'react'; - -export default (props: any): any => { - return ( -
- Settings -
- ); -} diff --git a/src/js/components/wallet/SignVerify.js b/src/js/components/wallet/SignVerify.js index d07e41d5..c98f1150 100644 --- a/src/js/components/wallet/SignVerify.js +++ b/src/js/components/wallet/SignVerify.js @@ -2,8 +2,10 @@ 'use strict'; import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; -export default (props: any): any => { +const SignVerify = (props: any): any => { return (
@@ -27,3 +29,16 @@ export default (props: any): any => {
); } + +const mapStateToProps = (state, own) => { + return { + + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(SignVerify); \ No newline at end of file diff --git a/src/js/components/wallet/WalletSettings.js b/src/js/components/wallet/WalletSettings.js new file mode 100644 index 00000000..bc2677d6 --- /dev/null +++ b/src/js/components/wallet/WalletSettings.js @@ -0,0 +1,27 @@ +/* @flow */ +'use strict'; + +import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; + +export const WalletSettings = (props: any): any => { + return ( +
+ Wallet settings +
+ ); +} + +const mapStateToProps = (state, own) => { + return { + + }; +} + +const mapDispatchToProps = (dispatch) => { + return { + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(WalletSettings); diff --git a/src/js/components/wallet/account/AbstractAccount.js b/src/js/components/wallet/account/AbstractAccount.js index e994dbb3..482a05f5 100644 --- a/src/js/components/wallet/account/AbstractAccount.js +++ b/src/js/components/wallet/account/AbstractAccount.js @@ -3,19 +3,54 @@ import React, { Component } from 'react'; import { Notification } from '../../common/Notification'; +import { findDevice } from '../../../utils/reducerUtils'; +import type { TrezorDevice } from '../../../reducers/TrezorConnectReducer'; + +export type AccountState = { + device: TrezorDevice; + discovery: any; + account: any; +} export default class AbstractAccount extends Component { + device: TrezorDevice; + discovery: any; + account: any; + deviceStatusNotification: any; + + constructor(props: any) { + super(props); + this.state = { + + } + } + + setLocalVars(vars: any) { + this.device = vars.device; + this.discovery = vars.discovery; + } + componentDidMount() { this.props.initAccount(); } componentWillUpdate(newProps: any) { + this.device = null; + this.discovery = null; + this.account = null; + this.deviceStatusNotification = null; + this.props.updateAccount(); } componentWillUnmount() { this.props.disposeAccount(); + + this.device = null; + this.discovery = null; + this.account = null; + this.deviceStatusNotification = null; } render(state: any): any { @@ -23,31 +58,49 @@ export default class AbstractAccount extends Component { const props = this.props; if (!state.deviceState) { - return (
); + return (
); } - - const device = this.props.devices.find(d => d.state === state.deviceState); + + const device = findDevice(this.props.devices, state.deviceState, state.deviceId, state.deviceInstance); + if (!device) { return (
Device with state {state.deviceState} not found
); } const discovery = props.discovery.find(d => d.deviceState === device.state && d.network === state.network); const account = props.accounts.find(a => a.deviceState === state.deviceState && a.index === state.accountIndex && a.network === state.network); + let deviceStatusNotification = null; if (!account) { if (!discovery || discovery.waitingForDevice) { - return ( -
- -
- ); + if (device.connected) { + if (device.available) { + return ( +
+ +
+ ); + } else { + return ( +
+ +
+ ); + } + } else { + return ( +
+ +
+ ); + } } else if (discovery.completed) { return (
- +
); } else { @@ -57,8 +110,20 @@ export default class AbstractAccount extends Component {
); } + } else { + if (!device.connected) { + deviceStatusNotification = ; + } else if (!device.available) { + deviceStatusNotification = ; + } } + // Set class variables for extender classes + this.device = device; + this.discovery = discovery; + this.account = account; + this.deviceStatusNotification = deviceStatusNotification; + return null; } } \ No newline at end of file diff --git a/src/js/components/wallet/aside/AccountSelection.js b/src/js/components/wallet/aside/AccountSelection.js index e5a3375f..1a3e3e78 100644 --- a/src/js/components/wallet/aside/AccountSelection.js +++ b/src/js/components/wallet/aside/AccountSelection.js @@ -17,8 +17,9 @@ const AccountSelection = (props: any): any => { if (!selected) return null; const { location } = props.router; + const urlParams = location.params; const accounts = props.accounts; - const baseUrl: string = `/device/${location.params.device}`; + const baseUrl: string = urlParams.deviceInstance ? `/device/${urlParams.device}:${urlParams.deviceInstance}` : `/device/${urlParams.device}`; const { config } = props.localStorage; const selectedCoin = config.coins.find(c => c.network === location.params.network); diff --git a/src/js/components/wallet/aside/Aside.js b/src/js/components/wallet/aside/Aside.js index 29eeadd4..7e93cbb2 100644 --- a/src/js/components/wallet/aside/Aside.js +++ b/src/js/components/wallet/aside/Aside.js @@ -48,7 +48,7 @@ const Aside = (props: any): any => { // ); // } - let menu = null; + let menu =
; if (props.deviceDropdownOpened) { menu = ; @@ -77,4 +77,4 @@ const Aside = (props: any): any => { ) } -export default Aside; \ No newline at end of file +export default Aside; diff --git a/src/js/components/wallet/aside/DeviceSelection.js b/src/js/components/wallet/aside/DeviceSelection.js index f9600a5a..eadca0e9 100644 --- a/src/js/components/wallet/aside/DeviceSelection.js +++ b/src/js/components/wallet/aside/DeviceSelection.js @@ -34,6 +34,10 @@ const Value = (props: any): any => { const deviceMenuItems: Array = []; + if (device.features && device.features.passphrase_protection) { + deviceMenuItems.push("settings"); // TODO: clone + } + if (device.unacquired) { css += " unacquired"; deviceStatus = "Used in other window"; @@ -49,6 +53,9 @@ const Value = (props: any): any => { if (!device.connected) { css += " reload-features"; deviceStatus = "Disconnected"; + } else if (!device.available) { + css += " unavailable"; + deviceStatus = "Unavailable"; } if (device.remember) { @@ -59,6 +66,8 @@ const Value = (props: any): any => { css += " trezor-t"; } + + const deviceMenuButtons = deviceMenuItems.map((item, index) => { return (
onClick(event, item, device) }>
@@ -81,9 +90,6 @@ const Value = (props: any): any => { { device.instanceLabel } { deviceStatus }
-
- { deviceMenuButtons } -
@@ -143,8 +149,7 @@ export const DeviceSelect = (props: any): any => { } } - console.log("DEVSEL", props) - const disabled: boolean = (devices && devices.length <= 1 && transport && transport.type.indexOf('webusb') < 0); + const disabled: boolean = (devices.length < 1 && transport && transport.version.indexOf('webusb') < 0); return ( = 0) + if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton(); } mouseDownHandler(event: MouseEvent): void { - console.log("HANDLE DOWN!!!!", event) let elem = event.target; let block: boolean = false; while (elem.parentElement) { // if (elem.className.indexOf('aside-button') >= 0) { - if (elem.tagName.toLowerCase() === 'aside') { + if (elem.tagName.toLowerCase() === 'aside' || (elem.className && elem.className.indexOf('modal-container') >= 0)) { block = true; break; } @@ -199,7 +203,7 @@ export class DeviceDropdown extends Component { window.addEventListener('mousedown', this.mouseDownHandler, false); // window.addEventListener('blur', this.blurHandler, false); const transport: any = this.props.connect.transport; - if (transport && transport.type.indexOf('webusb') >= 0) + if (transport && transport.version.indexOf('webusb') >= 0) TrezorConnect.renderWebUSBButton(); } @@ -208,16 +212,69 @@ export class DeviceDropdown extends Component { // window.removeEventListener('blur', this.blurHandler, false); } + onDeviceMenuClick(item, device): void { + if (item.type === 'reload') { + this.props.acquireDevice(device); + } else if (item.type === 'forget') { + // this.props.toggleDeviceDropdown(false); + this.props.forgetDevice(device); + } else if (item.type === 'clone') { + this.props.duplicateDevice(device); + } else if (item.type === 'settings') { + this.props.toggleDeviceDropdown(false); + this.props.gotoDeviceSettings(device); + } + } + render() { const { devices, transport } = this.props.connect; const selected = findSelectedDevice(this.props.connect); let webUsbButton = null; - if (transport && transport.type.indexOf('webusb') >= 0) { + if (transport && transport.version.indexOf('webusb') >= 0) { webUsbButton = ; } + let currentDeviceMenu = null; + if (selected.features) { + const deviceMenuItems: Array = []; + + if (selected.isUsedElsewhere) { + deviceMenuItems.push({ type: "reload", label: "Renew session" }); + } else if (selected.featuresNeedsReload) { + deviceMenuItems.push({ type: "reload", label: "Reload device" }); + } + + deviceMenuItems.push({ type: "settings", label: "Device settings" }); + if (selected.features.passphrase_protection && selected.connected && selected.available) { + deviceMenuItems.push({ type: "clone", label: "Clone device" }); + } + if (selected.remember) { + deviceMenuItems.push({ type: "forget", label: "Forget device" }); + } + + + const deviceMenuButtons = deviceMenuItems.map((item, index) => { + return ( +
this.onDeviceMenuClick(item, selected) }>{ item.label}
+ ) + }); + currentDeviceMenu = deviceMenuButtons.length < 1 ? null : ( +
+ { deviceMenuButtons } +
+ ); + } + + // const currentDeviceMenu = ( + //
+ //
Device settings
+ //
Clone device
+ //
Forget device
+ //
+ // ); + const deviceList: Array = devices.map((dev, index) => { if (dev === selected) return null; @@ -227,6 +284,8 @@ export class DeviceDropdown extends Component { deviceStatus = "Used in other window"; } else if (!dev.connected) { deviceStatus = "Disconnected"; + } else if (!dev.available) { + deviceStatus = "Unavailable"; } if (dev.features && dev.features.major_version > 1) { @@ -245,6 +304,7 @@ export class DeviceDropdown extends Component { return (
+ { currentDeviceMenu } { webUsbButton } { deviceList }
diff --git a/src/js/containers/AsideContainer.js b/src/js/components/wallet/aside/index.js similarity index 74% rename from src/js/containers/AsideContainer.js rename to src/js/components/wallet/aside/index.js index 12171cbe..3f0f4a14 100644 --- a/src/js/containers/AsideContainer.js +++ b/src/js/components/wallet/aside/index.js @@ -5,23 +5,25 @@ import React, { Component, PropTypes } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; -import Aside from '../components/wallet/aside/Aside'; -import * as TrezorConnectActions from '../actions/TrezorConnectActions'; -import { toggleDeviceDropdown } from '../actions/AppActions'; -function mapStateToProps(state, own) { +import * as TrezorConnectActions from '../../../actions/TrezorConnectActions'; +import { toggleDeviceDropdown } from '../../../actions/WalletActions'; + +import Aside from './Aside'; + +const mapStateToProps = (state, own) => { return { connect: state.connect, accounts: state.accounts, router: state.router, - deviceDropdownOpened: state.DOM.deviceDropdownOpened, + deviceDropdownOpened: state.wallet.dropdownOpened, fiat: state.fiat, localStorage: state.localStorage, discovery: state.discovery }; } -function mapDispatchToProps(dispatch) { +const mapDispatchToProps = (dispatch) => { return { //onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch), toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch), @@ -29,6 +31,7 @@ function mapDispatchToProps(dispatch) { acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), forgetDevice: bindActionCreators(TrezorConnectActions.forget, dispatch), duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch), + gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch), onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch), }; } diff --git a/src/js/components/wallet/index.js b/src/js/components/wallet/index.js new file mode 100644 index 00000000..99c95679 --- /dev/null +++ b/src/js/components/wallet/index.js @@ -0,0 +1,58 @@ +/* @flow */ +'use strict'; + +import React from 'react'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Route, withRouter } from 'react-router-dom'; + +import Header from '../common/Header'; +import Footer from '../common/Footer'; +import AccountTabs from './account/AccountTabs'; +import AsideContainer from './aside'; +import ModalContainer from '../modal'; +import Notifications from '../common/Notification'; +import Log from '../common/Log'; + +const Content = (props) => { + return ( +
+ + + + { props.children } +
+
+ ); +} + +const Wallet = (props: any): any => { + return ( +
+
+
+ + + { props.children } + +
+ +
+ ); +} + +const mapStateToProps = (state, own) => { + return { + + }; +} + +const mapDispatchToProps = (dispatch) => { + return { }; +} + +export default withRouter( + connect(mapStateToProps, mapDispatchToProps)(Wallet) +); diff --git a/src/js/components/wallet/send/SendForm.js b/src/js/components/wallet/send/SendForm.js index 90e2c0d3..8aae9733 100644 --- a/src/js/components/wallet/send/SendForm.js +++ b/src/js/components/wallet/send/SendForm.js @@ -11,16 +11,16 @@ import AbstractAccount from '../account/AbstractAccount'; export default class Send extends AbstractAccount { render() { - return super.render(this.props.sendForm) || _render(this.props); + return super.render(this.props.sendForm) || _render(this.props, this.device, this.discovery, this.account, this.deviceStatusNotification); } } -const _render = (props: any): any => { +const _render = (props: any, device, discovery, account, deviceStatusNotification): any => { - const device = props.devices.find(device => device.state === props.sendForm.deviceState); - const discovery = props.discovery.find(d => d.deviceState === device.state && d.network === props.sendForm.network); - const account = props.accounts.find(a => a.deviceState === props.sendForm.deviceState && a.index === props.sendForm.accountIndex && a.network === props.sendForm.network); + // const device = props.devices.find(device => device.state === props.sendForm.deviceState); + // const discovery = props.discovery.find(d => d.deviceState === device.state && d.network === props.sendForm.network); + // const account = props.accounts.find(a => a.deviceState === props.sendForm.deviceState && a.index === props.sendForm.accountIndex && a.network === props.sendForm.network); const addressTokens = props.tokens.filter(t => t.ethAddress === account.address); const { @@ -87,9 +87,16 @@ const _render = (props: any): any => { buttonLabel += ` ${total} ${ selectedCoin.symbol }`; } - if (device && !device.connected) { - buttonLabel = 'Device is not connected'; - buttonDisabled = true; + if (device) { + + if (!device.connected){ + buttonLabel = 'Device is not connected'; + buttonDisabled = true; + } else if (!device.available) { + buttonLabel = 'Device is unavailable'; + buttonDisabled = true; + } + } let notification = null; @@ -97,9 +104,7 @@ const _render = (props: any): any => { return (
- { !device.connected ? ( - - ) : null } + { deviceStatusNotification }

Send Ethereum or tokens

diff --git a/src/js/containers/SendFormContainer.js b/src/js/components/wallet/send/index.js similarity index 78% rename from src/js/containers/SendFormContainer.js rename to src/js/components/wallet/send/index.js index 41f67113..5dc31053 100644 --- a/src/js/containers/SendFormContainer.js +++ b/src/js/components/wallet/send/index.js @@ -5,10 +5,10 @@ import React, { Component, PropTypes } from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import SendForm from '../components/wallet/send/SendForm'; -import * as SendFormActions from '../actions/SendFormActions'; +import * as SendFormActions from '../../../actions/SendFormActions'; +import SendForm from './SendForm'; -function mapStateToProps(state, own) { +const mapStateToProps = (state, own) => { return { location: state.router.location, devices: state.connect.devices, @@ -22,13 +22,13 @@ function mapStateToProps(state, own) { }; } -function mapDispatchToProps(dispatch) { - return { +const mapDispatchToProps = (dispatch) => { + return { sendFormActions: bindActionCreators(SendFormActions, dispatch), initAccount: bindActionCreators(SendFormActions.init, dispatch), updateAccount: bindActionCreators(SendFormActions.update, dispatch), - disposeAccount: bindActionCreators(SendFormActions.dispose, dispatch), + disposeAccount: bindActionCreators(SendFormActions.dispose, dispatch), }; } -export default connect(mapStateToProps, mapDispatchToProps)(SendForm); \ No newline at end of file +export default connect(mapStateToProps, mapDispatchToProps)(SendForm) \ No newline at end of file diff --git a/src/js/components/wallet/summary/Summary.js b/src/js/components/wallet/summary/Summary.js index 3329abdb..fcf4d480 100644 --- a/src/js/components/wallet/summary/Summary.js +++ b/src/js/components/wallet/summary/Summary.js @@ -9,27 +9,28 @@ import AbstractAccount from '../account/AbstractAccount'; import { Notification } from '../../common/Notification'; import SummaryDetails from './SummaryDetails.js'; import SummaryTokens from './SummaryTokens.js'; +import { findDevice } from '../../../utils/reducerUtils'; export default class Summary extends AbstractAccount { render() { - return super.render(this.props.summary) || _render(this.props); + console.warn("RENDER SUMMARY!", this.device, this.discovery, this) + return super.render(this.props.summary) || _render(this.props, this.device, this.discovery, this.account, this.deviceStatusNotification); } } -const _render = (props: any): any => { +const _render = (props: any, device, discovery, account, deviceStatusNotification): any => { - const device = props.devices.find(d => d.state === props.summary.deviceState); - const discovery = props.discovery.find(d => d.deviceState === device.state && d.network === props.summary.network); - const account = props.accounts.find(a => a.deviceState === props.summary.deviceState && a.index === props.summary.accountIndex && a.network === props.summary.network); + //const device = props.devices.find(d => d.state === props.summary.deviceState && d.features.device_id === props.summary.deviceId); + // const device = findDevice(props.devices, props.summary.deviceState, props.summary.deviceId); + // const discovery = props.discovery.find(d => d.deviceState === device.state && d.network === props.summary.network); + //const account = props.accounts.find(a => a.deviceState === props.summary.deviceState && a.index === props.summary.accountIndex && a.network === props.summary.network); const tokens = props.tokens.filter(t => t.ethAddress === account.address); return (
- { !device.connected ? ( - - ) : null } + { deviceStatusNotification }

Address #{ parseInt(props.match.params.address) + 1 }

diff --git a/src/js/components/wallet/summary/SummaryContainer.js b/src/js/components/wallet/summary/index.js similarity index 91% rename from src/js/components/wallet/summary/SummaryContainer.js rename to src/js/components/wallet/summary/index.js index 3b9d8df9..2f85cd64 100644 --- a/src/js/components/wallet/summary/SummaryContainer.js +++ b/src/js/components/wallet/summary/index.js @@ -8,7 +8,7 @@ import { connect } from 'react-redux'; import Summary from './Summary'; import * as SummaryActions from '../../../actions/SummaryActions'; -function mapStateToProps(state, own) { +const mapStateToProps = (state, own) => { return { location: state.router.location, devices: state.connect.devices, @@ -21,7 +21,7 @@ function mapStateToProps(state, own) { }; } -function mapDispatchToProps(dispatch) { +const mapDispatchToProps = (dispatch) => { return { summaryActions: bindActionCreators(SummaryActions, dispatch), initAccount: bindActionCreators(SummaryActions.init, dispatch), diff --git a/src/js/containers/AcquireContainer.js b/src/js/containers/AcquireContainer.js deleted file mode 100644 index 31f2f6c7..00000000 --- a/src/js/containers/AcquireContainer.js +++ /dev/null @@ -1,23 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; - -import Acquire from '../components/wallet/Acquire'; -import * as TrezorConnectActions from '../actions/TrezorConnectActions'; - -function mapStateToProps(state, own) { - return { - connect: state.connect - }; -} - -function mapDispatchToProps(dispatch) { - return { - acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Acquire); \ No newline at end of file diff --git a/src/js/containers/BootloaderContainer.js b/src/js/containers/BootloaderContainer.js deleted file mode 100644 index 6db873cf..00000000 --- a/src/js/containers/BootloaderContainer.js +++ /dev/null @@ -1,21 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; - -import Bootloader from '../components/wallet/Bootloader'; - -function mapStateToProps(state, own) { - return { - - }; -} - -function mapDispatchToProps(dispatch) { - return { - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Bootloader); \ No newline at end of file diff --git a/src/js/containers/ContentContainer.js b/src/js/containers/ContentContainer.js deleted file mode 100644 index b5009f80..00000000 --- a/src/js/containers/ContentContainer.js +++ /dev/null @@ -1,46 +0,0 @@ -/* @flow */ -'use strict'; - -import React from 'react'; -import { Route } from 'react-router-dom'; - -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router-dom'; -import Log from '../components/common/Log'; -import Notifications from '../components/common/Notification'; -import Footer from '../components/common/Footer'; -import AccountTabs from '../components/wallet/account/AccountTabs'; - -import * as TrezorConnectActions from '../actions/TrezorConnectActions'; -import * as LogActions from '../actions/LogActions'; - -const Article = (props) => { - return ( -
- - - - { props.children } -
-
- ); -} - -function mapStateToProps(state, own) { - return { - - }; -} - -function mapDispatchToProps(dispatch) { - return { - - }; -} - -export default withRouter( - connect(mapStateToProps, mapDispatchToProps)(Article) -); \ No newline at end of file diff --git a/src/js/containers/DashboardContainer.js b/src/js/containers/DashboardContainer.js deleted file mode 100644 index b3cd6406..00000000 --- a/src/js/containers/DashboardContainer.js +++ /dev/null @@ -1,21 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; - -import Dashboard from '../components/wallet/Dashboard'; - -function mapStateToProps(state, own) { - return { - - }; -} - -function mapDispatchToProps(dispatch) { - return { - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Dashboard); \ No newline at end of file diff --git a/src/js/containers/HistoryContainer.js b/src/js/containers/HistoryContainer.js deleted file mode 100644 index 7723fea9..00000000 --- a/src/js/containers/HistoryContainer.js +++ /dev/null @@ -1,23 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; - -import History from '../components/wallet/History'; -import * as SendFormActions from '../actions/SendFormActions'; - -function mapStateToProps(state, own) { - return { - web3: state.web3.web3, - }; -} - -function mapDispatchToProps(dispatch) { - return { - //sendFormActions: bindActionCreators(SendFormActions, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(History); \ No newline at end of file diff --git a/src/js/containers/ReceiveContainer.js b/src/js/containers/ReceiveContainer.js deleted file mode 100644 index ec729912..00000000 --- a/src/js/containers/ReceiveContainer.js +++ /dev/null @@ -1,31 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; - -import Receive from '../components/wallet/Receive'; -import * as ReceiveActions from '../actions/ReceiveActions'; -import { getAddress } from '../actions/TrezorConnectActions'; - -function mapStateToProps(state, own) { - return { - location: state.router.location, - devices: state.connect.devices, - accounts: state.accounts, - discovery: state.discovery, - receive: state.receive - }; -} - -function mapDispatchToProps(dispatch) { - return { - initAccount: bindActionCreators(ReceiveActions.init, dispatch), - updateAccount: bindActionCreators(ReceiveActions.update, dispatch), - disposeAccount: bindActionCreators(ReceiveActions.dispose, dispatch), - showAddress: bindActionCreators(ReceiveActions.showAddress, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Receive); \ No newline at end of file diff --git a/src/js/containers/SettingsContainer.js b/src/js/containers/SettingsContainer.js deleted file mode 100644 index c63cc894..00000000 --- a/src/js/containers/SettingsContainer.js +++ /dev/null @@ -1,20 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; - -import Settings from '../components/wallet/Settings'; - -function mapStateToProps(state, own) { - return { - }; -} - -function mapDispatchToProps(dispatch) { - return { - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Settings); \ No newline at end of file diff --git a/src/js/containers/SignVerifyContainer.js b/src/js/containers/SignVerifyContainer.js deleted file mode 100644 index 8a811e7e..00000000 --- a/src/js/containers/SignVerifyContainer.js +++ /dev/null @@ -1,20 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; - -import SignVerify from '../components/wallet/SignVerify'; - -function mapStateToProps(state, own) { - return { - }; -} - -function mapDispatchToProps(dispatch) { - return { - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(SignVerify); \ No newline at end of file diff --git a/src/js/containers/TopNavigationContainer.js b/src/js/containers/TopNavigationContainer.js deleted file mode 100644 index 95dd0eb4..00000000 --- a/src/js/containers/TopNavigationContainer.js +++ /dev/null @@ -1,31 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component, PropTypes } from 'react'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router-dom'; - -import TopNavigation from '../components/TopNavigation'; -import * as TrezorConnectActions from '../actions/TrezorConnectActions'; -import { resizeAppContainer, toggleDeviceDropdown } from '../actions/AppActions'; - -function mapStateToProps(state, own) { - return { - connect: state.connect - }; -} - -function mapDispatchToProps(dispatch) { - return { - toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch), - resizeAppContainer: bindActionCreators(resizeAppContainer, dispatch), - acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), - onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch), - }; -} - -// export default connect(mapStateToProps, mapDispatchToProps)(TopNavigation); -export default withRouter( - connect(mapStateToProps, mapDispatchToProps)(TopNavigation) -); \ No newline at end of file diff --git a/src/js/containers/WalletContainer.js b/src/js/containers/WalletContainer.js deleted file mode 100644 index d05ef8fe..00000000 --- a/src/js/containers/WalletContainer.js +++ /dev/null @@ -1,40 +0,0 @@ -/* @flow */ -'use strict'; - -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router-dom'; - -import Header from '../components/common/Header'; -import AsideContainer from './AsideContainer'; -import ContentContainer from './ContentContainer'; -import ModalContainer from '../components/modal/ModalContainer'; - -const Wallet = (props: any): any => { - return ( -
-
-
- - - { props.children } - -
- -
- ); -} - -function mapStateToProps(state, own) { - return { - - }; -} - -function mapDispatchToProps(dispatch) { - return { }; -} - -export default withRouter( - connect(mapStateToProps, mapDispatchToProps)(Wallet) -); diff --git a/src/js/containers/index.js b/src/js/containers/index.js deleted file mode 100644 index 0adc537c..00000000 --- a/src/js/containers/index.js +++ /dev/null @@ -1,19 +0,0 @@ -/* @flow */ -'use strict'; - -// wrapper layouts -export { default as LandingPageContainer } from './LandingPageContainer'; -export { default as WalletContainer } from './WalletContainer'; - -// wallet sections -export { default as AcquireContainer } from './AcquireContainer'; -export { default as BootloaderContainer } from './BootloaderContainer'; - -export { default as DashboardContainer } from './DashboardContainer'; -export { default as HistoryContainer } from './HistoryContainer'; -export { default as SendFormContainer } from './SendFormContainer'; -export { default as ReceiveContainer } from './ReceiveContainer'; -export { default as SignVerifyContainer } from './SignVerifyContainer'; - - -export { default as SettingsContainer } from './SettingsContainer'; diff --git a/src/js/index.js b/src/js/index.js index f499d60b..48435b43 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -5,7 +5,7 @@ import React from 'react'; import { render } from 'react-dom'; import store from './store'; import router from './router'; -import { onResize, onBeforeUnload } from './actions/AppActions'; +import { onBeforeUnload } from './actions/WalletActions'; import Raven from 'raven-js'; diff --git a/src/js/reducers/AccountDetailReducer.js b/src/js/reducers/AbstractAccountReducer.js similarity index 100% rename from src/js/reducers/AccountDetailReducer.js rename to src/js/reducers/AbstractAccountReducer.js diff --git a/src/js/reducers/AppReducer.js b/src/js/reducers/AppReducer.js deleted file mode 100644 index a033b40a..00000000 --- a/src/js/reducers/AppReducer.js +++ /dev/null @@ -1,49 +0,0 @@ -/* @flow */ -'use strict'; - -import { ON_RESIZE, TOGGLE_DEVICE_DROPDOWN, RESIZE_CONTAINER } from '../actions/AppActions'; -import * as WEB3 from '../actions/constants/Web3'; - -const WIDTH: number = 1080; -const HEIGHT: number = 1920; - -const initialState: Object = { - orginalWidth: WIDTH, - orginalHeight: HEIGHT, - width: window.innerWidth, - height: window.innerHeight, - scale: Math.min(window.innerWidth / WIDTH, window.innerHeight / HEIGHT), - coinDropdownOpened: false, - deviceDropdownOpened: false, - initialized: false, - landingPage: true, -}; - -export default function DOM(state: Object = initialState, action: Object): any { - switch(action.type) { - case ON_RESIZE : - return { - ...state, - scale: Math.min(window.innerWidth / WIDTH, window.innerHeight / HEIGHT), - } - case RESIZE_CONTAINER : - return { - ...state, - coinDropdownOpened: action.opened - } - case TOGGLE_DEVICE_DROPDOWN : - return { - ...state, - deviceDropdownOpened: action.opened - } - - case WEB3.READY : - return { - ...state, - initialized: true - } - - default: - return state; - } -} diff --git a/src/js/reducers/ModalReducer.js b/src/js/reducers/ModalReducer.js index b7daab3d..50c8650a 100644 --- a/src/js/reducers/ModalReducer.js +++ b/src/js/reducers/ModalReducer.js @@ -2,7 +2,6 @@ 'use strict'; import { UI, DEVICE } from 'trezor-connect'; -import * as ACTIONS from '../actions'; import * as RECEIVE from '../actions/constants/receive'; import * as MODAL from '../actions/constants/Modal'; import * as CONNECT from '../actions/constants/TrezorConnect'; @@ -10,12 +9,14 @@ import * as CONNECT from '../actions/constants/TrezorConnect'; type ModalState = { opened: boolean; device: any; + instances: Array; windowType: ?string; } const initialState: ModalState = { opened: false, device: null, + instances: null, windowType: null }; @@ -26,6 +27,7 @@ export default function modal(state: ModalState = initialState, action: any): an case RECEIVE.REQUEST_UNVERIFIED : return { ...state, + device: action.device, opened: true, windowType: action.type } @@ -36,6 +38,7 @@ export default function modal(state: ModalState = initialState, action: any): an return { ...state, device: action.device, + instances: action.instances, opened: true, windowType: action.type }; @@ -92,7 +95,7 @@ export default function modal(state: ModalState = initialState, action: any): an } case UI.CLOSE_UI_WINDOW : - case ACTIONS.CLOSE_MODAL : + case MODAL.CLOSE : case CONNECT.FORGET : case CONNECT.FORGET_SINGLE : diff --git a/src/js/reducers/NotificationReducer.js b/src/js/reducers/NotificationReducer.js index b0edf133..3a97130b 100644 --- a/src/js/reducers/NotificationReducer.js +++ b/src/js/reducers/NotificationReducer.js @@ -3,6 +3,7 @@ import { LOCATION_CHANGE } from 'react-router-redux'; import * as NOTIFICATION from '../actions/constants/notification'; +import { DEVICE } from 'trezor-connect'; type NotificationAction = { label: string; @@ -11,6 +12,7 @@ type NotificationAction = { type NotificationEntry = { +id: ?string; + +devicePath: ?string; +type: string; +title: string; +message: string; @@ -33,6 +35,7 @@ const addNotification = (state: Array, payload: any): Array = state.filter(e => !e.cancelable); newState.push({ id: payload.id, + devicePath: payload.devicePath, type: payload.type, title: payload.title.toString(), message: payload.message.toString(), @@ -46,9 +49,11 @@ const addNotification = (state: Array, payload: any): Array, payload: any): Array => { if (payload && typeof payload.id === 'string') { - return state.filter(e => e.id !== payload.id); + return state.filter(entry => entry.id !== payload.id); + } else if (payload && typeof payload.devicePath === 'string') { + return state.filter(entry => entry.devicePath !== payload.devicePath); } else { - return state.filter(e => !e.cancelable); + return state.filter(entry => !entry.cancelable); } } @@ -61,6 +66,9 @@ export default function notification(state: Array = initialSt case LOCATION_CHANGE : case NOTIFICATION.CLOSE : return closeNotification(state, action.payload); + + case DEVICE.DISCONNECT : + return state.filter(entry => entry.devicePath !== action.device.path); default: return state; diff --git a/src/js/reducers/ReceiveReducer.js b/src/js/reducers/ReceiveReducer.js index d564bb28..7f079f11 100644 --- a/src/js/reducers/ReceiveReducer.js +++ b/src/js/reducers/ReceiveReducer.js @@ -5,6 +5,8 @@ import * as RECEIVE from '../actions/constants/receive'; export type State = { +deviceState: ?string; + +deviceId: ?string; + +deviceInstance: ?string; +accountIndex: ?number; +network: ?string; location: string; @@ -14,6 +16,8 @@ export type State = { export const initialState: State = { deviceState: null, + deviceId: null, + deviceInstance: null, accountIndex: null, network: null, location: '', diff --git a/src/js/reducers/SendFormReducer.js b/src/js/reducers/SendFormReducer.js index 82a4a66c..c977db93 100644 --- a/src/js/reducers/SendFormReducer.js +++ b/src/js/reducers/SendFormReducer.js @@ -11,6 +11,8 @@ import { getFeeLevels } from '../actions/SendFormActions'; export type State = { +deviceState: ?string; + +deviceId: ?string; + +deviceInstance: ?string; +accountIndex: number; +network: string; +coinSymbol: string; @@ -51,6 +53,8 @@ export type FeeLevel = { export const initialState: State = { deviceState: null, + deviceId: null, + deviceInstance: null, accountIndex: 0, network: '', coinSymbol: '', @@ -83,11 +87,11 @@ export const initialState: State = { const onGasPriceUpdated = (state: State, action: any): State => { - function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - const newPrice = getRandomInt(10, 50).toString(); - //const newPrice = EthereumjsUnits.convert(action.gasPrice, 'wei', 'gwei'); + // function getRandomInt(min, max) { + // return Math.floor(Math.random() * (max - min + 1)) + min; + // } + // const newPrice = getRandomInt(10, 50).toString(); + const newPrice = EthereumjsUnits.convert(action.gasPrice, 'wei', 'gwei'); if (action.network === state.network && newPrice !== state.recommendedGasPrice) { const newState: State = { ...state }; if (!state.untouched) { diff --git a/src/js/reducers/SummaryReducer.js b/src/js/reducers/SummaryReducer.js index 6b2e7155..415fa5cb 100644 --- a/src/js/reducers/SummaryReducer.js +++ b/src/js/reducers/SummaryReducer.js @@ -5,6 +5,8 @@ import * as SUMMARY from '../actions/constants/summary'; export type State = { +deviceState: ?string; + +deviceId: ?string; + +deviceInstance: ?string; +accountIndex: ?number; +network: ?string; location: string; @@ -15,6 +17,8 @@ export type State = { export const initialState: State = { deviceState: null, + deviceId: null, + deviceInstance: null, accountIndex: null, network: null, location: '', diff --git a/src/js/reducers/TrezorConnectReducer.js b/src/js/reducers/TrezorConnectReducer.js index f4c0f5ea..a0596902 100644 --- a/src/js/reducers/TrezorConnectReducer.js +++ b/src/js/reducers/TrezorConnectReducer.js @@ -7,6 +7,7 @@ import * as CONNECT from '../actions/constants/TrezorConnect'; export type TrezorDevice = { remember: boolean; connected: boolean; + available: boolean; // device cannot be used because of features.passphrase_protection is different then expected (saved) path: string; +label: string; +state: string; @@ -58,6 +59,8 @@ export const findSelectedDevice = (state: State): ?TrezorDevice => { }); } + + export const isSavedDevice = (state: State, device: any): ?Array => { const selected: ?SelectedDevice = state.selectedDevice; if (!selected) return null; @@ -73,17 +76,36 @@ export const isSavedDevice = (state: State, device: any): ?Array = } const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice => { + + // do not merge if passphrase protection was changed + // if (upcoming.features && current.features) { + // if (upcoming.features.passphrase_protection !== current.features.passphrase_protection) { + // // device settings has been changed, reset state + // // dev.state = null; + // // console.log("RESTETTTT STATE!"); + // } + // } + + let instanceLabel = current.instanceLabel; + if (upcoming.label !== current.label) { + instanceLabel = upcoming.label + if (typeof current.instance === 'number') { + instanceLabel += ` (${current.instance})`; + } + } + const dev: TrezorDevice = { // ...current, ...upcoming, // make sure that instance specific variables will not be overridden connected: typeof upcoming.connected === 'boolean' ? upcoming.connected : current.connected, + available: typeof upcoming.available === 'boolean' ? upcoming.available : current.available, remember: typeof upcoming.remember === 'boolean' ? upcoming.remember : current.remember, instance: current.instance, - instanceLabel: current.instanceLabel, + instanceLabel, state: current.state, acquiring: typeof upcoming.acquiring === 'boolean' ? upcoming.acquiring : current.acquiring, - ts: new Date().getTime(), + ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts, } if (upcoming.unacquired && current.state) { @@ -94,18 +116,11 @@ const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice => } else if (!upcoming.unacquired && current.unacquired) { dev.instanceLabel = upcoming.label; if (typeof dev.instance === 'number') { - dev.instanceLabel = `${upcoming.label} #${dev.instance}`; + dev.instanceLabel = `${upcoming.label} TODO:(${dev.instance})`; } } - if (upcoming.features && current.features) { - console.log("CZEKIN PASS PROT"); - if (upcoming.features.passphrase_protection !== current.features.passphrase_protection) { - // device settings has been changed, reset state - dev.state = null; - console.log("RESTETTTT STATE!"); - } - } + return dev; } @@ -132,7 +147,40 @@ const addDevice = (state: State, device: Object): State => { if (affectedDevices.length > 0 ) { // replace existing values - const changedDevices: Array = affectedDevices.map(d => mergeDevices(d, { ...device, connected: true} )); + // const changedDevices: Array = affectedDevices.map(d => mergeDevices(d, { ...device, connected: true} )); + let cloneInstance: number = 1; + const changedDevices: Array = affectedDevices.map(d => { + if (d.features.passphrase_protection === device.features.passphrase_protection) { + cloneInstance = 0; + return mergeDevices(d, { ...device, connected: true, available: true } ); + } else { + if (d.instance && cloneInstance > 0) { + cloneInstance = d.instance + 1; + } + return mergeDevices(d, { ...d, connected: true, available: false } ); + // return d; + } + }); + + if (cloneInstance > 0) { + // TODO: instance should be calculated form affectedDevice + const instance = cloneInstance; //new Date().getTime(); + + const newDevice: TrezorDevice = { + ...device, + acquiring: false, + remember: false, + connected: true, + available: true, + path: device.path, + label: device.label, + state: null, + instance, + instanceLabel: `${device.label} (${instance})`, + ts: new Date().getTime(), + } + changedDevices.push(newDevice); + } newState.devices = otherDevices.concat(changedDevices); } else { @@ -142,12 +190,13 @@ const addDevice = (state: State, device: Object): State => { acquiring: false, remember: false, connected: true, + available: true, path: device.path, label: device.label, state: null, // instance: 0, instanceLabel: device.label, - ts: 0, + ts: new Date().getTime(), } newState.devices.push(newDevice); @@ -184,11 +233,15 @@ const changeDevice = (state: State, device: Object): State => { let affectedDevices: Array = []; let otherDevices: Array = []; if (device.features) { - affectedDevices = state.devices.filter(d => (d.features && d.features.device_id === device.features.device_id) || (d.path.length > 0 && d.path === device.path) ); + affectedDevices = state.devices.filter(d => + (d.features && d.features.device_id === device.features.device_id && d.features.passphrase_protection === device.features.passphrase_protection) || + (d.path.length > 0 && d.path === device.path) + ); otherDevices = state.devices.filter(d => affectedDevices.indexOf(d) === -1); } else { - affectedDevices = state.devices.filter(d => d.path === device.path); - otherDevices = state.devices.filter(d => d.path !== device.path); + affectedDevices = state.devices.filter(d => !d.features && d.path === device.path); + otherDevices = state.devices.filter(d => affectedDevices.indexOf(d) === -1); + // otherDevices = state.devices.filter(d => d.path !== device.path); } if (affectedDevices.length > 0) { @@ -226,13 +279,14 @@ const changeDevice = (state: State, device: Object): State => { const disconnectDevice = (state: State, device: Object): State => { const newState: State = { ...state }; - const affectedDevices: Array = state.devices.filter(d => d.path === device.path); + const affectedDevices: Array = state.devices.filter(d => d.path === device.path || (d.features && device.features && d.features.device_id === device.features.device_id)); const otherDevices: Array = state.devices.filter(d => affectedDevices.indexOf(d) === -1); if (affectedDevices.length > 0) { const acquiredDevices = affectedDevices.filter(d => !d.unacquired && d.state); newState.devices = otherDevices.concat( acquiredDevices.map(d => { d.connected = false; + d.available = false; d.isUsedElsewhere = false; d.featuresNeedsReload = false; d.acquiring = false; @@ -261,7 +315,7 @@ const forgetDevice = (state: State, action: any): State => { } else { // remove all instances after disconnect (remember request declined) //newState.devices = state.devices.filter(d => d.path !== action.device.path); - newState.devices = state.devices.filter(d => (d.features && d.features.device_id !== action.device.features.device_id) || (!d.features && d.path !== action.device.path)); + newState.devices = state.devices.filter(d => d.remember || (d.features && d.features.device_id !== action.device.features.device_id) || (!d.features && d.path !== action.device.path)); } return newState; @@ -272,6 +326,7 @@ const devicesFromLocalStorage = (devices: Array): Array => { return { ...d, connected: false, + available: false, path: '', acquiring: false, featuresNeedsReload: false, @@ -282,24 +337,33 @@ const devicesFromLocalStorage = (devices: Array): Array => { const duplicate = (state: State, device: any): State => { const newState: State = { ...state }; - const affectedDevices: Array = state.devices.filter(d => d.path === device.path); + const affectedDevices: Array = state.devices.filter(d => d.features.device_id === device.features.device_id); + const instance = affectedDevices.reduce((inst, dev) => { + console.warn("REDUC", inst, dev); + return dev.instance ? dev.instance + 1 : inst; + }, 1); + + console.warn("NEEEW INST", instance); // if (affectedDevices.length > 0) { + const newDevice: TrezorDevice = { ...device, - state: null, - remember: device.remember, + acquiring: false, + remember: false, connected: device.connected, + available: device.available, path: device.path, label: device.label, - instance: new Date().getTime(), - instanceLabel: device.instanceLabel, - ts: 0, + state: null, + instance, + instanceLabel: `${device.label} (${instance})`, + ts: new Date().getTime(), } newState.devices.push(newDevice); newState.selectedDevice = { id: newDevice.features.device_id, - instance: newDevice.instance + instance } //} @@ -307,6 +371,19 @@ const duplicate = (state: State, device: any): State => { } +const onSelectDevice = (state: State, action: any): State => { + const newState: State = { ...state }; + newState.selectedDevice = action.payload; + + const selected = findSelectedDevice(newState); + if (selected) { + selected.ts = new Date().getTime(); + console.warn("APDEJT SELECTED!", selected.instanceLabel, selected.ts) + } + + return newState; +} + export default function connect(state: State = initialState, action: any): any { @@ -323,10 +400,11 @@ export default function connect(state: State = initialState, action: any): any { case CONNECT.SELECT_DEVICE : - return { - ...state, - selectedDevice: action.payload - } + return onSelectDevice(state, action); + // return { + // ...state, + // selectedDevice: action.payload + // } case CONNECT.INITIALIZATION_ERROR : return { @@ -378,7 +456,7 @@ export default function connect(state: State = initialState, action: any): any { return disconnectDevice(state, action.device); case DEVICE.CHANGED : - return changeDevice(state, { ...action.device, connected: true }); + return changeDevice(state, { ...action.device, connected: true, available: true }); default: return state; diff --git a/src/js/reducers/WalletReducer.js b/src/js/reducers/WalletReducer.js index d1a21381..b06885d4 100644 --- a/src/js/reducers/WalletReducer.js +++ b/src/js/reducers/WalletReducer.js @@ -1,26 +1,44 @@ /* @flow */ 'use strict'; -import { ON_RESIZE, TOGGLE_DEVICE_DROPDOWN, RESIZE_CONTAINER } from '../actions/AppActions'; import * as WEB3 from '../actions/constants/Web3'; - -const WIDTH: number = 1080; -const HEIGHT: number = 1920; +import * as WALLET from '../actions/constants/wallet'; type State = { - network: string; - device: string; - + ready: boolean; + dropdownOpened: boolean; + initialUrl: boolean; } const initialState: Object = { - + ready: false, + dropdownOpened: false, + initialParams: null, + initialPathname: null, }; export default function wallet(state: Object = initialState, action: Object): any { switch(action.type) { - + case WALLET.SET_INITIAL_URL : + return { + ...state, + initialParams: action.params, + initialPathname: action.pathname + } + + case WEB3.READY : + return { + ...state, + ready: true + } + + case WALLET.TOGGLE_DEVICE_DROPDOWN : + return { + ...state, + dropdownOpened: action.opened + } + default: return state; } diff --git a/src/js/reducers/index.js b/src/js/reducers/index.js index cd2d494a..0722e788 100644 --- a/src/js/reducers/index.js +++ b/src/js/reducers/index.js @@ -4,7 +4,6 @@ import { combineReducers } from 'redux'; import { routerReducer } from 'react-router-redux'; -import DOM from './AppReducer.js'; import log from './LogReducer.js'; import localStorage from './LocalStorageReducer.js'; import connect from './TrezorConnectReducer.js'; @@ -12,7 +11,7 @@ import notifications from './NotificationReducer.js'; import modal from './ModalReducer.js'; import web3 from './Web3Reducer.js'; import accounts from './AccountsReducer.js'; -import accountDetail from './AccountDetailReducer.js'; +import abstractAccount from './AbstractAccountReducer.js'; import sendForm from './SendFormReducer.js'; import receive from './ReceiveReducer.js'; import summary from './SummaryReducer.js'; @@ -20,10 +19,10 @@ import tokens from './TokensReducer.js'; import discovery from './DiscoveryReducer.js'; import pending from './PendingTxReducer.js'; import fiat from './FiatRateReducer.js'; +import wallet from './WalletReducer.js'; export default combineReducers({ router: routerReducer, - DOM, log, localStorage, connect, @@ -31,12 +30,13 @@ export default combineReducers({ modal, web3, accounts, - accountDetail, + abstractAccount, sendForm, receive, summary, tokens, discovery, pending, - fiat + fiat, + wallet }); \ No newline at end of file diff --git a/src/js/router/index.js b/src/js/router/index.js index fe38e285..750a3fa9 100644 --- a/src/js/router/index.js +++ b/src/js/router/index.js @@ -7,23 +7,18 @@ import { Provider } from 'react-redux'; import { ConnectedRouter } from 'react-router-redux'; import store, { history } from '../store'; -import { - LandingPageContainer, - WalletContainer, +import LandingPageContainer from '../components/landing'; +import WalletContainer from '../components/wallet'; +import BootloaderContainer from '../components/wallet/Bootloader'; +import AcquireContainer from '../components/wallet/Acquire'; - AcquireContainer, - BootloaderContainer, - - DashboardContainer, - - HistoryContainer, - SendFormContainer, - ReceiveContainer, - SignVerifyContainer, - SettingsContainer, -} from '../containers'; - -import SummaryContainer from '../components/wallet/summary/SummaryContainer'; +import DashboardContainer from '../components/wallet/Dashboard'; +import SummaryContainer from '../components/wallet/summary'; +import SendFormContainer from '../components/wallet/send'; +import ReceiveContainer from '../components/wallet/Receive'; +import SignVerifyContainer from '../components/wallet/SignVerify'; +import DeviceSettingsContainer from '../components/wallet/DeviceSettings'; +import WalletSettingsContainer from '../components/wallet/WalletSettings'; export default ( @@ -34,20 +29,19 @@ export default ( + + - {/* */} - - diff --git a/src/js/services/EtherscanService.js b/src/js/services/EtherscanService.js deleted file mode 100644 index ae6d2862..00000000 --- a/src/js/services/EtherscanService.js +++ /dev/null @@ -1,174 +0,0 @@ -/* @flo */ -'use strict'; - -//http://ropsten.etherscan.io/api?module=account&action=txlist&address=0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad&startblock=0&endblock=99999999&sort=asc&apikey=89IZG471H8ITVXY377I2QWJIT2D62DGG9Z - -import { LOCATION_CHANGE, push } from 'react-router-redux'; -import { httpRequest } from '../utils/networkUtils'; -import * as ACTIONS from '../actions/index'; -import { getBalance } from '../actions/Web3Actions'; - -const API_KEY: string = '89IZG471H8ITVXY377I2QWJIT2D62DGG9Z'; -//const API: string = `http://ropsten.etherscan.io/api?module=account&action=txlist&startblock=0&endblock=99999999&sort=asc&apikey=89IZG471H8ITVXY377I2QWJIT2D62DGG9Z`; -const API: string = `http://ropsten.etherscan.io/api?module=account&action=txlist&startblock=0&endblock=99999999&sort=desc&apikey=${API_KEY}`; - -export const getTransactionHistory = async (address: string): Promise> => { - const json = await httpRequest(`${API}&address=${address}`); - return json; -} - -const getTransactionStatus = async (txid: string): Promise> => { - //https://ropsten.etherscan.io/api?module=proxy&action=eth_getTransactionByHash&txhash=0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1&apikey=YourApiKeyToken - const url: string = `https://ropsten.etherscan.io/api?module=proxy&action=eth_getTransactionByHash&apikey=${API_KEY}` - //const url: string = `https://ropsten.etherscan.io/api?module=transaction&action=getstatus&apikey=${API_KEY}` - const json = await httpRequest(`${url}&txhash=${txid}`); - return json; -} - -const getTokenHistory = async (tokenAddress, address) => { - - // 0x58cda554935e4a1f2acbe15f8757400af275e084 - // 0x000000000000000000000000 + 98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad - let url: string = 'https://ropsten.etherscan.io/api?module=logs&action=getLogs'; - url += '&fromBlock=0&toBlock=latest'; - url += `&address=${tokenAddress}`; - url += '&topic0=0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; - url += `&topic2=${ address }`; - // https://api.etherscan.io/api?module=logs&action=getLogs - // &fromBlock=0 - // &toBlock=latest - // &address=[Token Contract Address] - // &topic0=0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef - // &topic1=[From Address, padded to 32 bytes - optional] - // &topic2=[To Address, padded to 32 bytes - optional] - - console.log("TOKENURL", url); - const json = await httpRequest(url); - return json; -} - -export const loadTokenHistory = (address): Promise => { - // https://ropsten.etherscan.io/apis#logs - return async (dispatch, getState): Promise => { - const tkn = '0x58cda554935e4a1f2acbe15f8757400af275e084'; - const ad = '0x00000000000000000000000098ead4bd2fbbb0cf0b49459aa0510ef53faa6cad'; - const incoming = await getTokenHistory(tkn, ad); - - console.log("TOKEN HIST!", JSON.parse(incoming) ); - } -} - -export const loadTransactionStatus = (txid): Promise => { - return async (dispatch, getState): Promise => { - const json = await getTransactionStatus(txid); - const status = JSON.parse(json); - - console.error("TXSTAT", status) - - if (status.result.blockHash) { - // find address with pending tx - const { addresses } = getState().addresses; - for (let addr of addresses) { - if (addr.findPendingTx(txid)) { - dispatch( getBalance(addr) ); - - const txType = status.result.from === addr.address ? 'out' : 'in'; - const txAddress = txType === 'out' ? status.result.to : status.result.from; - - dispatch({ - type: ACTIONS.ADDRESS_ADD_TO_HISTORY, - address: addr, - entry: { - txid: status.result.hash, - type: txType, - timestamp: '0', - address: txAddress, - value: status.result.value - } - }); - - //dispatch( loadHistory(addr, 3000) ); - } - } - - dispatch({ - type: ACTIONS.TX_STATUS_OK, - txid - }); - - - } - - // if (status.status === "1" && status.message === "OK") { - // if (status.result.isError === "0") { - // dispatch({ - // type: ACTIONS.TX_STATUS_OK, - // txid - // }); - // } else { - // dispatch({ - // type: ACTIONS.TX_STATUS_ERROR, - // txid, - // error: status.errDescription - // }); - // } - // } else { - // dispatch({ - // type: ACTIONS.TX_STATUS_UNKNOWN, - // txid, - // status - // }); - // } - } -} - -export const loadHistory = (address, delay): void => { - return async (dispatch, getState) => { - - // const json = await getTransactionStatus('0x2113e578497f3486944566e2417b5ac3b31d7e76f71557ae0626e2a6fe191e58'); - // console.log("JSON!", json) - - /* - if (delay) { - console.warn("-----PRELOAD with delay", address) - await new Promise(resolve => { - setTimeout(resolve, delay); - }) - } - - const history = await getTransactionHistory(address.address); - dispatch({ - type: ACTIONS.ADDRESS_SET_HISTORY, - address, - history - }); - */ - } -} - -const EtherscanService = store => next => action => { - - next(action); - - if (action.type === LOCATION_CHANGE) { - - const { location } = store.getState().router; - const { addresses } = store.getState().addresses; - - if (location) { - const parts = location.pathname.split("/"); - if (parts.length === 3 && parts[1] === "address") { - const addressId = parseInt(parts[2]); - - if (!isNaN(addressId) && addresses[addressId]) { - //store.dispatch( loadHistory( addresses[addressId] ) ); - //store.dispatch( loadTokenHistory( addresses[addressId] ) ); - } - - //console.error("ETH", parts, "id", parts.length, parts.length === 3 && parts[1] === "address"); - } - } - } -}; - -export default EtherscanService; diff --git a/src/js/services/LocalStorageService.js b/src/js/services/LocalStorageService.js index 554c0e8d..66c3a280 100644 --- a/src/js/services/LocalStorageService.js +++ b/src/js/services/LocalStorageService.js @@ -111,6 +111,7 @@ const LocalStorageService = (store: any) => (next: any) => (action: any) => { case DEVICE.CHANGED : case DEVICE.DISCONNECT : case CONNECT.AUTH_DEVICE : + case CONNECT.SELECT_DEVICE : save(store.dispatch, store.getState); //store.dispatch( LocalStorageActions.save('devices', JSON.stringify( store.getState().connect.devices.filter(d => d.remember === true && !d.unacquired) ) ) ); // store.dispatch( LocalStorageActions.save('selectedDevice', JSON.stringify( store.getState().connect.selectedDevice ) ) ); diff --git a/src/js/services/RouterService.js b/src/js/services/RouterService.js index 7786a582..7de3fefd 100644 --- a/src/js/services/RouterService.js +++ b/src/js/services/RouterService.js @@ -4,8 +4,9 @@ import pathToRegexp from 'path-to-regexp'; import { DEVICE } from 'trezor-connect'; import { LOCATION_CHANGE, push, replace } from 'react-router-redux'; -import { ON_BEFORE_UNLOAD } from '../actions/AppActions'; import * as CONNECT from '../actions/constants/TrezorConnect'; +import * as WALLET from '../actions/constants/wallet'; +import * as NotificationActions from '../actions/NotificationActions'; /** * Middleware used for init application and managing router path. @@ -66,25 +67,25 @@ let __unloading: boolean = false; const RouterService = (store: any) => (next: any) => (action: any) => { - if (action.type === ON_BEFORE_UNLOAD) { + if (action.type === WALLET.ON_BEFORE_UNLOAD) { __unloading = true; } else if (action.type === LOCATION_CHANGE && !__unloading) { const { location } = store.getState().router; const web3 = store.getState().web3; const { devices, error } = store.getState().connect; - const { opened } = store.getState().modal; + const isModalOpened: boolean = store.getState().modal.opened; let redirectPath: ?string; - // first (initial) event after app loads + // first event after application loads if (!location) { - action.payload.state = { - initURL: action.payload.pathname, - initSearch: action.payload.search - } - - // check if there are initial parameters in url (network/ device / account) + store.dispatch({ + type: WALLET.SET_INITIAL_URL, + pathname: action.payload.pathname, + params: pathToParams(action.payload.pathname) + }); + if (action.payload.search.length > 0) { // save it in WalletReducer, after device detection will redirect to this request redirectPath = '/'; @@ -98,7 +99,7 @@ const RouterService = (store: any) => (next: any) => (action: any) => { // if web3 wasn't initialized yet or there are no devices attached or initialization error occurs const landingPage: boolean = web3.length < 1 || devices.length < 1 || error; - if (opened && action.payload.pathname !== location.pathname) { + if (isModalOpened && action.payload.pathname !== location.pathname) { redirectPath = location.pathname; console.warn("Modal still opened"); } else if (landingPage) { @@ -113,7 +114,7 @@ const RouterService = (store: any) => (next: any) => (action: any) => { // TODO: switch to first device? // redirectPath = `/device/${ devices[0].path }`; redirectPath = location.pathname; - } else { + } else if (requestedParams.device) { if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { store.dispatch({ @@ -146,7 +147,8 @@ const RouterService = (store: any) => (next: any) => (action: any) => { } else { action.payload.params = requestedParams; } - + + store.dispatch( NotificationActions.clear(currentParams, requestedParams) ); } // Pass all actions through by default diff --git a/src/js/services/TrezorConnectService.js b/src/js/services/TrezorConnectService.js index 9fe3a12a..f240e768 100644 --- a/src/js/services/TrezorConnectService.js +++ b/src/js/services/TrezorConnectService.js @@ -11,13 +11,14 @@ import * as WEB3 from '../actions/constants/Web3'; import * as STORAGE from '../actions/constants/LocalStorage'; import * as CONNECT from '../actions/constants/TrezorConnect'; import * as NOTIFICATION from '../actions/constants/notification'; -import * as ACTIONS from '../actions'; +import * as MODAL from '../actions/constants/Modal'; const TrezorConnectService = (store: any) => (next: any) => (action: any) => { const prevState = store.getState().connect; const prevModalState = store.getState().modal; + const prevRouterState = store.getState().router; next(action); @@ -25,8 +26,14 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => { store.dispatch( TrezorConnectActions.init() ); } else if (action.type === TRANSPORT.ERROR) { - store.dispatch( push('/') ); - } else if (action.type === TRANSPORT.START && store.getState().web3.length > 0 && prevState.devices.length > 0) { + // TODO: check if modal is open + // store.dispatch( push('/') ); + // } else if (action.type === TRANSPORT.START && store.getState().web3.length > 0 && prevState.devices.length > 0) { + // store.dispatch( TrezorConnectActions.postInit() ); + } else if (action.type === TRANSPORT.START && store.getState().web3.length < 1) { + //store.dispatch( TrezorConnectActions.postInit() ); + store.dispatch( initWeb3() ); + } else if (action.type === WEB3.READY) { store.dispatch( TrezorConnectActions.postInit() ); } else if (action.type === TRANSPORT.UNREADABLE) { store.dispatch({ @@ -34,22 +41,16 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => { payload: { type: 'error', title: 'Unreadable HID device', - message: 'What to do?', + message: '', cancelable: true, } - }) - - } else if (action.type === WEB3.READY) { - store.dispatch( TrezorConnectActions.postInit() ); - + }); } else if (action.type === DEVICE.DISCONNECT) { store.dispatch( TrezorConnectActions.deviceDisconnect(action.device) ); } else if (action.type === CONNECT.REMEMBER_REQUEST) { - // TODO: + // TODO: 2 modals at once if (prevModalState.opened && prevModalState.windowType === CONNECT.REMEMBER_REQUEST) { - - store.dispatch({ type: CONNECT.FORGET, device: store.getState().modal.device @@ -82,6 +83,7 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => { // we need to change route if (prevState.selectedDevice) { if (!action.device.unacquired && action.device.path === prevState.selectedDevice.id) { + console.warn("TODO: here! better") store.dispatch( TrezorConnectActions.onSelectDevice(action.device) ); } } @@ -96,7 +98,7 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => { if (modal.opened && modal.windowType === CONNECT.REMEMBER_REQUEST) { if (action.device.features && modal.device.features.device_id === action.device.features.device_id) { store.dispatch({ - type: ACTIONS.CLOSE_MODAL, + type: MODAL.CLOSE, }); } else { store.dispatch({ @@ -113,6 +115,7 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => { store.dispatch( TrezorConnectActions.onDuplicateDevice() ); } else if (action.type === DEVICE.ACQUIRED || action.type === CONNECT.SELECT_DEVICE) { + console.warn("here!!!!!", action.type) store.dispatch( TrezorConnectActions.getSelectedDeviceState() ); } else if (action.type === CONNECT.COIN_CHANGED) { diff --git a/src/js/services/Web3Service.js b/src/js/services/Web3Service.js deleted file mode 100644 index c85fe007..00000000 --- a/src/js/services/Web3Service.js +++ /dev/null @@ -1,238 +0,0 @@ -/* @flow */ -'use strict'; - -import Web3 from 'web3'; -import { LOCATION_CHANGE } from 'react-router-redux'; -import * as ACTIONS from '../actions/index'; -import { getBalance, getGasPrice, getTransactionReceipt } from '../actions/Web3Actions'; -import { loadTransactionStatus } from './EtherscanService'; -import BigNumber from 'bignumber.js'; - -let web3: Web3; - -// export const getGasPrice = (): Promise => { -// return (dispatch, getState) => { -// web3.eth.getGasPrice((error, gasPrice) => { -// if (!error) { -// dispatch({ -// type: 'update_gas', -// gasPrice: web3.fromWei(gasPrice.toString(), 'gwei') -// }) -// } -// }); -// } -// } - - - -const Web3Service = store => next => action => { - - next(action); - - switch (action.type) { - - case 'WEB_2_START' : - if (web3) break; - - //web3 = new Web3(window.web3.currentProvider); - //web3 = new Web3(new Web3.providers.HttpProvider("https://api.myetherapi.com/rop")); - web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io2/QGyVKozSUEh2YhL4s2G4")); - //web3 = new Web3( new Web3.providers.HttpProvider("ws://34.230.234.51:30303") ); - - - - - /*store.dispatch( getGasPrice() ); - - - const latestBlockFilter = web3.eth.filter('latest'); - latestBlockFilter.watch((error, blockHash) => { - - const { addresses, pendingTxs } = store.getState().addresses; - - for (const addr of addresses) { - store.dispatch( getBalance(addr) ); - } - - store.dispatch( getGasPrice() ); - - if (pendingTxs.length > 0) { - for (const tx of pendingTxs) { - store.dispatch( getTransactionReceipt(tx) ); - } - } - });*/ - - - - // store.dispatch({ - // type: 'web3__init', - // web3 - // }); - - // store.dispatch({ - // type: 'update_gas', - // gasPrice: web3.fromWei(web3.eth.gasPrice, 'gwei') - // }) - - /* - { - "dd62ed3e": "allowance(address,address)", - "095ea7b3": "approve(address,uint256)", - "cae9ca51": "approveAndCall(address,uint256,bytes)", - "70a08231": "balanceOf(address)", - "313ce567": "decimals()", - "06fdde03": "name()", - "95d89b41": "symbol()", - "18160ddd": "totalSupply()", - "a9059cbb": "transfer(address,uint256)", - "23b872dd": "transferFrom(address,address,uint256)", - "54fd4d50": "version()" - }*/ - - // var balanceHex = "06fdde03"; // I believe this is the hex for balance - // var contractAddress = "0x58cda554935e4a1f2acbe15f8757400af275e084"; - // var userAddress = "0x5DBB9793537515398A1176d365b636A5321D9e39"; - // var balanceCall = getDataObj(contractAddress, balanceHex); - // var balance = web3.eth.call(balanceCall); - - - - - const abiArray = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]; - //const contr = web3.eth.contract(abiArray, '0x58cda554935e4a1f2acbe15f8757400af275e084'); - const contr = web3.eth.contract(abiArray).at('0x58cda554935e4a1f2acbe15f8757400af275e084'); - console.log("contr", contr ); - - contr.name.call((e,r) => { - console.log("nameeeee", e, r) - }) - - contr.symbol.call((e,r) => { - console.log("symboll", e, r) - }) - - //console.log( const.name ) - - // contr.balanceOf('0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad', (e, r) => { - // console.warn('contrR', e, r.toString(10)); - // }); - - let cntrData = contr.transfer.getData("0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad", 1, { - from: "0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad", - gasLimit: 36158, - gasPrice: "0x0ee6b28000" - }) - - console.log("contr", cntrData); - // const data = contr.transferFrom( - // '0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad', - // '0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad', - // 1 - // ); - - // const data = contr.transferFrom( - // '0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad', - // { - // from: '0x7314e0f1c0e28474bdb6be3e2c3e0453255188f8', - // value: 1 - // } - // ); - - // const data = contr.transferFrom( - // '0x00000000000000000000000098ead4bd2fbbb0cf0b49459aa0510ef53faa6cad', - // '0x000000000000000000000000a738ea40b69d87f4f9ac94c9a0763f96248df23b', - // 2 - // ); - //console.log("contr", contr, data) - - // var addr1 = '0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad'; - // var contractAddr = '0x58cda554935e4a1f2acbe15f8757400af275e084'; - // var tknAddress = (addr1).substring(2); - // var contractData = ('0x70a08231000000000000000000000000' + tknAddress); - - // console.warn("ADDDDDDDD", web3.toHex('0x98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad')); - // console.warn("ADDDDDDDD", web3.toHex('98ead4bd2fbbb0cf0b49459aa0510ef53faa6cad')); - - // web3.eth.call({ - // to: contractAddr, - // data: contractData - // }, function(err, result) { - // if (result) { - // console.log("---------result", result, web3); - // //var tokens = web3.toBN(result).toString(); - // //console.log('Tokens Owned: ' + web3.utils.fromWei(tokens, 'ether')); - // } - // else { - // console.log(err); // Dump errors here - // } - // }); - - - - /* - const pendingBlockFilter = web3.eth.filter('pending'); - pendingBlockFilter.watch((error, txid) => { - //console.warn("Watch pending block", txid, error) - if (!error) { - - if (pendingTx.length > 0) { - console.error("Watch pending block", pendingTx.indexOf(txid)) - } - - web3.eth.getTransactionReceipt(txid, async (error, tx) => { - if (!error && typeof tx === 'object') { - const { addresses } = store.getState().addresses; - const receiver = addresses.find(a => a.address === tx.to); - if (receiver) { - console.error("PendingTX RECV", txid, tx, receiver) - const balance = await getBalance(receiver.address); - store.dispatch({ - type: ACTIONS.ADDRESS_SET_BALANCE, - txid, - address: receiver, - balance: web3.fromWei(balance.toString(), 'ether') - }) - } - const sender = addresses.find(a => a.address === tx.from); - if (sender) { - const balance = await getBalance(sender.address); - store.dispatch({ - type: ACTIONS.ADDRESS_SET_BALANCE, - txid, - address: sender, - balance: web3.fromWei(balance.toString(), 'ether') - }) - } - - //console.log("PendingTX", txid, tx, receiver, sender) - } - }) - } - }); - */ - - //"web3": "^0.19.0" - - - break; - - } - - -}; - -export default Web3Service; - - -export const estimateGas = (gasOptions): Promise => { - return new Promise((resolve, reject) => { - web3.eth.estimateGas(gasOptions, (error, result) => { - if (error) { - reject(error); - } else { - resolve(result); - } - }); - }) -} \ No newline at end of file diff --git a/src/js/services/index.js b/src/js/services/index.js index 4e68a0b4..1aa2b6b9 100644 --- a/src/js/services/index.js +++ b/src/js/services/index.js @@ -5,14 +5,10 @@ import RouterService from './RouterService'; import LocalStorageService from './LocalStorageService'; import CoinmarketcapService from './CoinmarketcapService'; import TrezorConnectService from './TrezorConnectService'; -import Web3Service from './Web3Service'; -import EtherscanService from './EtherscanService'; export default [ RouterService, LocalStorageService, TrezorConnectService, - Web3Service, CoinmarketcapService, - //EtherscanService, ]; \ No newline at end of file diff --git a/src/js/utils/reducerUtils.js b/src/js/utils/reducerUtils.js index cc860a13..7a3fbdd6 100644 --- a/src/js/utils/reducerUtils.js +++ b/src/js/utils/reducerUtils.js @@ -9,4 +9,9 @@ export const getAccounts = (accounts: Array, device: any, network: ?string) return accounts.filter((addr) => addr.deviceState === device.state); } +} + +// Public method used in components to find device by state and device_id +export const findDevice = (devices: Array, state: ?string, deviceId: ?string, instance: ?string): ?TrezorDevice => { + return devices.find(d => d.state === state && d.features && d.features.device_id === deviceId && d.instance === instance); } \ No newline at end of file diff --git a/src/styles/aside.less b/src/styles/aside.less index 9d09c7ff..ba949451 100644 --- a/src/styles/aside.less +++ b/src/styles/aside.less @@ -95,6 +95,8 @@ aside { margin: 12px 0px 12px 80px; } + + .device-select { background: @color_white; border-bottom: 1px solid @color_divider; @@ -246,6 +248,56 @@ aside { } + .device-menu { + padding: 8px 0px; + // padding-left: 80px; + padding-left: 74px; + border-bottom: 1px solid @color_divider; + + div { + display: flex; + align-items: center; + line-height: 24px; + cursor: pointer; + color: @color_text_secondary; + .hover(); + + &:before { + .icomoon-refresh; + // color: @color_text_secondary; + position: relative; + font-size: 24px; + margin-right: 12px; + .hover(); + } + + &:hover { + color: @color_text_primary; + // &:before { + // color: @color_text_primary; + // } + } + + &.forget { + &:before { + .icomoon-eject; + } + } + + &.clone { + &:before { + .icomoon-settings; + } + } + + &.settings { + &:before { + .icomoon-settings; + } + } + } + } + a { position: relative; display: block; @@ -391,12 +443,12 @@ aside { } &.ropsten:before, - &.eth:before { + &.ethereum:before { background-image: url('../images/eth-logo.png'); background-size: auto 20px; } &.rinkeby:before, - &.etc:before { + &.ethereum-classic:before { background-image: url('../images/etc-logo.png'); background-size: auto 20px; } diff --git a/src/styles/fonts.less b/src/styles/fonts.less index 773e64e6..b929b9bb 100644 --- a/src/styles/fonts.less +++ b/src/styles/fonts.less @@ -169,5 +169,6 @@ .icomoon-download { .icomoon-base(); - content: "\e91f"; + content: "\e91b"; + transform: rotate(180deg) } diff --git a/src/styles/landingPage.less b/src/styles/landingPage.less index cb4ed7b6..69614a38 100644 --- a/src/styles/landingPage.less +++ b/src/styles/landingPage.less @@ -180,7 +180,7 @@ justify-content: space-between; padding: 36px 0px; margin: 0 auto; - width: 420px; + // width: 420px; p { // flex: 1; @@ -197,6 +197,18 @@ // color: @color_green_secondary; // } // } + .webusb-and, + .trezor-webusb-button { + display: none; + } + + &.webusb { + width: 400px; + .webusb-and, + .trezor-webusb-button { + display: block; + } + } } p { diff --git a/src/styles/modal.less b/src/styles/modal.less index 32d774e5..b2aaed49 100644 --- a/src/styles/modal.less +++ b/src/styles/modal.less @@ -108,6 +108,7 @@ } } + .duplicate, .remember { width: 360px; padding: 24px 48px; @@ -137,6 +138,7 @@ } } } + .close-modal { position: absolute; diff --git a/src/styles/send.less b/src/styles/send.less index 4d7c37d7..954b1b13 100644 --- a/src/styles/send.less +++ b/src/styles/send.less @@ -190,20 +190,23 @@ .advanced-container { display: flex; + flex-wrap: wrap; justify-content: space-between; padding: 0px 48px; padding-bottom: 24px; button { - width: 50%; + min-width: 50%; + white-space: nowrap; } &.opened { flex-direction: column; padding: 0px; button { position: relative; - left: 50%; - width: 50%; + // left: 50%; + // width: 50%; + float: right; // TODO: better } .advanced { display: inline-block; @@ -231,6 +234,12 @@ } } } + + // @media screen and (max-width: 900px) { + // :not(.opened) { + // border: 1px solid red; + // } + // } } diff --git a/src/styles/summary.less b/src/styles/summary.less index 04cfee79..26ac2664 100644 --- a/src/styles/summary.less +++ b/src/styles/summary.less @@ -13,12 +13,12 @@ } &.ropsten:before, - &.eth:before { + &.ethereum:before { background-image: url('../images/eth-logo.png'); background-size: auto 20px; } &.rinkeby:before, - &.etc:before { + &.ethereum-classic:before { background-image: url('../images/etc-logo.png'); background-size: auto 20px; }