Send
diff --git a/src/js/components/modal/DuplicateDevice.js b/src/js/components/modal/DuplicateDevice.js
index 3db3375f..15bbd345 100644
--- a/src/js/components/modal/DuplicateDevice.js
+++ b/src/js/components/modal/DuplicateDevice.js
@@ -1,21 +1,63 @@
/* @flow */
'use strict';
-import React from 'react';
+import React, { Component } from 'react';
+import { getNewInstance } from '../../reducers/TrezorConnectReducer'
import type { Props } from './index';
-const RememberDevice = (props: Props) => {
- if (!props.modal.opened) return null;
- const { device } = props.modal;
- const { onCancel, onDuplicateDevice } = props.modalActions;
- return (
-
-
Clone { device.instanceLabel }?
-
This will create new instance of device which can be used with different passphrase
-
onDuplicateDevice( { ...device, instanceLabel: "TODO: user label from input" } ) }>Create new instance
-
Cancel
-
- );
+type State = {
+ defaultName: string;
+ instanceName: ?string;
}
-export default RememberDevice;
\ No newline at end of file
+export default class DuplicateDevice extends Component
{
+
+ state: State;
+
+ constructor(props: Props) {
+ super(props);
+
+ const device = props.modal.opened ? props.modal.device : null;
+ if (!device) return;
+
+ const instance = getNewInstance(props.connect.devices, device);
+
+ this.state = {
+ defaultName: `${device.label} (${instance.toString()})`,
+ instanceName: null
+ }
+ }
+
+ onNameChange = (value: string): void => {
+ this.setState({
+ instanceName: value.length > 0 ? value : null
+ });
+ }
+
+ render() {
+
+ if (!this.props.modal.opened) return null;
+
+ const { device } = this.props.modal;
+ const { onCancel, onDuplicateDevice } = this.props.modalActions;
+
+ return (
+
+
Clone { device.label }?
+
This will create new instance of device which can be used with different passphrase
+
Instance name
+
this.onNameChange(event.currentTarget.value) }
+ defaultValue={ this.state.instanceName } />
+
onDuplicateDevice( { ...device, instanceName: this.state.instanceName } ) }>Create new instance
+
Cancel
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/js/components/modal/InvalidPin.js b/src/js/components/modal/InvalidPin.js
index bd313be5..ae8b1005 100644
--- a/src/js/components/modal/InvalidPin.js
+++ b/src/js/components/modal/InvalidPin.js
@@ -10,7 +10,7 @@ const InvalidPin = (props: Props) => {
const { device } = props.modal;
return (
-
Entered PIN for { device.label } is not correct.
+
Entered PIN for { device.label } is not correct
Retrying...
);
diff --git a/src/js/components/modal/PassphraseType.js b/src/js/components/modal/PassphraseType.js
index 0d2a0f1d..0cd2dbf8 100644
--- a/src/js/components/modal/PassphraseType.js
+++ b/src/js/components/modal/PassphraseType.js
@@ -2,12 +2,17 @@
'use strict';
import React from 'react';
+import type { Props } from './index';
+
+const Confirmation = (props: Props) => {
+
+ if (!props.modal.opened) return null;
+ const { device } = props.modal;
-const Confirmation = () => {
return (
-
Complete the action on your TREZOR device
+ Complete the action on { device.label } device
);
diff --git a/src/js/components/modal/RememberDevice.js b/src/js/components/modal/RememberDevice.js
index 4762fa95..ca55af92 100644
--- a/src/js/components/modal/RememberDevice.js
+++ b/src/js/components/modal/RememberDevice.js
@@ -67,16 +67,15 @@ export default class RememberDevice extends Component {
const { onForgetDevice, onRememberDevice } = this.props.modalActions;
let label = device.label;
- let devicePlural: string = "device or to remember it";
- if (instances && instances.length > 1) {
+ const devicePlural: string = instances && instances.length > 1 ? "devices or to remember them" : "device or to remember it";
+ if (instances && instances.length > 0) {
label = instances.map((instance, index) => {
let comma: string = '';
if (index > 0) comma = ', ';
return (
{ comma }{ instance.instanceLabel }
);
- })
- devicePlural = "devices or to remember them";
+ });
}
return (
diff --git a/src/js/flowtype/index.js b/src/js/flowtype/index.js
index a285e417..4210c5b9 100644
--- a/src/js/flowtype/index.js
+++ b/src/js/flowtype/index.js
@@ -51,6 +51,7 @@ export type TrezorDevice = {
state: ?string;
instance?: number;
instanceLabel: string;
+ instanceName: ?string;
features?: Features;
unacquired?: boolean;
acquiring: boolean;
diff --git a/src/js/reducers/TrezorConnectReducer.js b/src/js/reducers/TrezorConnectReducer.js
index 657e0aed..e7426b8f 100644
--- a/src/js/reducers/TrezorConnectReducer.js
+++ b/src/js/reducers/TrezorConnectReducer.js
@@ -81,6 +81,24 @@ export const isSavedDevice = (state: State, device: any): ?Array
=
});
}
+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
@@ -95,7 +113,7 @@ const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice =>
if (upcoming.label !== current.label) {
instanceLabel = upcoming.label
if (typeof current.instance === 'number') {
- instanceLabel += ` (${current.instance})`;
+ instanceLabel += ` (${current.instanceName || current.instance})`;
}
}
@@ -112,21 +130,14 @@ const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice =>
acquiring: typeof upcoming.acquiring === 'boolean' ? upcoming.acquiring : current.acquiring,
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.instanceLabel = current.instanceLabel;
+ dev.unacquired = false;
dev.features = current.features;
dev.label = current.label;
- dev.unacquired = false;
- } else if (!upcoming.unacquired && current.unacquired) {
- // dev.instanceLabel = upcoming.label;
- // if (typeof dev.instance === 'number') {
- // dev.instanceLabel = `${upcoming.label} TODO:(${dev.instance})`;
- // }
}
-
-
return dev;
}
@@ -151,14 +162,17 @@ const addDevice = (state: State, device: Device): State => {
}
if (affectedDevices.length > 0 ) {
- // replace existing values
- // const changedDevices: Array = affectedDevices.map(d => mergeDevices(d, { ...device, connected: true} ));
+ // 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;
}
@@ -167,9 +181,12 @@ const addDevice = (state: State, device: Device): State => {
}
});
- if (cloneInstance > 0) {
+ // 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 = cloneInstance; //new Date().getTime();
+ const instance = getNewInstance(affectedDevices, device);
const newDevice: TrezorDevice = {
...device,
@@ -182,14 +199,15 @@ const addDevice = (state: State, device: Device): State => {
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,
@@ -199,14 +217,11 @@ const addDevice = (state: State, device: Device): State => {
path: device.path,
label: device.label,
state: null,
- // instance: 0,
instanceLabel: device.label,
+ instanceName: null,
ts: new Date().getTime(),
}
newState.devices.push(newDevice);
-
- // const clone = { ...newDevice, instance: 1, instanceLabel: device.label + '#1' };
- // newState.devices.push(clone);
}
return newState;
@@ -251,9 +266,15 @@ const changeDevice = (state: State, device: Object): State => {
if (affectedDevices.length > 0) {
const isAffectedUnacquired: number = affectedDevices.findIndex(d => d.unacquired);
- if (isAffectedUnacquired >= 0 && affectedDevices.length > 1){
- affectedDevices.splice(isAffectedUnacquired, 1);
- }
+ // if (isAffectedUnacquired >= 0 && affectedDevices.length > 1){
+ if (isAffectedUnacquired >= 0){
+ // TODO: should unacquired device be removed? or merged?
+ //affectedDevices.splice(isAffectedUnacquired, 1);
+ } else {
+ // replace existing values
+ const changedDevices: Array = affectedDevices.map(d => mergeDevices(d, device));
+ newState.devices = otherDevices.concat(changedDevices);
+ }
// else if (isAffectedUnacquired >= 0 && !device.unacquired && affectedDevices.length > 1) {
// affectedDevices.splice(isAffectedUnacquired, 1);
@@ -263,12 +284,6 @@ const changeDevice = (state: State, device: Object): State => {
if (state.selectedDevice && device.path === state.selectedDevice.id && affectedDevices.length > 1) {
// affectedDevices = affectedDevices.filter(d => d.path !== state.selectedDevice.id && d.features);
}
-
-
-
- // replace existing values
- const changedDevices: Array = affectedDevices.map(d => mergeDevices(d, device));
- newState.devices = otherDevices.concat(changedDevices);
}
return newState;
@@ -336,30 +351,33 @@ const devicesFromLocalStorage = (devices: Array): Array => {
const duplicate = (state: State, device: TrezorDevice): State => {
const newState: State = { ...state };
- const affectedDevices: Array = state.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 affectedDevices: Array = state.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);
+ // const instance: number = affectedDevices.reduce((inst, dev) => {
+ // return dev.instance ? dev.instance + 1 : inst + 1;
+ // }, 0);
+
+ const instance: number = getNewInstance(state.devices, device);
const newDevice: TrezorDevice = {
...device,
- acquiring: false,
+ // acquiring: false,
remember: false,
- connected: device.connected,
- available: device.available,
- path: device.path,
- label: device.label,
+ // connected: device.connected,
+ // available: device.available,
+ // path: device.path,
+ // label: device.label,
state: null,
instance,
- instanceLabel: `${device.label} (${instance})`,
+ // instanceLabel: `${device.label} (${instance})`,
+ instanceLabel: `${device.label} (${ device.instanceName || instance })`,
ts: new Date().getTime(),
}
newState.devices.push(newDevice);
@@ -450,7 +468,7 @@ export default function connect(state: State = initialState, action: Action): St
case DEVICE.CONNECT_UNACQUIRED :
return addDevice(state, action.device);
case DEVICE.CHANGED :
- return changeDevice(state, { ...action.device, connected: true, available: true });
+ 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);