diff --git a/src/js/reducers/TrezorConnectReducer.js b/src/js/reducers/TrezorConnectReducer.js index f2d5316c..f948e210 100644 --- a/src/js/reducers/TrezorConnectReducer.js +++ b/src/js/reducers/TrezorConnectReducer.js @@ -14,8 +14,8 @@ export type SelectedDevice = { } export type State = { - devices: Array; - selectedDevice: ?SelectedDevice; + // devices: Array; + // selectedDevice: ?SelectedDevice; discoveryComplete: boolean; error: ?string; transport: ?{ @@ -34,330 +34,14 @@ export type State = { const initialState: State = { - devices: [], - selectedDevice: null, + // devices: [], + //selectedDevice: null, discoveryComplete: false, error: null, transport: null, browserState: {} }; -export const findSelectedDevice = (state: State): ?TrezorDevice => { - const selected: ?SelectedDevice = state.selectedDevice; - if (!selected) return null; - - return state.devices.find(d => { - if (d.unacquired && d.path === selected.id) { - return true; - } else if (d.features && d.features.bootloader_mode && d.path === selected.id) { - return true; - } else if (d.features && d.features.device_id === selected.id && d.instance === selected.instance) { - return true; - } - return false; - }); -} - -export const findDevice = (devices: Array, deviceId: string, deviceState: string, instance: ?number): ?TrezorDevice => { - return devices.find(d => { - // TODO: && (instance && d.instance === instance) - if (d.features && d.features.device_id === deviceId && d.state === deviceState) { - return true; - } - return false; - }); -} - - - -export const isSavedDevice = (state: State, device: any): ?Array => { - const selected: ?SelectedDevice = state.selectedDevice; - if (!selected) return null; - - if (!device || !device.features) return null; - - return state.devices.filter(d => { - if (d.features && d.features.device_id === device.features.device_id){ - return d; - } - return null; - }); -} - -export const getNewInstance = (devices: Array, device: Device | TrezorDevice): number => { - - const affectedDevices: Array = devices.filter(d => d.features && device.features && d.features.device_id === device.features.device_id) - .sort((a, b) => { - if (!a.instance) { - return -1; - } else { - return !b.instance || a.instance > b.instance ? 1 : -1; - } - }); - - const instance: number = affectedDevices.reduce((inst, dev) => { - return dev.instance ? dev.instance + 1 : inst + 1; - }, 0); - - return instance; -} - -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; - // } - // } - - let instanceLabel = current.instanceLabel; - if (upcoming.label !== current.label) { - instanceLabel = upcoming.label - if (typeof current.instance === 'number') { - instanceLabel += ` (${current.instanceName || current.instance})`; - } - } - - const dev: TrezorDevice = { - // ...current, - ...upcoming, - // make sure that instance specific variables will not be overridden - connected: typeof upcoming.connected === 'boolean' ? upcoming.connected : current.connected, - available: typeof upcoming.available === 'boolean' ? upcoming.available : current.available, - remember: typeof upcoming.remember === 'boolean' ? upcoming.remember : current.remember, - instance: current.instance, - instanceLabel, - instanceName: typeof upcoming.instanceName === 'string' ? upcoming.instanceName : current.instanceName, - state: current.state, - ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts, - } - // corner-case: trying to merge unacquired device with acquired - // make sure that sensitive fields will not be changed and device will remain acquired - if (upcoming.unacquired && current.state) { - dev.unacquired = false; - dev.features = current.features; - dev.label = current.label; - } - - return dev; -} - -const addDevice = (state: State, device: Device): State => { - - const newState: State = { ...state }; - - let affectedDevices: Array = []; - let otherDevices: Array = []; - if (!device.features) { - // check if connected device is unacquired, and it's already exists - affectedDevices = newState.devices.filter(d => d.path === device.path); - // if so, ignore this action - if (affectedDevices.length > 0) { - return state; - } - otherDevices = newState.devices.filter(d => affectedDevices.indexOf(d) === -1); - } else { - affectedDevices = newState.devices.filter(d => d.features && d.features.device_id === device.features.device_id); - const unacquiredDevices = newState.devices.filter(d => d.path.length > 0 && d.path === device.path); - otherDevices = newState.devices.filter(d => affectedDevices.indexOf(d) < 0 && unacquiredDevices.indexOf(d) < 0); - } - - if (affectedDevices.length > 0 ) { - // check if freshly added device has different "passphrase_protection" settings - let cloneInstance: number = 1; - let hasDifferentPassphraseSettings: boolean = false; - let hasInstancesWithPassphraseSettings: boolean = false; - const changedDevices: Array = affectedDevices.map(d => { - if (d.features && d.features.passphrase_protection === device.features.passphrase_protection) { - cloneInstance = 0; - hasInstancesWithPassphraseSettings = true; - return mergeDevices(d, { ...device, connected: true, available: true } ); - } else { - hasDifferentPassphraseSettings = true; - if (d.instance && cloneInstance > 0) { - cloneInstance = d.instance + 1; - } - return mergeDevices(d, { ...d, connected: true, available: false } ); - // return d; - } - }); - - // edge case: freshly connected device has different "passphrase_protection" than saved instances - // need to automatically create another instance with default instance name - if (hasDifferentPassphraseSettings && !hasInstancesWithPassphraseSettings) { - // TODO: instance should be calculated form affectedDevice - // const instance = cloneInstance; //new Date().getTime(); - const instance = getNewInstance(affectedDevices, device); - - 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})`, - instanceName: null, - ts: new Date().getTime(), - } - changedDevices.push(newDevice); - } - - newState.devices = otherDevices.concat(changedDevices); - - } else { - const newDevice: TrezorDevice = { - ...device, - acquiring: false, - remember: false, - connected: true, - available: true, - path: device.path, - label: device.label, - state: null, - instanceLabel: device.label, - instanceName: null, - ts: new Date().getTime(), - } - // newState.devices.push(newDevice); - newState.devices = otherDevices.concat([newDevice]); - } - - return newState; -} - -const setDeviceState = (state: State, device: TrezorDevice, deviceState: string): State => { - const newState: State = { ...state }; - const affectedDevice: ?TrezorDevice = state.devices.find(d => d.path === device.path && d.instance === device.instance); - // device could already have own state from firmware, do not override it - if (affectedDevice && !affectedDevice.state) { - const otherDevices: Array = state.devices.filter(d => d !== affectedDevice); - affectedDevice.state = deviceState; - newState.devices = otherDevices.concat([affectedDevice]); - } - return newState; -} - -const changeDevice = (state: State, device: Device): State => { - - // change only acquired devices - if (!device.features) return state; - - const affectedDevices: Array = state.devices.filter(d => - (d.features && d.features.device_id === device.features.device_id && d.features.passphrase_protection === device.features.passphrase_protection) || - (d.features && d.path.length > 0 && d.path === device.path) - ); - const otherDevices: Array = state.devices.filter(d => affectedDevices.indexOf(d) === -1); - - if (affectedDevices.length > 0) { - const newState: State = { ...state }; - const changedDevices = affectedDevices.map(d => mergeDevices(d, device)); - newState.devices = otherDevices.concat(changedDevices); - return newState; - } - - return state; -} - -const disconnectDevice = (state: State, device: Device): State => { - - const newState: State = { ...state }; - const affectedDevices: Array = state.devices.filter(d => d.path === device.path || (d.features && device.features && d.features.device_id === device.features.device_id)); - const otherDevices: Array = state.devices.filter(d => affectedDevices.indexOf(d) === -1); - - if (affectedDevices.length > 0) { - const acquiredDevices = affectedDevices.filter(d => !d.unacquired && d.state); - newState.devices = otherDevices.concat( acquiredDevices.map(d => { - d.connected = false; - d.available = false; - d.isUsedElsewhere = false; - d.featuresNeedsReload = false; - d.path = ''; - return d; - })); - } - - // selected device was removed and forgotten - // clear this field - const selected = findSelectedDevice(newState); - if (!selected) { - newState.selectedDevice = null; - } - - return newState; -} - -const forgetDevice = (state: State, action: any): State => { - const newState: State = { ...state }; - - if (action.type === CONNECT.FORGET_SINGLE) { - // remove only one instance (called from Aside button) - newState.devices.splice(newState.devices.indexOf(action.device), 1); - } else { - // remove all instances after disconnect (remember request declined) - //newState.devices = state.devices.filter(d => d.path !== action.device.path); - newState.devices = state.devices.filter(d => d.remember || (d.features && d.features.device_id !== action.device.features.device_id) || (!d.features && d.path !== action.device.path)); - } - - return newState; -} - -const devicesFromLocalStorage = (devices: Array): Array => { - return devices.map(d => { - return { - ...d, - connected: false, - available: false, - path: '', - acquiring: false, - featuresNeedsReload: false, - isUsedElsewhere: false - } - }); -} - -const duplicate = (state: State, device: TrezorDevice): State => { - const newState: State = { ...state }; - - const instance: number = getNewInstance(state.devices, device); - - const newDevice: TrezorDevice = { - ...device, - // acquiring: false, - remember: false, - // connected: device.connected, - // available: device.available, - // path: device.path, - // label: device.label, - state: null, - // instance, - // instanceLabel: `${device.label} (${instance})`, - instanceLabel: `${device.label} (${ device.instanceName || instance })`, - ts: new Date().getTime(), - } - newState.devices.push(newDevice); - newState.selectedDevice = { - id: newDevice.features ? newDevice.features.device_id : '-empty-', - instance - } - - return newState; -} - - -const onSelectedDevice = (state: State, device: ?TrezorDevice): State => { - const newState: State = { ...state }; - if (device) { - const otherDevices: Array = state.devices.filter(d => d !== device); - newState.devices = otherDevices.concat([ { ...device, ts: new Date().getTime() } ]); - } - return newState; -} export default function connect(state: State = initialState, action: Action): State { @@ -371,12 +55,6 @@ export default function connect(state: State = initialState, action: Action): St browserState: action.payload.browser } - case CONNECT.DUPLICATE : - return duplicate(state, action.device); - - - case WALLET.SET_SELECTED_DEVICE : - return onSelectedDevice(state, action.device); case CONNECT.INITIALIZATION_ERROR : return { @@ -399,31 +77,6 @@ export default function connect(state: State = initialState, action: Action): St transport: null, }; - case CONNECT.DEVICE_FROM_STORAGE : - return { - ...state, - devices: devicesFromLocalStorage(action.payload), - } - - case CONNECT.AUTH_DEVICE : - return setDeviceState(state, action.device, action.state); - case CONNECT.REMEMBER : - return changeDevice(state, { ...action.device, path: '', remember: true } ); - - case CONNECT.FORGET : - case CONNECT.FORGET_SINGLE : - return forgetDevice(state, action); - - case DEVICE.CONNECT : - case DEVICE.CONNECT_UNACQUIRED : - return addDevice(state, action.device); - case DEVICE.CHANGED : - return changeDevice(state, { ...action.device, connected: true, available: true }); // TODO: check if available will propagate to unavailable - case DEVICE.DISCONNECT : - case DEVICE.DISCONNECT_UNACQUIRED : - return disconnectDevice(state, action.device); - - default: return state;