device clone, device connect priority + BIG refactoring for

components/containers
pull/2/merge
Szymon Lesisz 6 years ago
parent 1e316ea9d2
commit 60ead5a452

@ -46,7 +46,7 @@
{ {
"name": "Ethereum Classic", "name": "Ethereum Classic",
"symbol": "ETC", "symbol": "ETC",
"network": "ethereumclassic", "network": "ethereum-classic",
"bip44": "m/44'/61'/0'/0", "bip44": "m/44'/61'/0'/0",
"defaultGasPrice": 64, "defaultGasPrice": 64,
"defaultGasLimit": 21000, "defaultGasLimit": 21000,

@ -41,9 +41,6 @@ export function loadTokensFromJSON(): any {
return collection; return collection;
}, Promise.resolve({})); }, Promise.resolve({}));
console.log("JADE DAL")
const devices: ?string = get('devices'); const devices: ?string = get('devices');
if (devices) { if (devices) {
dispatch({ dispatch({

@ -2,7 +2,6 @@
'use strict'; 'use strict';
import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect'; import TrezorConnect, { UI, UI_EVENT } from 'trezor-connect';
import * as ACTIONS from './index';
import * as MODAL from './constants/Modal'; import * as MODAL from './constants/Modal';
import * as CONNECT from './constants/TrezorConnect'; import * as CONNECT from './constants/TrezorConnect';
@ -10,7 +9,7 @@ import * as CONNECT from './constants/TrezorConnect';
export function onPinSubmit(value: string): any { export function onPinSubmit(value: string): any {
TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value }); TrezorConnect.uiResponse({ type: UI.RECEIVE_PIN, payload: value });
return { return {
type: ACTIONS.CLOSE_MODAL type: MODAL.CLOSE
} }
} }
@ -25,7 +24,7 @@ export function onPassphraseSubmit(passphrase: string): any {
}); });
dispatch({ dispatch({
type: ACTIONS.CLOSE_MODAL type: MODAL.CLOSE
}); });
} }
} }
@ -60,7 +59,7 @@ export const onForgetSingleDevice = (device: any) => {
export const onCancel = () => { export const onCancel = () => {
return { return {
type: ACTIONS.CLOSE_MODAL type: MODAL.CLOSE
} }
} }

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

@ -21,6 +21,8 @@ export const init = (): any => {
const state: State = { const state: State = {
...initialState, ...initialState,
deviceState: selected.state, deviceState: selected.state,
deviceId: selected.features.device_id,
deviceInstance: selected.instance,
accountIndex: parseInt(urlParams.address), accountIndex: parseInt(urlParams.address),
network: urlParams.network, network: urlParams.network,
location: location.pathname, location: location.pathname,
@ -67,9 +69,10 @@ export const showAddress = (address_n: string): any => {
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);
if (!selected) return; if (!selected) return;
if (selected && !selected.connected) { if (selected && (!selected.connected || !selected.available)) {
dispatch({ dispatch({
type: RECEIVE.REQUEST_UNVERIFIED, type: RECEIVE.REQUEST_UNVERIFIED,
device: selected
}); });
return; return;
} }

@ -117,6 +117,8 @@ export const init = (): any => {
const state: State = { const state: State = {
...initialState, ...initialState,
deviceState: selected.state, deviceState: selected.state,
deviceId: selected.features.device_id,
deviceInstance: selected.instance,
accountIndex: parseInt(urlParams.address), accountIndex: parseInt(urlParams.address),
network: urlParams.network, network: urlParams.network,
coinSymbol: coin.symbol, coinSymbol: coin.symbol,

@ -2,7 +2,6 @@
'use strict'; 'use strict';
import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUtil from 'ethereumjs-util';
import * as ACTIONS from './index';
import * as SUMMARY from './constants/summary'; import * as SUMMARY from './constants/summary';
import * as TOKEN from './constants/Token'; import * as TOKEN from './constants/Token';
import * as ADDRESS from './constants/Address'; import * as ADDRESS from './constants/Address';
@ -20,11 +19,13 @@ export const init = (): any => {
const urlParams = location.params; const urlParams = location.params;
const selected = findSelectedDevice( getState().connect ); const selected = findSelectedDevice( getState().connect );
if (!selected) return; if (!selected || !selected.state) return;
const state: State = { const state: State = {
...initialState, ...initialState,
deviceState: selected.state, deviceState: selected.state,
deviceId: selected.features.device_id,
deviceInstance: selected.instance,
accountIndex: parseInt(urlParams.address), accountIndex: parseInt(urlParams.address),
network: urlParams.network, network: urlParams.network,
location: location.pathname, location: location.pathname,
@ -46,7 +47,7 @@ export const update = (newProps: any): any => {
} = getState(); } = getState();
const isLocationChanged: boolean = router.location.pathname !== summary.location; const isLocationChanged: boolean = router.location.pathname !== summary.location;
if (isLocationChanged) { if (isLocationChanged || !summary.deviceState) {
dispatch( init() ); dispatch( init() );
return; return;
} }
@ -137,98 +138,3 @@ export const removeToken = (token: any): any => {
token 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
}
}

@ -2,20 +2,18 @@
'use strict'; 'use strict';
import TrezorConnect, { UI, DEVICE, DEVICE_EVENT, UI_EVENT, TRANSPORT_EVENT } from 'trezor-connect'; 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 ADDRESS from './constants/Address';
import * as TOKEN from './constants/Token'; import * as TOKEN from './constants/Token';
import * as CONNECT from './constants/TrezorConnect'; import * as CONNECT from './constants/TrezorConnect';
import * as DISCOVERY from './constants/Discovery'; import * as DISCOVERY from './constants/Discovery';
import * as NOTIFICATION from './constants/notification'; import * as NOTIFICATION from './constants/notification';
import * as WALLET from './constants/wallet';
import HDKey from 'hdkey'; import HDKey from 'hdkey';
import EthereumjsUtil from 'ethereumjs-util'; import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx'; import EthereumjsTx from 'ethereumjs-tx';
//import { getBalance } from '../services/Web3Service';
import { getBalance } from './Web3Actions'; import { getBalance } from './Web3Actions';
import { getTransactionHistory } from '../services/EtherscanService';
import { push } from 'react-router-redux'; import { push } from 'react-router-redux';
@ -24,7 +22,7 @@ import { init as initWeb3, getNonceAsync, getBalanceAsync, getTokenBalanceAsync
import type { Discovery } from '../reducers/DiscoveryReducer'; import type { Discovery } from '../reducers/DiscoveryReducer';
import { resolveAfter } from '../utils/promiseUtils'; import { resolveAfter } from '../utils/promiseUtils';
import { getAccounts } from '../utils/reducerUtils'; import { getAccounts } from '../utils/reducerUtils';
import { findSelectedDevice, isSavedDevice } from '../reducers/TrezorConnectReducer'; import { findSelectedDevice, isSavedDevice, TrezorDevice } from '../reducers/TrezorConnectReducer';
export const init = (): any => { export const init = (): any => {
return async (dispatch, getState): Promise<void> => { return async (dispatch, getState): Promise<void> => {
@ -57,15 +55,18 @@ export const init = (): any => {
await TrezorConnect.init({ await TrezorConnect.init({
transportReconnect: true, transportReconnect: true,
connectSrc: 'https://localhost:8088/', connectSrc: 'https://localhost:8088/',
// connectSrc: 'https://connect.trezor.io/tpm/',
// connectSrc: 'https://sisyfos.trezor.io/', // connectSrc: 'https://sisyfos.trezor.io/',
debug: true, debug: true,
popup: false, popup: false,
webusb: false webusb: true
}); });
setTimeout(() => { // wait for init
dispatch( initWeb3() );
}, 2000) // setTimeout(() => {
// dispatch( initWeb3() );
//}, 2000)
} catch (error) { } catch (error) {
dispatch({ dispatch({
@ -102,37 +103,53 @@ export const postInit = (): any => {
// devices were connected before Web3 initialized. force DEVICE.CONNECT event on them // devices were connected before Web3 initialized. force DEVICE.CONNECT event on them
const { devices } = getState().connect; 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) { if (devices.length > 0) {
const unacquired = devices.find(d => d.unacquired); const unacquired = devices.find(d => d.unacquired);
if (unacquired) { if (unacquired) {
handleDeviceConnect(unacquired); dispatch( onSelectDevice(unacquired) );
} else { } else {
const latest = devices.sort((a, b) => { const latest: Array<TrezorDevice> = sortDevices(devices);
if (!a.ts || !b.ts) { const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
return -1; dispatch( onSelectDevice(firstConnected || latest[0]) );
// TODO
if (initialParams) {
if (!initialParams.hasOwnProperty("network") && initialPathname !== getState().router.location.pathname) {
// dispatch( push(initialPathname) );
} else { } 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 => { const sortDevices = (devices: Array<TrezorDevice>): Array<TrezorDevice> => {
return (dispatch, getState): void => { 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); const selected = findSelectedDevice(getState().connect);
if (selected && selected.state) { if (!selected || (selected && selected.state)) {
dispatch( onSelectDevice(device) );
} else if (!selected) {
dispatch( onSelectDevice(device) ); dispatch( onSelectDevice(device) );
} }
// if (device.unacquired && selected && selected.path !== device.path && !selected.connected) { // 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 // 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 => { return (dispatch, getState): void => {
// || device.isUsedElsewhere // || device.isUsedElsewhere
console.log("------> REDITTO", device, getState().wallet.initialUrl);
// switch to initial url and reset this value
if (device.unacquired) { if (device.unacquired) {
dispatch( push(`/device/${ device.path }/acquire`) ); dispatch( push(`/device/${ device.path }/acquire`) );
} else if (device.features.bootloader_mode) { } else if (device.features.bootloader_mode) {
@ -155,58 +180,50 @@ export function onSelectDevice(device: any): any {
} else if (device.instance) { } else if (device.instance) {
dispatch( push(`/device/${ device.features.device_id }:${ device.instance }`) ); dispatch( push(`/device/${ device.features.device_id }:${ device.instance }`) );
} else { } 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<TrezorDevice> = getState().connect.devices.filter(d => d.path === device.path);
const latest: Array<TrezorDevice> = 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 }/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<any> => {
// 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 => { export const switchToFirstAvailableDevice = (): any => {
return async (dispatch, getState): Promise<void> => { return async (dispatch, getState): Promise<void> => {
const { devices } = getState().connect; const { devices } = getState().connect;
if (devices.length > 0) { if (devices.length > 0) {
// TODO: Priority: // TODO: Priority:
// 1. Unacquired // 1. First Unacquired
// 2. First connected // 2. First connected
// 3. Saved with latest timestamp // 3. Saved with latest timestamp
// 4. First from the list
const unacquired = devices.find(d => d.unacquired); const unacquired = devices.find(d => d.unacquired);
if (unacquired) { if (unacquired) {
dispatch( initConnectedDevice(unacquired) ); dispatch( initConnectedDevice(unacquired) );
} else { } else {
const latest = devices.sort((a, b) => { const latest: Array<TrezorDevice> = sortDevices(devices);
if (!a.ts || !b.ts) { const firstConnected: ?TrezorDevice = latest.find(d => d.connected);
return -1; dispatch( onSelectDevice(firstConnected || latest[0]) );
} else {
return a.ts > b.ts ? 1 : -1;
}
});
dispatch( initConnectedDevice(devices[0]) );
} }
} else { } else {
dispatch( push('/') ); dispatch( push('/') );
dispatch({ dispatch({
@ -217,17 +234,24 @@ export const switchToFirstAvailableDevice = (): any => {
} }
} }
export const getSelectedDeviceState = (): any => { export const getSelectedDeviceState = (): any => {
return async (dispatch, getState): Promise<void> => { return async (dispatch, getState): Promise<void> => {
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);
console.warn("init selected", selected) console.warn("init selected", selected)
if (selected if (selected
&& selected.connected && selected.connected
&& !selected.unacquired && selected.features
&& !selected.acquiring && !selected.acquiring
&& !selected.state) { && !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) { if (response && response.success) {
dispatch({ dispatch({
@ -239,20 +263,25 @@ export const getSelectedDeviceState = (): any => {
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
payload: { payload: {
devicePath: selected.path,
type: 'error', type: 'error',
title: 'Authentification error', title: 'Authentication error',
message: response.payload.error, message: response.payload.error,
cancelable: true, cancelable: false,
actions: [ actions: [
{ {
label: 'Try again', label: 'Try again',
callback: () => { callback: () => {
dispatch( {
type: NOTIFICATION.CLOSE,
payload: { devicePath: selected.path }
});
dispatch( getSelectedDeviceState() ); dispatch( getSelectedDeviceState() );
} }
} }
] ]
} }
}) });
} }
} }
} }
@ -273,7 +302,7 @@ export const deviceDisconnect = (device: any): any => {
dispatch({ dispatch({
type: CONNECT.REMEMBER_REQUEST, type: CONNECT.REMEMBER_REQUEST,
device, 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 { export function acquire(): any {
return async (dispatch, getState) => { return async (dispatch, getState) => {
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);
const saved = getState().connect.devices.map(d => { // const saved = getState().connect.devices.map(d => {
if (d.state) { // if (d.state) {
return { // return {
instance: d.instance, // instance: d.instance,
state: d.state // state: d.state
} // }
} else { // } else {
return null; // return null;
} // }
}); // });
//const response = await __acquire(selected.path, selected.instance); //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) // called from Aside - device menu (forget single instance)
export const forget = (device: any) => { export const forget = (device: any) => {
return { return {
@ -404,6 +443,12 @@ export const beginDiscoveryProcess = (device: any, network: string): any => {
const { config } = getState().localStorage; const { config } = getState().localStorage;
const coinToDiscover = config.coins.find(c => c.network === network); const coinToDiscover = config.coins.find(c => c.network === network);
dispatch({
type: DISCOVERY.WAITING,
device,
network
});
// TODO: validate device deviceState // TODO: validate device deviceState
// const deviceState = await __acquire(device.path, device.instance); // const deviceState = await __acquire(device.path, device.instance);
// if (deviceState && deviceState.success) { // if (deviceState && deviceState.success) {
@ -425,9 +470,10 @@ export const beginDiscoveryProcess = (device: any, network: string): any => {
keepSession: true // acquire and hold session keepSession: true // acquire and hold session
}); });
// handle TREZOR response error
if (!response.success) { if (!response.success) {
// TODO: check message // TODO: check message
console.warn("DISCO ERROR", response) console.warn("DISCOVERY ERROR", response)
dispatch({ dispatch({
type: NOTIFICATION.ADD, type: NOTIFICATION.ADD,
payload: { payload: {
@ -448,9 +494,10 @@ export const beginDiscoveryProcess = (device: any, network: string): any => {
return; 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<number> = response.payload.path; const basePath: Array<number> = response.payload.path;
const hdKey = new HDKey(); const hdKey = new HDKey();
hdKey.publicKey = new Buffer(response.payload.publicKey, 'hex'); hdKey.publicKey = new Buffer(response.payload.publicKey, 'hex');
@ -502,6 +549,15 @@ export const discoverAddress = (device: any, discoveryProcess: Discovery): any =
}); });
if (discoveryProcess.interrupted) return; 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) { if (verifyAddress && verifyAddress.success) {
//const trezorAddress: string = '0x' + verifyAddress.payload.address; //const trezorAddress: string = '0x' + verifyAddress.payload.address;
const trezorAddress: string = EthereumjsUtil.toChecksumAddress(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) { } else if (!selected.state) {
console.warn("Start discovery: Selected device wasn't authenticated yet...") console.warn("Start discovery: Selected device wasn't authenticated yet...")
return; return;
} else if (selected.connected && !selected.available) {
console.warn("Start discovery: Selected device is unavailable...")
return;
} }
const discovery = getState().discovery; const discovery = getState().discovery;
@ -679,8 +738,8 @@ export const restoreDiscovery = (): any => {
} }
// there is no discovery process but it should be // 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 // this is possible race condition when "network" was changed in url but device was not authenticated yet
// try to discovery after CONNECT.AUTH_DEVICE action // try to start discovery after CONNECT.AUTH_DEVICE action
export const checkDiscoveryStatus = (): any => { export const checkDiscoveryStatus = (): any => {
return (dispatch, getState): void => { return (dispatch, getState): void => {
const selected = findSelectedDevice(getState().connect); const selected = findSelectedDevice(getState().connect);

@ -2,9 +2,8 @@
'use strict'; 'use strict';
export const ON_RESIZE: string = 'ON_RESIZE'; 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 TOGGLE_DEVICE_DROPDOWN: string = 'TOGGLE_DEVICE_DROPDOWN';
export const RESIZE_CONTAINER: string = 'RESIZE_CONTAINER'; import * as WALLET from './constants/wallet';
export const onResize = (): any => { export const onResize = (): any => {
return { return {
@ -14,20 +13,13 @@ export const onResize = (): any => {
export const onBeforeUnload = (): any => { export const onBeforeUnload = (): any => {
return { return {
type: ON_BEFORE_UNLOAD type: WALLET.ON_BEFORE_UNLOAD
}
}
export const resizeAppContainer = (opened: boolean): any => {
return {
type: RESIZE_CONTAINER,
opened
} }
} }
export const toggleDeviceDropdown = (opened: boolean): any => { export const toggleDeviceDropdown = (opened: boolean): any => {
return { return {
type: TOGGLE_DEVICE_DROPDOWN, type: WALLET.TOGGLE_DEVICE_DROPDOWN,
opened opened
} }
} }

@ -7,10 +7,8 @@ import EthereumjsUtil from 'ethereumjs-util';
import EthereumjsTx from 'ethereumjs-tx'; import EthereumjsTx from 'ethereumjs-tx';
import TrezorConnect from 'trezor-connect'; import TrezorConnect from 'trezor-connect';
import { strip } from '../utils/ethUtils'; import { strip } from '../utils/ethUtils';
import * as ACTIONS from './index';
import * as ADDRESS from './constants/Address'; import * as ADDRESS from './constants/Address';
import * as WEB3 from './constants/Web3'; import * as WEB3 from './constants/Web3';
import { loadHistory } from '../services/EtherscanService';
import { httpRequest } from '../utils/networkUtils'; import { httpRequest } from '../utils/networkUtils';
type ActionMethod = (dispatch: any, getState: any) => Promise<any>; type ActionMethod = (dispatch: any, getState: any) => Promise<any>;
@ -192,9 +190,6 @@ export function init(web3: ?Web3, coinIndex: number = 0): ActionMethod {
} }
} }
function initBlockTicker() {
}
export function initContracts(): ActionMethod { export function initContracts(): ActionMethod {
return async (dispatch, getState) => { 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 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) => { web3.eth.getGasPrice((error, gasPrice) => {
if (!error) { if (!error) {
dispatch({ if (web3instance.gasPrice && web3instance.gasPrice.toString() !== gasPrice.toString()) {
type: WEB3.GAS_PRICE_UPDATED, dispatch({
network: network, type: WEB3.GAS_PRICE_UPDATED,
gasPrice network: network,
}); gasPrice
});
}
} }
}); });
} }
@ -313,7 +311,6 @@ export function getTransactionReceipt(tx: any): any {
web3.eth.getBlock(receipt.blockHash, (error, block) => { web3.eth.getBlock(receipt.blockHash, (error, block) => {
console.log("---MAMM BLOCK", error, block, receipt, receipt.blockHash) console.log("---MAMM BLOCK", error, block, receipt, receipt.blockHash)
dispatch({ dispatch({
//type: ACTIONS.TX_CONFIRMED,
type: WEB3.PENDING_TX_RESOLVED, type: WEB3.PENDING_TX_RESOLVED,
tx, tx,
receipt, receipt,

@ -11,3 +11,4 @@ export const FORGET: string = 'modal__forget';
export const REMEMBER: string = 'modal__remember'; export const REMEMBER: string = 'modal__remember';
export const ON_FORGET: string = 'modal__on_forget'; export const ON_FORGET: string = 'modal__on_forget';
export const ON_REMEMBER: string = 'modal__on_remember'; export const ON_REMEMBER: string = 'modal__on_remember';
export const CLOSE: string = 'modal__close';

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

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

@ -26,7 +26,7 @@ export const Notification = (props: any) => {
) : null } ) : null }
<div className="notification-body"> <div className="notification-body">
<h2>{ props.title }</h2> <h2>{ props.title }</h2>
<p dangerouslySetInnerHTML={{__html: props.message }}></p> { props.message ? (<p dangerouslySetInnerHTML={{__html: props.message }}></p>) : null }
</div> </div>
{ props.actions && props.actions.length > 0 ? ( { props.actions && props.actions.length > 0 ? (
<div className="notification-action"> <div className="notification-action">

@ -6,19 +6,28 @@ import TrezorConnect from 'trezor-connect';
export default class InstallBridge extends Component { export default class InstallBridge extends Component {
componentDidMount(): void {
const transport: any = this.props.transport;
if (transport && transport.version.indexOf('webusb') >= 0)
TrezorConnect.renderWebUSBButton();
}
componentDidUpdate() { componentDidUpdate() {
const transport = this.props.transport; const transport = this.props.transport;
if (transport && transport.type.indexOf('webusb') >= 0) if (transport && transport.version.indexOf('webusb') >= 0)
TrezorConnect.renderWebUSBButton(); TrezorConnect.renderWebUSBButton();
} }
render() { render() {
let css = 'row';
let webusb = null; let webusb = null;
let connectClaim = 'Connect TREZOR to continue'; let connectClaim = 'Connect TREZOR to continue';
let and = null; let and = null;
let bridgeClaim = null; let bridgeClaim = null;
const transport = this.props.transport; const transport = this.props.transport;
if (transport && transport.type.indexOf('webusb') >= 0) { if (transport && transport.version.indexOf('webusb') >= 0) {
css = 'row webusb'
webusb = <button className="trezor-webusb-button">Check for devices</button>; webusb = <button className="trezor-webusb-button">Check for devices</button>;
connectClaim = 'Connect TREZOR'; connectClaim = 'Connect TREZOR';
and = <p>and</p>; and = <p>and</p>;
@ -30,7 +39,7 @@ export default class InstallBridge extends Component {
<h2 className="claim">The private bank in your hands.</h2> <h2 className="claim">The private bank in your hands.</h2>
<p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p> <p>TREZOR Wallet is an easy-to-use interface for your TREZOR.</p>
<p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p> <p>TREZOR Wallet allows you to easily control your funds, manage your balance and initiate transfers.</p>
<div className="row"> <div className={ css }>
<p className="connect"> <p className="connect">
<span> <span>
<svg width="12px" height="35px" viewBox="0 0 20 57"> <svg width="12px" height="35px" viewBox="0 0 20 57">
@ -44,8 +53,8 @@ export default class InstallBridge extends Component {
{ connectClaim } { connectClaim }
</span> </span>
</p> </p>
{ and } <p className="webusb-and">and</p>
{ webusb } <button className="trezor-webusb-button">Check for devices</button>
</div> </div>
<div className="image"> <div className="image">
<p> <p>

@ -2,6 +2,7 @@
'use strict'; 'use strict';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Preloader from './Preloader';
import Select from 'react-select'; import Select from 'react-select';
type State = { type State = {
@ -28,9 +29,9 @@ export default class InstallBridge extends Component {
const currentTarget = installers.find(i => i.id === props.browserState.osname); const currentTarget = installers.find(i => i.id === props.browserState.osname);
this.state = { this.state = {
version: '2.0.11', version: '2.0.12',
target: currentTarget, url: 'https://wallet.trezor.io/data/bridge/2.0.12/',
url: 'https://wallet.trezor.io/data/bridge/2.0.11/' 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() { render() {
if (!this.state.target) {
return <Preloader />;
}
const url = `${ this.state.url }${ this.state.target.value }`; const url = `${ this.state.url }${ this.state.target.value }`;
return ( return (
<main> <main>
<h3 className="claim">TREZOR Bridge. <span>Version 2.0.11</span></h3> <h3 className="claim">TREZOR Bridge. <span>Version 2.0.12</span></h3>
<p>New communication tool to facilitate the connection between your TREZOR and your internet browser.</p> <p>New communication tool to facilitate the connection between your TREZOR and your internet browser.</p>
<div className="row"> <div className="row">
<Select <Select

@ -58,7 +58,7 @@ export default (props: any): any => {
} else if (connectError || bridgeRoute) { } else if (connectError || bridgeRoute) {
css += ' install-bridge'; css += ' install-bridge';
body = <InstallBridge browserState={ props.connect.browserState } />; body = <InstallBridge browserState={ props.connect.browserState } />;
} else if (web3.length > 0 && devices.length < 1) { } else if (props.wallet.ready && devices.length < 1) {
css += ' connect-device'; css += ' connect-device';
body = <ConnectDevice transport={ transport } />; body = <ConnectDevice transport={ transport } />;
} }

@ -1,24 +1,24 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
import React, { Component, PropTypes } from 'react'; import React from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import LandingPage from '../components/landing/LandingPage'; import LandingPage from './LandingPage';
import * as LogActions from '../actions/LogActions';
function mapStateToProps(state, own) { const mapStateToProps = (state, own) => {
return { return {
localStorage: state.localStorage, localStorage: state.localStorage,
modal: state.modal, modal: state.modal,
web3: state.web3, web3: state.web3,
wallet: state.wallet,
connect: state.connect, connect: state.connect,
router: state.router router: state.router
}; };
} }
function mapDispatchToProps(dispatch) { const mapDispatchToProps = (dispatch) => {
return { return {
}; };

@ -38,20 +38,42 @@ export const ConfirmUnverifiedAddress = (props: any): any => {
showAddress showAddress
} = props.receiveActions; } = props.receiveActions;
const {
device
} = props.modal;
return ( if (!device.connected) {
<div className="confirm-address-unverified"> return (
<button className="close-modal transparent" onClick={ onCancel }></button> <div className="confirm-address-unverified">
<h3>Your TREZOR is not connected</h3> <button className="close-modal transparent" onClick={ onCancel }></button>
<p>To prevent phishing attacks, you should verify the address on your TREZOR first. Please reconnect your device to continue with the verification process.</p> <h3>{ device.instanceLabel } is not connected</h3>
<button onClick={ event => { <p>To prevent phishing attacks, you should verify the address on your TREZOR first. Please reconnect your device to continue with the verification process.</p>
onCancel(); <button onClick={ event => {
showAddress(account.addressPath); onCancel();
} }>Try again</button> showAddress(account.addressPath);
<button className="white" onClick={ event => { } }>Try again</button>
onCancel(); <button className="white" onClick={ event => {
showUnverifiedAddress(); onCancel();
} }>Show unverified address</button> showUnverifiedAddress();
</div> } }>Show unverified address</button>
); </div>
);
} else {
const enable: string = device.features.passphrase_protection ? 'Enable' : 'Disable';
return (
<div className="confirm-address-unverified">
<button className="close-modal transparent" onClick={ onCancel }></button>
<h3>{ device.instanceLabel } is unavailable</h3>
<p>To prevent phishing attacks, you should verify the address on your TREZOR first. { enable } passphrase settings to continue with the verification process.</p>
<button onClick={ event => {
onCancel();
showAddress(account.addressPath);
} }>Try again</button>
<button className="white" onClick={ event => {
onCancel();
showUnverifiedAddress();
} }>Show unverified address</button>
</div>
);
}
} }

@ -7,13 +7,11 @@ const RememberDevice = (props: any): any => {
const { device } = props.modal; const { device } = props.modal;
const { onCancel, onDuplicateDevice } = props.modalActions; const { onCancel, onDuplicateDevice } = props.modalActions;
return ( return (
<div className="pin"> <div className="duplicate">
<h3>Duplicate { device.label } ?</h3> <h3>Clone { device.instanceLabel }?</h3>
<p>This will create new instance of device which can be used with different passphrase</p>
<label>Device label</label> <button onClick={ event => onDuplicateDevice( { ...device, instanceLabel: "TODO: user label from input" } ) }>Create new instance</button>
<input type="text" /> <button className="white" onClick={ onCancel }>Cancel</button>
<button onClick={ onCancel }>Cancel</button>
<button onClick={ event => onDuplicateDevice( { ...device, instanceLabel: "kokot" } ) }>Duplicate</button>
</div> </div>
); );
} }

@ -3,8 +3,10 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import raf from 'raf'; import raf from 'raf';
import { findSelectedDevice } from '../../reducers/TrezorConnectReducer';
type State = { type State = {
deviceLabel: string;
singleInput: boolean; singleInput: boolean;
passphrase: string; passphrase: string;
passphraseRevision: string; passphraseRevision: string;
@ -25,10 +27,20 @@ export default class PinModal extends Component {
super(props); super(props);
// check if this device is already known // 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 = { this.state = {
singleInput: isSavedDevice ? true : false, deviceLabel,
singleInput,
passphrase: '', passphrase: '',
passphraseRevision: '', passphraseRevision: '',
passphraseFocused: false, passphraseFocused: false,
@ -220,6 +232,7 @@ export default class PinModal extends Component {
} = this.props.modal; } = this.props.modal;
const { const {
deviceLabel,
singleInput, singleInput,
passphrase, passphrase,
passphraseRevision, passphraseRevision,
@ -242,7 +255,7 @@ export default class PinModal extends Component {
return ( return (
<div className="passphrase"> <div className="passphrase">
{/* <button className="close-modal transparent" onClick={ event => this.submit(true) }></button> */} {/* <button className="close-modal transparent" onClick={ event => this.submit(true) }></button> */}
<h3>Enter { device.label } passphrase</h3> <h3>Enter { deviceLabel } passphrase</h3>
<p>Note that passphrase is case-sensitive.</p> <p>Note that passphrase is case-sensitive.</p>
<div className="row"> <div className="row">
<label>Passphrase</label> <label>Passphrase</label>

@ -63,11 +63,20 @@ export default class RememberDevice extends Component {
} }
render(): any { render(): any {
const { device } = this.props.modal; const { device, instances } = this.props.modal;
const { onForgetDevice, onRememberDevice } = this.props.modalActions; const { onForgetDevice, onRememberDevice } = this.props.modalActions;
let label = device.label;
let devicePlural = false;
if (instances && instances.length > 0) {
label = instances.map(i => {
return (<span>{i.instanceLabel}</span>);
})
devicePlural = instances.length > 1;
}
return ( return (
<div className="remember"> <div className="remember">
<h3>Forget { device.label } ?</h3> <h3>Forget {label}?</h3>
<p>Would you like TREZOR Wallet to forget your device or to remember it, so that it is still visible even while disconnected?</p> <p>Would you like TREZOR Wallet to forget your device or to remember it, so that it is still visible even while disconnected?</p>
<button onClick={ event => onForgetDevice(device) }>Forget</button> <button onClick={ event => onForgetDevice(device) }>Forget</button>
<button className="white" onClick={ event => onRememberDevice(device) }><span>Remember <Loader size={ 28 } label={ this.state.countdown } /></span></button> <button className="white" onClick={ event => onRememberDevice(device) }><span>Remember <Loader size={ 28 } label={ this.state.countdown } /></span></button>
@ -81,7 +90,7 @@ export const ForgetDevice = (props: any): any => {
const { onForgetSingleDevice, onCancel } = props.modalActions; const { onForgetSingleDevice, onCancel } = props.modalActions;
return ( return (
<div className="remember"> <div className="remember">
<h3>Forget { device.label } ?</h3> <h3>Forget { device.instanceLabel } ?</h3>
<p>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.</p> <p>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.</p>
<button onClick={ event => onForgetSingleDevice(device) }>Forget</button> <button onClick={ event => onForgetSingleDevice(device) }>Forget</button>
<button className="white" onClick={ onCancel }>Don't forget</button> <button className="white" onClick={ onCancel }>Don't forget</button>
@ -94,7 +103,7 @@ export const DisconnectDevice = (props: any): any => {
const { onForgetSingleDevice, onCancel } = props.modalActions; const { onForgetSingleDevice, onCancel } = props.modalActions;
return ( return (
<div className="remember"> <div className="remember">
<h3>Unplug { device.label }</h3> <h3>Unplug { device.instanceLabel }</h3>
<p>TREZOR Wallet will forget your TREZOR right after you disconnect it.</p> <p>TREZOR Wallet will forget your TREZOR right after you disconnect it.</p>
<b>TODO: its not true, actually i've already forget those data!!!</b> <b>TODO: its not true, actually i've already forget those data!!!</b>
</div> </div>

@ -105,6 +105,7 @@ const mapStateToProps = (state: any, own: any): any => {
modal: state.modal, modal: state.modal,
accounts: state.accounts, accounts: state.accounts,
devices: state.connect.devices, devices: state.connect.devices,
connect: state.connect,
sendForm: state.sendForm, sendForm: state.sendForm,
receive: state.receive, receive: state.receive,
localStorage: state.localStorage localStorage: state.localStorage

@ -2,7 +2,10 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Notification } from '../common/Notification'; import { Notification } from '../common/Notification';
import * as TrezorConnectActions from '../../actions/TrezorConnectActions';
const Acquire = (props: any): any => { const Acquire = (props: any): any => {
@ -21,19 +24,24 @@ const Acquire = (props: any): any => {
title="Device is used in other window" title="Device is used in other window"
message="Do you want to use your device in this window?" message="Do you want to use your device in this window?"
className="info" className="info"
cancelable={false} cancelable={ false }
actions={actions} actions={ actions }
close={ () => {} } close={ () => {} }
/> />
{/* <div className="warning">
<div>
<h2></h2>
<p></p>
</div>
<button onClick={ event => props.acquireDevice() }>Acquire device</button>
</div> */}
</section> </section>
); );
} }
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);

@ -2,6 +2,8 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
const Bootloader = (props: any): any => { const Bootloader = (props: any): any => {
return ( 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);

@ -2,6 +2,8 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
const Dashboard = (props: any): any => { const Dashboard = (props: any): any => {
return ( 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);

@ -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 (
<section className="settings">
Device settings
</section>
);
}
const mapStateToProps = (state, own) => {
return {
};
}
const mapDispatchToProps = (dispatch) => {
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DeviceSettings);

@ -2,18 +2,23 @@
'use strict'; 'use strict';
import React, { Component } from 'react'; 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 { QRCode } from 'react-qr-svg';
import AbstractAccount from './account/AbstractAccount'; import AbstractAccount from './account/AbstractAccount';
import { Notification } from '../common/Notification'; 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() { 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 { const {
network, network,
@ -23,8 +28,8 @@ const _render = (props: any): any => {
addressUnverified, addressUnverified,
} = props.receive; } = props.receive;
const device = props.devices.find(d => d.state === deviceState); // 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 account = props.accounts.find(a => a.deviceState === deviceState && a.index === accountIndex && a.network === network);
let qrCode = null; let qrCode = null;
let address = `${account.address.substring(0, 20)}...`; let address = `${account.address.substring(0, 20)}...`;
@ -50,7 +55,7 @@ const _render = (props: any): any => {
className = addressUnverified ? 'address unverified' : 'address'; className = addressUnverified ? 'address unverified' : 'address';
const tooltip = addressUnverified ? const tooltip = addressUnverified ?
(<div>Unverified address.<br/>{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }</div>) (<div>Unverified address.<br/>{ device.connected && device.available ? 'Show on TREZOR' : 'Connect your TREZOR to verify it.' }</div>)
: :
(<div>{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }</div>); (<div>{ device.connected ? 'Show on TREZOR' : 'Connect your TREZOR to verify address.' }</div>);
@ -68,9 +73,7 @@ const _render = (props: any): any => {
return ( return (
<section className="receive"> <section className="receive">
{ !device.connected ? ( { deviceStatusNotification }
<Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } />
) : null }
<h2>Receive Ethereum or tokens</h2> <h2>Receive Ethereum or tokens</h2>
<div className={ className }> <div className={ className }>
@ -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);

@ -1,12 +0,0 @@
/* @flow */
'use strict';
import React from 'react';
export default (props: any): any => {
return (
<section className="settings">
Settings
</section>
);
}

@ -2,8 +2,10 @@
'use strict'; 'use strict';
import React from 'react'; import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
export default (props: any): any => { const SignVerify = (props: any): any => {
return ( return (
<section className="signverify"> <section className="signverify">
<div className="sign"> <div className="sign">
@ -27,3 +29,16 @@ export default (props: any): any => {
</section> </section>
); );
} }
const mapStateToProps = (state, own) => {
return {
};
}
const mapDispatchToProps = (dispatch) => {
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SignVerify);

@ -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 (
<section className="settings">
Wallet settings
</section>
);
}
const mapStateToProps = (state, own) => {
return {
};
}
const mapDispatchToProps = (dispatch) => {
return {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(WalletSettings);

@ -3,19 +3,54 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Notification } from '../../common/Notification'; 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 { 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() { componentDidMount() {
this.props.initAccount(); this.props.initAccount();
} }
componentWillUpdate(newProps: any) { componentWillUpdate(newProps: any) {
this.device = null;
this.discovery = null;
this.account = null;
this.deviceStatusNotification = null;
this.props.updateAccount(); this.props.updateAccount();
} }
componentWillUnmount() { componentWillUnmount() {
this.props.disposeAccount(); this.props.disposeAccount();
this.device = null;
this.discovery = null;
this.account = null;
this.deviceStatusNotification = null;
} }
render(state: any): any { render(state: any): any {
@ -23,31 +58,49 @@ export default class AbstractAccount extends Component {
const props = this.props; const props = this.props;
if (!state.deviceState) { if (!state.deviceState) {
return (<section></section>); return (<section><Notification className="info" title="Loading device" /></section>);
} }
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) { if (!device) {
return (<section>Device with state {state.deviceState} not found</section>); return (<section>Device with state {state.deviceState} not found</section>);
} }
const discovery = props.discovery.find(d => d.deviceState === device.state && d.network === state.network); 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); const account = props.accounts.find(a => a.deviceState === state.deviceState && a.index === state.accountIndex && a.network === state.network);
let deviceStatusNotification = null;
if (!account) { if (!account) {
if (!discovery || discovery.waitingForDevice) { if (!discovery || discovery.waitingForDevice) {
return ( if (device.connected) {
<section> if (device.available) {
<Notification return (
className="info" <section>
title="Loading account" <Notification className="info" title="Loading account" />
message="" </section>
/> );
</section> } else {
); return (
<section>
<Notification
className="info"
title={ `Device ${ device.instanceLabel } is unavailable` }
message="Change passphrase settings to use this device"
/>
</section>
);
}
} else {
return (
<section>
<Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } />
</section>
);
}
} else if (discovery.completed) { } else if (discovery.completed) {
return ( return (
<section> <section>
<Notification className="warning" title="Account is not exist" /> <Notification className="warning" title="Account does not exist" />
</section> </section>
); );
} else { } else {
@ -57,8 +110,20 @@ export default class AbstractAccount extends Component {
</section> </section>
); );
} }
} else {
if (!device.connected) {
deviceStatusNotification = <Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } />;
} else if (!device.available) {
deviceStatusNotification = <Notification className="info" title={ `Device ${ device.instanceLabel } is unavailable` } message="Change passphrase settings to use this device" />;
}
} }
// Set class variables for extender classes
this.device = device;
this.discovery = discovery;
this.account = account;
this.deviceStatusNotification = deviceStatusNotification;
return null; return null;
} }
} }

@ -17,8 +17,9 @@ const AccountSelection = (props: any): any => {
if (!selected) return null; if (!selected) return null;
const { location } = props.router; const { location } = props.router;
const urlParams = location.params;
const accounts = props.accounts; 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 { config } = props.localStorage;
const selectedCoin = config.coins.find(c => c.network === location.params.network); const selectedCoin = config.coins.find(c => c.network === location.params.network);

@ -48,7 +48,7 @@ const Aside = (props: any): any => {
// ); // );
// } // }
let menu = null; let menu = <section></section>;
if (props.deviceDropdownOpened) { if (props.deviceDropdownOpened) {
menu = <DeviceDropdown {...props} />; menu = <DeviceDropdown {...props} />;

@ -34,6 +34,10 @@ const Value = (props: any): any => {
const deviceMenuItems: Array<any> = []; const deviceMenuItems: Array<any> = [];
if (device.features && device.features.passphrase_protection) {
deviceMenuItems.push("settings"); // TODO: clone
}
if (device.unacquired) { if (device.unacquired) {
css += " unacquired"; css += " unacquired";
deviceStatus = "Used in other window"; deviceStatus = "Used in other window";
@ -49,6 +53,9 @@ const Value = (props: any): any => {
if (!device.connected) { if (!device.connected) {
css += " reload-features"; css += " reload-features";
deviceStatus = "Disconnected"; deviceStatus = "Disconnected";
} else if (!device.available) {
css += " unavailable";
deviceStatus = "Unavailable";
} }
if (device.remember) { if (device.remember) {
@ -59,6 +66,8 @@ const Value = (props: any): any => {
css += " trezor-t"; css += " trezor-t";
} }
const deviceMenuButtons = deviceMenuItems.map((item, index) => { const deviceMenuButtons = deviceMenuItems.map((item, index) => {
return ( return (
<div key={ item } className={ item } onClick={ event => onClick(event, item, device) }></div> <div key={ item } className={ item } onClick={ event => onClick(event, item, device) }></div>
@ -81,9 +90,6 @@ const Value = (props: any): any => {
<span className="label">{ device.instanceLabel }</span> <span className="label">{ device.instanceLabel }</span>
<span className="status">{ deviceStatus }</span> <span className="status">{ deviceStatus }</span>
</div> </div>
<div className="device-menu">
{ deviceMenuButtons }
</div>
<div className="arrow"> <div className="arrow">
</div> </div>
</div> </div>
@ -143,8 +149,7 @@ export const DeviceSelect = (props: any): any => {
} }
} }
console.log("DEVSEL", props) const disabled: boolean = (devices.length < 1 && transport && transport.version.indexOf('webusb') < 0);
const disabled: boolean = (devices && devices.length <= 1 && transport && transport.type.indexOf('webusb') < 0);
return ( return (
<Value <Value
@ -169,17 +174,16 @@ export class DeviceDropdown extends Component {
componentDidUpdate() { componentDidUpdate() {
const transport: any = this.props.connect.transport; const transport: any = this.props.connect.transport;
if (transport && transport.type.indexOf('webusb') >= 0) if (transport && transport.version.indexOf('webusb') >= 0)
TrezorConnect.renderWebUSBButton(); TrezorConnect.renderWebUSBButton();
} }
mouseDownHandler(event: MouseEvent): void { mouseDownHandler(event: MouseEvent): void {
console.log("HANDLE DOWN!!!!", event)
let elem = event.target; let elem = event.target;
let block: boolean = false; let block: boolean = false;
while (elem.parentElement) { while (elem.parentElement) {
// if (elem.className.indexOf('aside-button') >= 0) { // 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; block = true;
break; break;
} }
@ -199,7 +203,7 @@ export class DeviceDropdown extends Component {
window.addEventListener('mousedown', this.mouseDownHandler, false); window.addEventListener('mousedown', this.mouseDownHandler, false);
// window.addEventListener('blur', this.blurHandler, false); // window.addEventListener('blur', this.blurHandler, false);
const transport: any = this.props.connect.transport; const transport: any = this.props.connect.transport;
if (transport && transport.type.indexOf('webusb') >= 0) if (transport && transport.version.indexOf('webusb') >= 0)
TrezorConnect.renderWebUSBButton(); TrezorConnect.renderWebUSBButton();
} }
@ -208,16 +212,69 @@ export class DeviceDropdown extends Component {
// window.removeEventListener('blur', this.blurHandler, false); // 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() { render() {
const { devices, transport } = this.props.connect; const { devices, transport } = this.props.connect;
const selected = findSelectedDevice(this.props.connect); const selected = findSelectedDevice(this.props.connect);
let webUsbButton = null; let webUsbButton = null;
if (transport && transport.type.indexOf('webusb') >= 0) { if (transport && transport.version.indexOf('webusb') >= 0) {
webUsbButton = <button className="trezor-webusb-button">Check for devices</button>; webUsbButton = <button className="trezor-webusb-button">Check for devices</button>;
} }
let currentDeviceMenu = null;
if (selected.features) {
const deviceMenuItems: Array<any> = [];
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 (
<div key={ item.type } className={ item.type } onClick={ event => this.onDeviceMenuClick(item, selected) }>{ item.label}</div>
)
});
currentDeviceMenu = deviceMenuButtons.length < 1 ? null : (
<div className="device-menu">
{ deviceMenuButtons }
</div>
);
}
// const currentDeviceMenu = (
// <div className="device-menu">
// <div className="settings">Device settings</div>
// <div className="clone">Clone device</div>
// <div className="forget">Forget device</div>
// </div>
// );
const deviceList: Array<any> = devices.map((dev, index) => { const deviceList: Array<any> = devices.map((dev, index) => {
if (dev === selected) return null; if (dev === selected) return null;
@ -227,6 +284,8 @@ export class DeviceDropdown extends Component {
deviceStatus = "Used in other window"; deviceStatus = "Used in other window";
} else if (!dev.connected) { } else if (!dev.connected) {
deviceStatus = "Disconnected"; deviceStatus = "Disconnected";
} else if (!dev.available) {
deviceStatus = "Unavailable";
} }
if (dev.features && dev.features.major_version > 1) { if (dev.features && dev.features.major_version > 1) {
@ -245,6 +304,7 @@ export class DeviceDropdown extends Component {
return ( return (
<section> <section>
{ currentDeviceMenu }
{ webUsbButton } { webUsbButton }
{ deviceList } { deviceList }
</section> </section>

@ -5,23 +5,25 @@ import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom'; 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 { return {
connect: state.connect, connect: state.connect,
accounts: state.accounts, accounts: state.accounts,
router: state.router, router: state.router,
deviceDropdownOpened: state.DOM.deviceDropdownOpened, deviceDropdownOpened: state.wallet.dropdownOpened,
fiat: state.fiat, fiat: state.fiat,
localStorage: state.localStorage, localStorage: state.localStorage,
discovery: state.discovery discovery: state.discovery
}; };
} }
function mapDispatchToProps(dispatch) { const mapDispatchToProps = (dispatch) => {
return { return {
//onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch), //onAccountSelect: bindActionCreators(AccountActions.onAccountSelect, dispatch),
toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch), toggleDeviceDropdown: bindActionCreators(toggleDeviceDropdown, dispatch),
@ -29,6 +31,7 @@ function mapDispatchToProps(dispatch) {
acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch), acquireDevice: bindActionCreators(TrezorConnectActions.acquire, dispatch),
forgetDevice: bindActionCreators(TrezorConnectActions.forget, dispatch), forgetDevice: bindActionCreators(TrezorConnectActions.forget, dispatch),
duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch), duplicateDevice: bindActionCreators(TrezorConnectActions.duplicateDevice, dispatch),
gotoDeviceSettings: bindActionCreators(TrezorConnectActions.gotoDeviceSettings, dispatch),
onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch), onSelectDevice: bindActionCreators(TrezorConnectActions.onSelectDevice, dispatch),
}; };
} }

@ -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 (
<article>
<nav>
<Route path="/device/:device/network/:network/address/:address" component={ AccountTabs } />
</nav>
<Notifications />
<Log />
{ props.children }
<Footer />
</article>
);
}
const Wallet = (props: any): any => {
return (
<div className="app">
<Header />
<main>
<AsideContainer />
<Content>
{ props.children }
</Content>
</main>
<ModalContainer />
</div>
);
}
const mapStateToProps = (state, own) => {
return {
};
}
const mapDispatchToProps = (dispatch) => {
return { };
}
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Wallet)
);

@ -11,16 +11,16 @@ import AbstractAccount from '../account/AbstractAccount';
export default class Send extends AbstractAccount { export default class Send extends AbstractAccount {
render() { 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 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 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 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 addressTokens = props.tokens.filter(t => t.ethAddress === account.address);
const { const {
@ -87,9 +87,16 @@ const _render = (props: any): any => {
buttonLabel += ` ${total} ${ selectedCoin.symbol }`; buttonLabel += ` ${total} ${ selectedCoin.symbol }`;
} }
if (device && !device.connected) { if (device) {
buttonLabel = 'Device is not connected';
buttonDisabled = true; if (!device.connected){
buttonLabel = 'Device is not connected';
buttonDisabled = true;
} else if (!device.available) {
buttonLabel = 'Device is unavailable';
buttonDisabled = true;
}
} }
let notification = null; let notification = null;
@ -97,9 +104,7 @@ const _render = (props: any): any => {
return ( return (
<section className="send-form"> <section className="send-form">
{ !device.connected ? ( { deviceStatusNotification }
<Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } />
) : null }
<h2>Send Ethereum or tokens</h2> <h2>Send Ethereum or tokens</h2>
<div className="row address-input"> <div className="row address-input">

@ -5,10 +5,10 @@ import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-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 { return {
location: state.router.location, location: state.router.location,
devices: state.connect.devices, devices: state.connect.devices,
@ -22,7 +22,7 @@ function mapStateToProps(state, own) {
}; };
} }
function mapDispatchToProps(dispatch) { const mapDispatchToProps = (dispatch) => {
return { return {
sendFormActions: bindActionCreators(SendFormActions, dispatch), sendFormActions: bindActionCreators(SendFormActions, dispatch),
initAccount: bindActionCreators(SendFormActions.init, dispatch), initAccount: bindActionCreators(SendFormActions.init, dispatch),
@ -31,4 +31,4 @@ function mapDispatchToProps(dispatch) {
}; };
} }
export default connect(mapStateToProps, mapDispatchToProps)(SendForm); export default connect(mapStateToProps, mapDispatchToProps)(SendForm)

@ -9,27 +9,28 @@ import AbstractAccount from '../account/AbstractAccount';
import { Notification } from '../../common/Notification'; import { Notification } from '../../common/Notification';
import SummaryDetails from './SummaryDetails.js'; import SummaryDetails from './SummaryDetails.js';
import SummaryTokens from './SummaryTokens.js'; import SummaryTokens from './SummaryTokens.js';
import { findDevice } from '../../../utils/reducerUtils';
export default class Summary extends AbstractAccount { export default class Summary extends AbstractAccount {
render() { 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 device = props.devices.find(d => d.state === props.summary.deviceState && d.features.device_id === props.summary.deviceId);
const discovery = props.discovery.find(d => d.deviceState === device.state && d.network === props.summary.network); // const device = findDevice(props.devices, props.summary.deviceState, props.summary.deviceId);
const account = props.accounts.find(a => a.deviceState === props.summary.deviceState && a.index === props.summary.accountIndex && a.network === props.summary.network); // 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); const tokens = props.tokens.filter(t => t.ethAddress === account.address);
return ( return (
<section className="summary"> <section className="summary">
{ !device.connected ? ( { deviceStatusNotification }
<Notification className="info" title={ `Device ${ device.instanceLabel } is disconnected` } />
) : null }
<h2 className={ `summary-header ${props.summary.network}` }>Address #{ parseInt(props.match.params.address) + 1 }</h2> <h2 className={ `summary-header ${props.summary.network}` }>Address #{ parseInt(props.match.params.address) + 1 }</h2>

@ -8,7 +8,7 @@ import { connect } from 'react-redux';
import Summary from './Summary'; import Summary from './Summary';
import * as SummaryActions from '../../../actions/SummaryActions'; import * as SummaryActions from '../../../actions/SummaryActions';
function mapStateToProps(state, own) { const mapStateToProps = (state, own) => {
return { return {
location: state.router.location, location: state.router.location,
devices: state.connect.devices, devices: state.connect.devices,
@ -21,7 +21,7 @@ function mapStateToProps(state, own) {
}; };
} }
function mapDispatchToProps(dispatch) { const mapDispatchToProps = (dispatch) => {
return { return {
summaryActions: bindActionCreators(SummaryActions, dispatch), summaryActions: bindActionCreators(SummaryActions, dispatch),
initAccount: bindActionCreators(SummaryActions.init, dispatch), initAccount: bindActionCreators(SummaryActions.init, dispatch),

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

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

@ -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 (
<article>
<nav>
<Route path="/device/:device/network/:network/address/:address" component={ AccountTabs } />
</nav>
<Notifications />
<Log />
{ props.children }
<Footer />
</article>
);
}
function mapStateToProps(state, own) {
return {
};
}
function mapDispatchToProps(dispatch) {
return {
};
}
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Article)
);

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

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

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

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

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

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

@ -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 (
<div className="app">
<Header />
<main>
<AsideContainer />
<ContentContainer>
{ props.children }
</ContentContainer>
</main>
<ModalContainer />
</div>
);
}
function mapStateToProps(state, own) {
return {
};
}
function mapDispatchToProps(dispatch) {
return { };
}
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Wallet)
);

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

@ -5,7 +5,7 @@ import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import store from './store'; import store from './store';
import router from './router'; import router from './router';
import { onResize, onBeforeUnload } from './actions/AppActions'; import { onBeforeUnload } from './actions/WalletActions';
import Raven from 'raven-js'; import Raven from 'raven-js';

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

@ -2,7 +2,6 @@
'use strict'; 'use strict';
import { UI, DEVICE } from 'trezor-connect'; import { UI, DEVICE } from 'trezor-connect';
import * as ACTIONS from '../actions';
import * as RECEIVE from '../actions/constants/receive'; import * as RECEIVE from '../actions/constants/receive';
import * as MODAL from '../actions/constants/Modal'; import * as MODAL from '../actions/constants/Modal';
import * as CONNECT from '../actions/constants/TrezorConnect'; import * as CONNECT from '../actions/constants/TrezorConnect';
@ -10,12 +9,14 @@ import * as CONNECT from '../actions/constants/TrezorConnect';
type ModalState = { type ModalState = {
opened: boolean; opened: boolean;
device: any; device: any;
instances: Array<any>;
windowType: ?string; windowType: ?string;
} }
const initialState: ModalState = { const initialState: ModalState = {
opened: false, opened: false,
device: null, device: null,
instances: null,
windowType: null windowType: null
}; };
@ -26,6 +27,7 @@ export default function modal(state: ModalState = initialState, action: any): an
case RECEIVE.REQUEST_UNVERIFIED : case RECEIVE.REQUEST_UNVERIFIED :
return { return {
...state, ...state,
device: action.device,
opened: true, opened: true,
windowType: action.type windowType: action.type
} }
@ -36,6 +38,7 @@ export default function modal(state: ModalState = initialState, action: any): an
return { return {
...state, ...state,
device: action.device, device: action.device,
instances: action.instances,
opened: true, opened: true,
windowType: action.type windowType: action.type
}; };
@ -92,7 +95,7 @@ export default function modal(state: ModalState = initialState, action: any): an
} }
case UI.CLOSE_UI_WINDOW : case UI.CLOSE_UI_WINDOW :
case ACTIONS.CLOSE_MODAL : case MODAL.CLOSE :
case CONNECT.FORGET : case CONNECT.FORGET :
case CONNECT.FORGET_SINGLE : case CONNECT.FORGET_SINGLE :

@ -3,6 +3,7 @@
import { LOCATION_CHANGE } from 'react-router-redux'; import { LOCATION_CHANGE } from 'react-router-redux';
import * as NOTIFICATION from '../actions/constants/notification'; import * as NOTIFICATION from '../actions/constants/notification';
import { DEVICE } from 'trezor-connect';
type NotificationAction = { type NotificationAction = {
label: string; label: string;
@ -11,6 +12,7 @@ type NotificationAction = {
type NotificationEntry = { type NotificationEntry = {
+id: ?string; +id: ?string;
+devicePath: ?string;
+type: string; +type: string;
+title: string; +title: string;
+message: string; +message: string;
@ -33,6 +35,7 @@ const addNotification = (state: Array<NotificationEntry>, payload: any): Array<N
const newState: Array<NotificationEntry> = state.filter(e => !e.cancelable); const newState: Array<NotificationEntry> = state.filter(e => !e.cancelable);
newState.push({ newState.push({
id: payload.id, id: payload.id,
devicePath: payload.devicePath,
type: payload.type, type: payload.type,
title: payload.title.toString(), title: payload.title.toString(),
message: payload.message.toString(), message: payload.message.toString(),
@ -46,9 +49,11 @@ const addNotification = (state: Array<NotificationEntry>, payload: any): Array<N
const closeNotification = (state: Array<NotificationEntry>, payload: any): Array<NotificationEntry> => { const closeNotification = (state: Array<NotificationEntry>, payload: any): Array<NotificationEntry> => {
if (payload && typeof payload.id === 'string') { 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 { } else {
return state.filter(e => !e.cancelable); return state.filter(entry => !entry.cancelable);
} }
} }
@ -62,6 +67,9 @@ export default function notification(state: Array<NotificationEntry> = initialSt
case NOTIFICATION.CLOSE : case NOTIFICATION.CLOSE :
return closeNotification(state, action.payload); return closeNotification(state, action.payload);
case DEVICE.DISCONNECT :
return state.filter(entry => entry.devicePath !== action.device.path);
default: default:
return state; return state;
} }

@ -5,6 +5,8 @@ import * as RECEIVE from '../actions/constants/receive';
export type State = { export type State = {
+deviceState: ?string; +deviceState: ?string;
+deviceId: ?string;
+deviceInstance: ?string;
+accountIndex: ?number; +accountIndex: ?number;
+network: ?string; +network: ?string;
location: string; location: string;
@ -14,6 +16,8 @@ export type State = {
export const initialState: State = { export const initialState: State = {
deviceState: null, deviceState: null,
deviceId: null,
deviceInstance: null,
accountIndex: null, accountIndex: null,
network: null, network: null,
location: '', location: '',

@ -11,6 +11,8 @@ import { getFeeLevels } from '../actions/SendFormActions';
export type State = { export type State = {
+deviceState: ?string; +deviceState: ?string;
+deviceId: ?string;
+deviceInstance: ?string;
+accountIndex: number; +accountIndex: number;
+network: string; +network: string;
+coinSymbol: string; +coinSymbol: string;
@ -51,6 +53,8 @@ export type FeeLevel = {
export const initialState: State = { export const initialState: State = {
deviceState: null, deviceState: null,
deviceId: null,
deviceInstance: null,
accountIndex: 0, accountIndex: 0,
network: '', network: '',
coinSymbol: '', coinSymbol: '',
@ -83,11 +87,11 @@ export const initialState: State = {
const onGasPriceUpdated = (state: State, action: any): State => { const onGasPriceUpdated = (state: State, action: any): State => {
function getRandomInt(min, max) { // function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min; // return Math.floor(Math.random() * (max - min + 1)) + min;
} // }
const newPrice = getRandomInt(10, 50).toString(); // const newPrice = getRandomInt(10, 50).toString();
//const newPrice = EthereumjsUnits.convert(action.gasPrice, 'wei', 'gwei'); const newPrice = EthereumjsUnits.convert(action.gasPrice, 'wei', 'gwei');
if (action.network === state.network && newPrice !== state.recommendedGasPrice) { if (action.network === state.network && newPrice !== state.recommendedGasPrice) {
const newState: State = { ...state }; const newState: State = { ...state };
if (!state.untouched) { if (!state.untouched) {

@ -5,6 +5,8 @@ import * as SUMMARY from '../actions/constants/summary';
export type State = { export type State = {
+deviceState: ?string; +deviceState: ?string;
+deviceId: ?string;
+deviceInstance: ?string;
+accountIndex: ?number; +accountIndex: ?number;
+network: ?string; +network: ?string;
location: string; location: string;
@ -15,6 +17,8 @@ export type State = {
export const initialState: State = { export const initialState: State = {
deviceState: null, deviceState: null,
deviceId: null,
deviceInstance: null,
accountIndex: null, accountIndex: null,
network: null, network: null,
location: '', location: '',

@ -7,6 +7,7 @@ import * as CONNECT from '../actions/constants/TrezorConnect';
export type TrezorDevice = { export type TrezorDevice = {
remember: boolean; remember: boolean;
connected: boolean; connected: boolean;
available: boolean; // device cannot be used because of features.passphrase_protection is different then expected (saved)
path: string; path: string;
+label: string; +label: string;
+state: string; +state: string;
@ -58,6 +59,8 @@ export const findSelectedDevice = (state: State): ?TrezorDevice => {
}); });
} }
export const isSavedDevice = (state: State, device: any): ?Array<TrezorDevice> => { export const isSavedDevice = (state: State, device: any): ?Array<TrezorDevice> => {
const selected: ?SelectedDevice = state.selectedDevice; const selected: ?SelectedDevice = state.selectedDevice;
if (!selected) return null; if (!selected) return null;
@ -73,17 +76,36 @@ export const isSavedDevice = (state: State, device: any): ?Array<TrezorDevice> =
} }
const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice => { 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 = { const dev: TrezorDevice = {
// ...current, // ...current,
...upcoming, ...upcoming,
// make sure that instance specific variables will not be overridden // make sure that instance specific variables will not be overridden
connected: typeof upcoming.connected === 'boolean' ? upcoming.connected : current.connected, 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, remember: typeof upcoming.remember === 'boolean' ? upcoming.remember : current.remember,
instance: current.instance, instance: current.instance,
instanceLabel: current.instanceLabel, instanceLabel,
state: current.state, state: current.state,
acquiring: typeof upcoming.acquiring === 'boolean' ? upcoming.acquiring : current.acquiring, 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) { if (upcoming.unacquired && current.state) {
@ -94,18 +116,11 @@ const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice =>
} else if (!upcoming.unacquired && current.unacquired) { } else if (!upcoming.unacquired && current.unacquired) {
dev.instanceLabel = upcoming.label; dev.instanceLabel = upcoming.label;
if (typeof dev.instance === 'number') { 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; return dev;
} }
@ -132,7 +147,40 @@ const addDevice = (state: State, device: Object): State => {
if (affectedDevices.length > 0 ) { if (affectedDevices.length > 0 ) {
// replace existing values // replace existing values
const changedDevices: Array<TrezorDevice> = affectedDevices.map(d => mergeDevices(d, { ...device, connected: true} )); // const changedDevices: Array<TrezorDevice> = affectedDevices.map(d => mergeDevices(d, { ...device, connected: true} ));
let cloneInstance: number = 1;
const changedDevices: Array<TrezorDevice> = 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); newState.devices = otherDevices.concat(changedDevices);
} else { } else {
@ -142,12 +190,13 @@ const addDevice = (state: State, device: Object): State => {
acquiring: false, acquiring: false,
remember: false, remember: false,
connected: true, connected: true,
available: true,
path: device.path, path: device.path,
label: device.label, label: device.label,
state: null, state: null,
// instance: 0, // instance: 0,
instanceLabel: device.label, instanceLabel: device.label,
ts: 0, ts: new Date().getTime(),
} }
newState.devices.push(newDevice); newState.devices.push(newDevice);
@ -184,11 +233,15 @@ const changeDevice = (state: State, device: Object): State => {
let affectedDevices: Array<TrezorDevice> = []; let affectedDevices: Array<TrezorDevice> = [];
let otherDevices: Array<TrezorDevice> = []; let otherDevices: Array<TrezorDevice> = [];
if (device.features) { 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); otherDevices = state.devices.filter(d => affectedDevices.indexOf(d) === -1);
} else { } else {
affectedDevices = state.devices.filter(d => d.path === device.path); affectedDevices = state.devices.filter(d => !d.features && d.path === device.path);
otherDevices = state.devices.filter(d => 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) { if (affectedDevices.length > 0) {
@ -226,13 +279,14 @@ const changeDevice = (state: State, device: Object): State => {
const disconnectDevice = (state: State, device: Object): State => { const disconnectDevice = (state: State, device: Object): State => {
const newState: State = { ...state }; const newState: State = { ...state };
const affectedDevices: Array<TrezorDevice> = state.devices.filter(d => d.path === device.path); const affectedDevices: Array<TrezorDevice> = state.devices.filter(d => d.path === device.path || (d.features && device.features && d.features.device_id === device.features.device_id));
const otherDevices: Array<TrezorDevice> = state.devices.filter(d => affectedDevices.indexOf(d) === -1); const otherDevices: Array<TrezorDevice> = state.devices.filter(d => affectedDevices.indexOf(d) === -1);
if (affectedDevices.length > 0) { if (affectedDevices.length > 0) {
const acquiredDevices = affectedDevices.filter(d => !d.unacquired && d.state); const acquiredDevices = affectedDevices.filter(d => !d.unacquired && d.state);
newState.devices = otherDevices.concat( acquiredDevices.map(d => { newState.devices = otherDevices.concat( acquiredDevices.map(d => {
d.connected = false; d.connected = false;
d.available = false;
d.isUsedElsewhere = false; d.isUsedElsewhere = false;
d.featuresNeedsReload = false; d.featuresNeedsReload = false;
d.acquiring = false; d.acquiring = false;
@ -261,7 +315,7 @@ const forgetDevice = (state: State, action: any): State => {
} else { } else {
// remove all instances after disconnect (remember request declined) // 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.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; return newState;
@ -272,6 +326,7 @@ const devicesFromLocalStorage = (devices: Array<any>): Array<TrezorDevice> => {
return { return {
...d, ...d,
connected: false, connected: false,
available: false,
path: '', path: '',
acquiring: false, acquiring: false,
featuresNeedsReload: false, featuresNeedsReload: false,
@ -282,24 +337,33 @@ const devicesFromLocalStorage = (devices: Array<any>): Array<TrezorDevice> => {
const duplicate = (state: State, device: any): State => { const duplicate = (state: State, device: any): State => {
const newState: State = { ...state }; const newState: State = { ...state };
const affectedDevices: Array<TrezorDevice> = state.devices.filter(d => d.path === device.path); const affectedDevices: Array<TrezorDevice> = 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) { // if (affectedDevices.length > 0) {
const newDevice: TrezorDevice = { const newDevice: TrezorDevice = {
...device, ...device,
state: null, acquiring: false,
remember: device.remember, remember: false,
connected: device.connected, connected: device.connected,
available: device.available,
path: device.path, path: device.path,
label: device.label, label: device.label,
instance: new Date().getTime(), state: null,
instanceLabel: device.instanceLabel, instance,
ts: 0, instanceLabel: `${device.label} (${instance})`,
ts: new Date().getTime(),
} }
newState.devices.push(newDevice); newState.devices.push(newDevice);
newState.selectedDevice = { newState.selectedDevice = {
id: newDevice.features.device_id, 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 { 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 : case CONNECT.SELECT_DEVICE :
return { return onSelectDevice(state, action);
...state, // return {
selectedDevice: action.payload // ...state,
} // selectedDevice: action.payload
// }
case CONNECT.INITIALIZATION_ERROR : case CONNECT.INITIALIZATION_ERROR :
return { return {
@ -378,7 +456,7 @@ export default function connect(state: State = initialState, action: any): any {
return disconnectDevice(state, action.device); return disconnectDevice(state, action.device);
case DEVICE.CHANGED : case DEVICE.CHANGED :
return changeDevice(state, { ...action.device, connected: true }); return changeDevice(state, { ...action.device, connected: true, available: true });
default: default:
return state; return state;

@ -1,25 +1,43 @@
/* @flow */ /* @flow */
'use strict'; 'use strict';
import { ON_RESIZE, TOGGLE_DEVICE_DROPDOWN, RESIZE_CONTAINER } from '../actions/AppActions';
import * as WEB3 from '../actions/constants/Web3'; import * as WEB3 from '../actions/constants/Web3';
import * as WALLET from '../actions/constants/wallet';
const WIDTH: number = 1080;
const HEIGHT: number = 1920;
type State = { type State = {
network: string; ready: boolean;
device: string; dropdownOpened: boolean;
initialUrl: boolean;
} }
const initialState: Object = { const initialState: Object = {
ready: false,
dropdownOpened: false,
initialParams: null,
initialPathname: null,
}; };
export default function wallet(state: Object = initialState, action: Object): any { export default function wallet(state: Object = initialState, action: Object): any {
switch(action.type) { 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: default:
return state; return state;

@ -4,7 +4,6 @@
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux'; import { routerReducer } from 'react-router-redux';
import DOM from './AppReducer.js';
import log from './LogReducer.js'; import log from './LogReducer.js';
import localStorage from './LocalStorageReducer.js'; import localStorage from './LocalStorageReducer.js';
import connect from './TrezorConnectReducer.js'; import connect from './TrezorConnectReducer.js';
@ -12,7 +11,7 @@ import notifications from './NotificationReducer.js';
import modal from './ModalReducer.js'; import modal from './ModalReducer.js';
import web3 from './Web3Reducer.js'; import web3 from './Web3Reducer.js';
import accounts from './AccountsReducer.js'; import accounts from './AccountsReducer.js';
import accountDetail from './AccountDetailReducer.js'; import abstractAccount from './AbstractAccountReducer.js';
import sendForm from './SendFormReducer.js'; import sendForm from './SendFormReducer.js';
import receive from './ReceiveReducer.js'; import receive from './ReceiveReducer.js';
import summary from './SummaryReducer.js'; import summary from './SummaryReducer.js';
@ -20,10 +19,10 @@ import tokens from './TokensReducer.js';
import discovery from './DiscoveryReducer.js'; import discovery from './DiscoveryReducer.js';
import pending from './PendingTxReducer.js'; import pending from './PendingTxReducer.js';
import fiat from './FiatRateReducer.js'; import fiat from './FiatRateReducer.js';
import wallet from './WalletReducer.js';
export default combineReducers({ export default combineReducers({
router: routerReducer, router: routerReducer,
DOM,
log, log,
localStorage, localStorage,
connect, connect,
@ -31,12 +30,13 @@ export default combineReducers({
modal, modal,
web3, web3,
accounts, accounts,
accountDetail, abstractAccount,
sendForm, sendForm,
receive, receive,
summary, summary,
tokens, tokens,
discovery, discovery,
pending, pending,
fiat fiat,
wallet
}); });

@ -7,23 +7,18 @@ import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux'; import { ConnectedRouter } from 'react-router-redux';
import store, { history } from '../store'; import store, { history } from '../store';
import { import LandingPageContainer from '../components/landing';
LandingPageContainer, import WalletContainer from '../components/wallet';
WalletContainer, import BootloaderContainer from '../components/wallet/Bootloader';
import AcquireContainer from '../components/wallet/Acquire';
AcquireContainer,
BootloaderContainer, import DashboardContainer from '../components/wallet/Dashboard';
import SummaryContainer from '../components/wallet/summary';
DashboardContainer, import SendFormContainer from '../components/wallet/send';
import ReceiveContainer from '../components/wallet/Receive';
HistoryContainer, import SignVerifyContainer from '../components/wallet/SignVerify';
SendFormContainer, import DeviceSettingsContainer from '../components/wallet/DeviceSettings';
ReceiveContainer, import WalletSettingsContainer from '../components/wallet/WalletSettings';
SignVerifyContainer,
SettingsContainer,
} from '../containers';
import SummaryContainer from '../components/wallet/summary/SummaryContainer';
export default ( export default (
<Provider store={ store }> <Provider store={ store }>
@ -34,20 +29,19 @@ export default (
<Route exact path="/import" component={ LandingPageContainer } /> <Route exact path="/import" component={ LandingPageContainer } />
<Route> <Route>
<WalletContainer> <WalletContainer>
<Route exact path="/settings" component={ WalletSettingsContainer } />
<Route exact path="/device/:device/" component={ DashboardContainer } /> <Route exact path="/device/:device/" component={ DashboardContainer } />
<Route exact path="/device/:device/network/:network" component={ DashboardContainer } /> <Route exact path="/device/:device/network/:network" component={ DashboardContainer } />
<Route exact path="/device/:device/acquire" component={ AcquireContainer } /> <Route exact path="/device/:device/acquire" component={ AcquireContainer } />
<Route exact path="/device/:device/bootloader" component={ BootloaderContainer } /> <Route exact path="/device/:device/bootloader" component={ BootloaderContainer } />
<Route exact path="/device/:device/settings" component={ DeviceSettingsContainer } />
<Route exact path="/device/:device/network/:network/address/:address" component={ SummaryContainer } /> <Route exact path="/device/:device/network/:network/address/:address" component={ SummaryContainer } />
<Route path="/device/:device/network/:network/address/:address/send" component={ SendFormContainer } /> <Route path="/device/:device/network/:network/address/:address/send" component={ SendFormContainer } />
<Route path="/device/:device/network/:network/address/:address/send/override" component={ SendFormContainer } /> <Route path="/device/:device/network/:network/address/:address/send/override" component={ SendFormContainer } />
<Route path="/device/:device/network/:network/address/:address/receive" component={ ReceiveContainer } /> <Route path="/device/:device/network/:network/address/:address/receive" component={ ReceiveContainer } />
<Route path="/device/:device/network/:network/address/:address/signverify" component={ SignVerifyContainer } /> <Route path="/device/:device/network/:network/address/:address/signverify" component={ SignVerifyContainer } />
{/* <Route path="/device/:device/address/:address/history" component={ HistoryContainer } /> */}
</WalletContainer> </WalletContainer>
</Route> </Route>
<Route path="/settings" component={ SettingsContainer } />
</Switch> </Switch>
</ConnectedRouter> </ConnectedRouter>
</Provider> </Provider>

@ -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<Array<any>> => {
const json = await httpRequest(`${API}&address=${address}`);
return json;
}
const getTransactionStatus = async (txid: string): Promise<Array<any>> => {
//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<any> => {
// https://ropsten.etherscan.io/apis#logs
return async (dispatch, getState): Promise<any> => {
const tkn = '0x58cda554935e4a1f2acbe15f8757400af275e084';
const ad = '0x00000000000000000000000098ead4bd2fbbb0cf0b49459aa0510ef53faa6cad';
const incoming = await getTokenHistory(tkn, ad);
console.log("TOKEN HIST!", JSON.parse(incoming) );
}
}
export const loadTransactionStatus = (txid): Promise<any> => {
return async (dispatch, getState): Promise<any> => {
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;

@ -111,6 +111,7 @@ const LocalStorageService = (store: any) => (next: any) => (action: any) => {
case DEVICE.CHANGED : case DEVICE.CHANGED :
case DEVICE.DISCONNECT : case DEVICE.DISCONNECT :
case CONNECT.AUTH_DEVICE : case CONNECT.AUTH_DEVICE :
case CONNECT.SELECT_DEVICE :
save(store.dispatch, store.getState); 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('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 ) ) ); // store.dispatch( LocalStorageActions.save('selectedDevice', JSON.stringify( store.getState().connect.selectedDevice ) ) );

@ -4,8 +4,9 @@
import pathToRegexp from 'path-to-regexp'; import pathToRegexp from 'path-to-regexp';
import { DEVICE } from 'trezor-connect'; import { DEVICE } from 'trezor-connect';
import { LOCATION_CHANGE, push, replace } from 'react-router-redux'; 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 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. * 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) => { const RouterService = (store: any) => (next: any) => (action: any) => {
if (action.type === ON_BEFORE_UNLOAD) { if (action.type === WALLET.ON_BEFORE_UNLOAD) {
__unloading = true; __unloading = true;
} else if (action.type === LOCATION_CHANGE && !__unloading) { } else if (action.type === LOCATION_CHANGE && !__unloading) {
const { location } = store.getState().router; const { location } = store.getState().router;
const web3 = store.getState().web3; const web3 = store.getState().web3;
const { devices, error } = store.getState().connect; const { devices, error } = store.getState().connect;
const { opened } = store.getState().modal; const isModalOpened: boolean = store.getState().modal.opened;
let redirectPath: ?string; let redirectPath: ?string;
// first (initial) event after app loads // first event after application loads
if (!location) { if (!location) {
action.payload.state = { store.dispatch({
initURL: action.payload.pathname, type: WALLET.SET_INITIAL_URL,
initSearch: action.payload.search pathname: action.payload.pathname,
} params: pathToParams(action.payload.pathname)
});
// check if there are initial parameters in url (network/ device / account)
if (action.payload.search.length > 0) { if (action.payload.search.length > 0) {
// save it in WalletReducer, after device detection will redirect to this request // save it in WalletReducer, after device detection will redirect to this request
redirectPath = '/'; 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 // 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; 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; redirectPath = location.pathname;
console.warn("Modal still opened"); console.warn("Modal still opened");
} else if (landingPage) { } else if (landingPage) {
@ -113,7 +114,7 @@ const RouterService = (store: any) => (next: any) => (action: any) => {
// TODO: switch to first device? // TODO: switch to first device?
// redirectPath = `/device/${ devices[0].path }`; // redirectPath = `/device/${ devices[0].path }`;
redirectPath = location.pathname; redirectPath = location.pathname;
} else { } else if (requestedParams.device) {
if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) { if (currentParams.device !== requestedParams.device || currentParams.deviceInstance !== requestedParams.deviceInstance) {
store.dispatch({ store.dispatch({
@ -147,6 +148,7 @@ const RouterService = (store: any) => (next: any) => (action: any) => {
action.payload.params = requestedParams; action.payload.params = requestedParams;
} }
store.dispatch( NotificationActions.clear(currentParams, requestedParams) );
} }
// Pass all actions through by default // Pass all actions through by default

@ -11,13 +11,14 @@ import * as WEB3 from '../actions/constants/Web3';
import * as STORAGE from '../actions/constants/LocalStorage'; import * as STORAGE from '../actions/constants/LocalStorage';
import * as CONNECT from '../actions/constants/TrezorConnect'; import * as CONNECT from '../actions/constants/TrezorConnect';
import * as NOTIFICATION from '../actions/constants/notification'; import * as 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 TrezorConnectService = (store: any) => (next: any) => (action: any) => {
const prevState = store.getState().connect; const prevState = store.getState().connect;
const prevModalState = store.getState().modal; const prevModalState = store.getState().modal;
const prevRouterState = store.getState().router;
next(action); next(action);
@ -25,8 +26,14 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => {
store.dispatch( TrezorConnectActions.init() ); store.dispatch( TrezorConnectActions.init() );
} else if (action.type === TRANSPORT.ERROR) { } else if (action.type === TRANSPORT.ERROR) {
store.dispatch( push('/') ); // TODO: check if modal is open
} else if (action.type === TRANSPORT.START && store.getState().web3.length > 0 && prevState.devices.length > 0) { // 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() ); store.dispatch( TrezorConnectActions.postInit() );
} else if (action.type === TRANSPORT.UNREADABLE) { } else if (action.type === TRANSPORT.UNREADABLE) {
store.dispatch({ store.dispatch({
@ -34,22 +41,16 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => {
payload: { payload: {
type: 'error', type: 'error',
title: 'Unreadable HID device', title: 'Unreadable HID device',
message: 'What to do?', message: '',
cancelable: true, cancelable: true,
} }
}) });
} else if (action.type === WEB3.READY) {
store.dispatch( TrezorConnectActions.postInit() );
} else if (action.type === DEVICE.DISCONNECT) { } else if (action.type === DEVICE.DISCONNECT) {
store.dispatch( TrezorConnectActions.deviceDisconnect(action.device) ); store.dispatch( TrezorConnectActions.deviceDisconnect(action.device) );
} else if (action.type === CONNECT.REMEMBER_REQUEST) { } else if (action.type === CONNECT.REMEMBER_REQUEST) {
// TODO: // TODO: 2 modals at once
if (prevModalState.opened && prevModalState.windowType === CONNECT.REMEMBER_REQUEST) { if (prevModalState.opened && prevModalState.windowType === CONNECT.REMEMBER_REQUEST) {
store.dispatch({ store.dispatch({
type: CONNECT.FORGET, type: CONNECT.FORGET,
device: store.getState().modal.device device: store.getState().modal.device
@ -82,6 +83,7 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => {
// we need to change route // we need to change route
if (prevState.selectedDevice) { if (prevState.selectedDevice) {
if (!action.device.unacquired && action.device.path === prevState.selectedDevice.id) { if (!action.device.unacquired && action.device.path === prevState.selectedDevice.id) {
console.warn("TODO: here! better")
store.dispatch( TrezorConnectActions.onSelectDevice(action.device) ); 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 (modal.opened && modal.windowType === CONNECT.REMEMBER_REQUEST) {
if (action.device.features && modal.device.features.device_id === action.device.features.device_id) { if (action.device.features && modal.device.features.device_id === action.device.features.device_id) {
store.dispatch({ store.dispatch({
type: ACTIONS.CLOSE_MODAL, type: MODAL.CLOSE,
}); });
} else { } else {
store.dispatch({ store.dispatch({
@ -113,6 +115,7 @@ const TrezorConnectService = (store: any) => (next: any) => (action: any) => {
store.dispatch( TrezorConnectActions.onDuplicateDevice() ); store.dispatch( TrezorConnectActions.onDuplicateDevice() );
} else if (action.type === DEVICE.ACQUIRED || action.type === CONNECT.SELECT_DEVICE) { } else if (action.type === DEVICE.ACQUIRED || action.type === CONNECT.SELECT_DEVICE) {
console.warn("here!!!!!", action.type)
store.dispatch( TrezorConnectActions.getSelectedDeviceState() ); store.dispatch( TrezorConnectActions.getSelectedDeviceState() );
} else if (action.type === CONNECT.COIN_CHANGED) { } else if (action.type === CONNECT.COIN_CHANGED) {

@ -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<BigNumber> => {
// 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<any> => {
return new Promise((resolve, reject) => {
web3.eth.estimateGas(gasOptions, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
})
}

@ -5,14 +5,10 @@ import RouterService from './RouterService';
import LocalStorageService from './LocalStorageService'; import LocalStorageService from './LocalStorageService';
import CoinmarketcapService from './CoinmarketcapService'; import CoinmarketcapService from './CoinmarketcapService';
import TrezorConnectService from './TrezorConnectService'; import TrezorConnectService from './TrezorConnectService';
import Web3Service from './Web3Service';
import EtherscanService from './EtherscanService';
export default [ export default [
RouterService, RouterService,
LocalStorageService, LocalStorageService,
TrezorConnectService, TrezorConnectService,
Web3Service,
CoinmarketcapService, CoinmarketcapService,
//EtherscanService,
]; ];

@ -10,3 +10,8 @@ export const getAccounts = (accounts: Array<any>, device: any, network: ?string)
} }
} }
// Public method used in components to find device by state and device_id
export const findDevice = (devices: Array<TrezorDevice>, state: ?string, deviceId: ?string, instance: ?string): ?TrezorDevice => {
return devices.find(d => d.state === state && d.features && d.features.device_id === deviceId && d.instance === instance);
}

@ -95,6 +95,8 @@ aside {
margin: 12px 0px 12px 80px; margin: 12px 0px 12px 80px;
} }
.device-select { .device-select {
background: @color_white; background: @color_white;
border-bottom: 1px solid @color_divider; 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 { a {
position: relative; position: relative;
display: block; display: block;
@ -391,12 +443,12 @@ aside {
} }
&.ropsten:before, &.ropsten:before,
&.eth:before { &.ethereum:before {
background-image: url('../images/eth-logo.png'); background-image: url('../images/eth-logo.png');
background-size: auto 20px; background-size: auto 20px;
} }
&.rinkeby:before, &.rinkeby:before,
&.etc:before { &.ethereum-classic:before {
background-image: url('../images/etc-logo.png'); background-image: url('../images/etc-logo.png');
background-size: auto 20px; background-size: auto 20px;
} }

@ -169,5 +169,6 @@
.icomoon-download { .icomoon-download {
.icomoon-base(); .icomoon-base();
content: "\e91f"; content: "\e91b";
transform: rotate(180deg)
} }

@ -180,7 +180,7 @@
justify-content: space-between; justify-content: space-between;
padding: 36px 0px; padding: 36px 0px;
margin: 0 auto; margin: 0 auto;
width: 420px; // width: 420px;
p { p {
// flex: 1; // flex: 1;
@ -197,6 +197,18 @@
// color: @color_green_secondary; // color: @color_green_secondary;
// } // }
// } // }
.webusb-and,
.trezor-webusb-button {
display: none;
}
&.webusb {
width: 400px;
.webusb-and,
.trezor-webusb-button {
display: block;
}
}
} }
p { p {

@ -108,6 +108,7 @@
} }
} }
.duplicate,
.remember { .remember {
width: 360px; width: 360px;
padding: 24px 48px; padding: 24px 48px;
@ -138,6 +139,7 @@
} }
} }
.close-modal { .close-modal {
position: absolute; position: absolute;
top: 0; top: 0;

@ -190,20 +190,23 @@
.advanced-container { .advanced-container {
display: flex; display: flex;
flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
padding: 0px 48px; padding: 0px 48px;
padding-bottom: 24px; padding-bottom: 24px;
button { button {
width: 50%; min-width: 50%;
white-space: nowrap;
} }
&.opened { &.opened {
flex-direction: column; flex-direction: column;
padding: 0px; padding: 0px;
button { button {
position: relative; position: relative;
left: 50%; // left: 50%;
width: 50%; // width: 50%;
float: right; // TODO: better
} }
.advanced { .advanced {
display: inline-block; display: inline-block;
@ -232,6 +235,12 @@
} }
} }
// @media screen and (max-width: 900px) {
// :not(.opened) {
// border: 1px solid red;
// }
// }
} }
.gas-row { .gas-row {

@ -13,12 +13,12 @@
} }
&.ropsten:before, &.ropsten:before,
&.eth:before { &.ethereum:before {
background-image: url('../images/eth-logo.png'); background-image: url('../images/eth-logo.png');
background-size: auto 20px; background-size: auto 20px;
} }
&.rinkeby:before, &.rinkeby:before,
&.etc:before { &.ethereum-classic:before {
background-image: url('../images/etc-logo.png'); background-image: url('../images/etc-logo.png');
background-size: auto 20px; background-size: auto 20px;
} }

Loading…
Cancel
Save