mirror of
https://github.com/trezor/trezor-wallet
synced 2025-07-03 21:32:34 +00:00
clone custom name
This commit is contained in:
parent
ff7f79eb14
commit
2beaeff8ac
@ -56,7 +56,7 @@ export const ConfirmUnverifiedAddress = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="confirm-address-unverified">
|
<div className="confirm-address-unverified">
|
||||||
<button className="close-modal transparent" onClick={ onCancel }></button>
|
<button className="close-modal transparent" onClick={ onCancel }></button>
|
||||||
<h3>{ device.instanceLabel } is not connected</h3>
|
<h3>{ device.label } is not connected</h3>
|
||||||
<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>
|
<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>
|
||||||
<button onClick={ event => {
|
<button onClick={ event => {
|
||||||
onCancel();
|
onCancel();
|
||||||
@ -74,7 +74,7 @@ export const ConfirmUnverifiedAddress = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="confirm-address-unverified">
|
<div className="confirm-address-unverified">
|
||||||
<button className="close-modal transparent" onClick={ onCancel }></button>
|
<button className="close-modal transparent" onClick={ onCancel }></button>
|
||||||
<h3>{ device.instanceLabel } is unavailable</h3>
|
<h3>{ device.label } 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>
|
<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 => {
|
<button onClick={ event => {
|
||||||
onCancel();
|
onCancel();
|
||||||
|
@ -5,6 +5,10 @@ import React from 'react';
|
|||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
const Confirmation = (props: Props) => {
|
const Confirmation = (props: Props) => {
|
||||||
|
|
||||||
|
if (!props.modal.opened) return null;
|
||||||
|
const { device } = props.modal;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
amount,
|
amount,
|
||||||
address,
|
address,
|
||||||
@ -18,8 +22,8 @@ const Confirmation = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="confirm-tx">
|
<div className="confirm-tx">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h3>Confirm transaction on your TREZOR</h3>
|
<h3>Confirm transaction on { device.label } device</h3>
|
||||||
<p>Details are shown on device</p>
|
<p>Details are shown on display</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<label>Send </label>
|
<label>Send </label>
|
||||||
|
@ -1,21 +1,63 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { getNewInstance } from '../../reducers/TrezorConnectReducer'
|
||||||
import type { Props } from './index';
|
import type { Props } from './index';
|
||||||
|
|
||||||
const RememberDevice = (props: Props) => {
|
type State = {
|
||||||
if (!props.modal.opened) return null;
|
defaultName: string;
|
||||||
const { device } = props.modal;
|
instanceName: ?string;
|
||||||
const { onCancel, onDuplicateDevice } = props.modalActions;
|
}
|
||||||
|
|
||||||
|
export default class DuplicateDevice extends Component<Props, State> {
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div className="duplicate">
|
<div className="duplicate">
|
||||||
<h3>Clone { device.instanceLabel }?</h3>
|
<h3>Clone { device.label }?</h3>
|
||||||
<p>This will create new instance of device which can be used with different passphrase</p>
|
<p>This will create new instance of device which can be used with different passphrase</p>
|
||||||
<button onClick={ event => onDuplicateDevice( { ...device, instanceLabel: "TODO: user label from input" } ) }>Create new instance</button>
|
<label>Instance name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
autoCapitalize="off"
|
||||||
|
spellCheck="false"
|
||||||
|
placeholder={ this.state.defaultName }
|
||||||
|
onChange={ event => this.onNameChange(event.currentTarget.value) }
|
||||||
|
defaultValue={ this.state.instanceName } />
|
||||||
|
<button onClick={ event => onDuplicateDevice( { ...device, instanceName: this.state.instanceName } ) }>Create new instance</button>
|
||||||
<button className="white" onClick={ onCancel }>Cancel</button>
|
<button className="white" onClick={ onCancel }>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export default RememberDevice;
|
|
@ -10,7 +10,7 @@ const InvalidPin = (props: Props) => {
|
|||||||
const { device } = props.modal;
|
const { device } = props.modal;
|
||||||
return (
|
return (
|
||||||
<div className="pin">
|
<div className="pin">
|
||||||
<h3>Entered PIN for { device.label } is not correct.</h3>
|
<h3>Entered PIN for { device.label } is not correct</h3>
|
||||||
<p>Retrying...</p>
|
<p>Retrying...</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,12 +2,17 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
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 (
|
return (
|
||||||
<div className="confirm-tx">
|
<div className="confirm-tx">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h3>Complete the action on your TREZOR device</h3>
|
<h3>Complete the action on { device.label } device</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -67,16 +67,15 @@ export default class RememberDevice extends Component<Props, State> {
|
|||||||
const { onForgetDevice, onRememberDevice } = this.props.modalActions;
|
const { onForgetDevice, onRememberDevice } = this.props.modalActions;
|
||||||
|
|
||||||
let label = device.label;
|
let label = device.label;
|
||||||
let devicePlural: string = "device or to remember it";
|
const devicePlural: string = instances && instances.length > 1 ? "devices or to remember them" : "device or to remember it";
|
||||||
if (instances && instances.length > 1) {
|
if (instances && instances.length > 0) {
|
||||||
label = instances.map((instance, index) => {
|
label = instances.map((instance, index) => {
|
||||||
let comma: string = '';
|
let comma: string = '';
|
||||||
if (index > 0) comma = ', ';
|
if (index > 0) comma = ', ';
|
||||||
return (
|
return (
|
||||||
<span key={ index }>{ comma }{ instance.instanceLabel }</span>
|
<span key={ index }>{ comma }{ instance.instanceLabel }</span>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
devicePlural = "devices or to remember them";
|
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="remember">
|
<div className="remember">
|
||||||
|
@ -51,6 +51,7 @@ export type TrezorDevice = {
|
|||||||
state: ?string;
|
state: ?string;
|
||||||
instance?: number;
|
instance?: number;
|
||||||
instanceLabel: string;
|
instanceLabel: string;
|
||||||
|
instanceName: ?string;
|
||||||
features?: Features;
|
features?: Features;
|
||||||
unacquired?: boolean;
|
unacquired?: boolean;
|
||||||
acquiring: boolean;
|
acquiring: boolean;
|
||||||
|
@ -81,6 +81,24 @@ export const isSavedDevice = (state: State, device: any): ?Array<TrezorDevice> =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getNewInstance = (devices: Array<TrezorDevice>, device: Device | TrezorDevice): number => {
|
||||||
|
|
||||||
|
const affectedDevices: Array<TrezorDevice> = 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 => {
|
const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice => {
|
||||||
|
|
||||||
// do not merge if passphrase protection was changed
|
// do not merge if passphrase protection was changed
|
||||||
@ -95,7 +113,7 @@ const mergeDevices = (current: TrezorDevice, upcoming: Object): TrezorDevice =>
|
|||||||
if (upcoming.label !== current.label) {
|
if (upcoming.label !== current.label) {
|
||||||
instanceLabel = upcoming.label
|
instanceLabel = upcoming.label
|
||||||
if (typeof current.instance === 'number') {
|
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,
|
acquiring: typeof upcoming.acquiring === 'boolean' ? upcoming.acquiring : current.acquiring,
|
||||||
ts: typeof upcoming.ts === 'number' ? upcoming.ts : current.ts,
|
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) {
|
if (upcoming.unacquired && current.state) {
|
||||||
dev.instanceLabel = current.instanceLabel;
|
dev.unacquired = false;
|
||||||
dev.features = current.features;
|
dev.features = current.features;
|
||||||
dev.label = current.label;
|
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;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,14 +162,17 @@ const addDevice = (state: State, device: Device): State => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (affectedDevices.length > 0 ) {
|
if (affectedDevices.length > 0 ) {
|
||||||
// replace existing values
|
// check if freshly added device has different "passphrase_protection" settings
|
||||||
// const changedDevices: Array<TrezorDevice> = affectedDevices.map(d => mergeDevices(d, { ...device, connected: true} ));
|
|
||||||
let cloneInstance: number = 1;
|
let cloneInstance: number = 1;
|
||||||
|
let hasDifferentPassphraseSettings: boolean = false;
|
||||||
|
let hasInstancesWithPassphraseSettings: boolean = false;
|
||||||
const changedDevices: Array<TrezorDevice> = affectedDevices.map(d => {
|
const changedDevices: Array<TrezorDevice> = affectedDevices.map(d => {
|
||||||
if (d.features && d.features.passphrase_protection === device.features.passphrase_protection) {
|
if (d.features && d.features.passphrase_protection === device.features.passphrase_protection) {
|
||||||
cloneInstance = 0;
|
cloneInstance = 0;
|
||||||
|
hasInstancesWithPassphraseSettings = true;
|
||||||
return mergeDevices(d, { ...device, connected: true, available: true } );
|
return mergeDevices(d, { ...device, connected: true, available: true } );
|
||||||
} else {
|
} else {
|
||||||
|
hasDifferentPassphraseSettings = true;
|
||||||
if (d.instance && cloneInstance > 0) {
|
if (d.instance && cloneInstance > 0) {
|
||||||
cloneInstance = d.instance + 1;
|
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
|
// 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 = {
|
const newDevice: TrezorDevice = {
|
||||||
...device,
|
...device,
|
||||||
@ -182,14 +199,15 @@ const addDevice = (state: State, device: Device): State => {
|
|||||||
state: null,
|
state: null,
|
||||||
instance,
|
instance,
|
||||||
instanceLabel: `${device.label} (${instance})`,
|
instanceLabel: `${device.label} (${instance})`,
|
||||||
|
instanceName: null,
|
||||||
ts: new Date().getTime(),
|
ts: new Date().getTime(),
|
||||||
}
|
}
|
||||||
changedDevices.push(newDevice);
|
changedDevices.push(newDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
newState.devices = otherDevices.concat(changedDevices);
|
newState.devices = otherDevices.concat(changedDevices);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
const newDevice: TrezorDevice = {
|
const newDevice: TrezorDevice = {
|
||||||
...device,
|
...device,
|
||||||
acquiring: false,
|
acquiring: false,
|
||||||
@ -199,14 +217,11 @@ const addDevice = (state: State, device: Device): State => {
|
|||||||
path: device.path,
|
path: device.path,
|
||||||
label: device.label,
|
label: device.label,
|
||||||
state: null,
|
state: null,
|
||||||
// instance: 0,
|
|
||||||
instanceLabel: device.label,
|
instanceLabel: device.label,
|
||||||
|
instanceName: null,
|
||||||
ts: new Date().getTime(),
|
ts: new Date().getTime(),
|
||||||
}
|
}
|
||||||
newState.devices.push(newDevice);
|
newState.devices.push(newDevice);
|
||||||
|
|
||||||
// const clone = { ...newDevice, instance: 1, instanceLabel: device.label + '#1' };
|
|
||||||
// newState.devices.push(clone);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
@ -251,8 +266,14 @@ const changeDevice = (state: State, device: Object): State => {
|
|||||||
if (affectedDevices.length > 0) {
|
if (affectedDevices.length > 0) {
|
||||||
|
|
||||||
const isAffectedUnacquired: number = affectedDevices.findIndex(d => d.unacquired);
|
const isAffectedUnacquired: number = affectedDevices.findIndex(d => d.unacquired);
|
||||||
if (isAffectedUnacquired >= 0 && affectedDevices.length > 1){
|
// if (isAffectedUnacquired >= 0 && affectedDevices.length > 1){
|
||||||
affectedDevices.splice(isAffectedUnacquired, 1);
|
if (isAffectedUnacquired >= 0){
|
||||||
|
// TODO: should unacquired device be removed? or merged?
|
||||||
|
//affectedDevices.splice(isAffectedUnacquired, 1);
|
||||||
|
} else {
|
||||||
|
// replace existing values
|
||||||
|
const changedDevices: Array<TrezorDevice> = affectedDevices.map(d => mergeDevices(d, device));
|
||||||
|
newState.devices = otherDevices.concat(changedDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// else if (isAffectedUnacquired >= 0 && !device.unacquired && affectedDevices.length > 1) {
|
// else if (isAffectedUnacquired >= 0 && !device.unacquired && affectedDevices.length > 1) {
|
||||||
@ -263,12 +284,6 @@ const changeDevice = (state: State, device: Object): State => {
|
|||||||
if (state.selectedDevice && device.path === state.selectedDevice.id && affectedDevices.length > 1) {
|
if (state.selectedDevice && device.path === state.selectedDevice.id && affectedDevices.length > 1) {
|
||||||
// affectedDevices = affectedDevices.filter(d => d.path !== state.selectedDevice.id && d.features);
|
// affectedDevices = affectedDevices.filter(d => d.path !== state.selectedDevice.id && d.features);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// replace existing values
|
|
||||||
const changedDevices: Array<TrezorDevice> = affectedDevices.map(d => mergeDevices(d, device));
|
|
||||||
newState.devices = otherDevices.concat(changedDevices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
@ -336,30 +351,33 @@ const devicesFromLocalStorage = (devices: Array<any>): Array<TrezorDevice> => {
|
|||||||
|
|
||||||
const duplicate = (state: State, device: TrezorDevice): State => {
|
const duplicate = (state: State, device: TrezorDevice): State => {
|
||||||
const newState: State = { ...state };
|
const newState: State = { ...state };
|
||||||
const affectedDevices: Array<TrezorDevice> = state.devices.filter(d => d.features && device.features && d.features.device_id === device.features.device_id)
|
// const affectedDevices: Array<TrezorDevice> = state.devices.filter(d => d.features && device.features && d.features.device_id === device.features.device_id)
|
||||||
.sort((a, b) => {
|
// .sort((a, b) => {
|
||||||
if (!a.instance) {
|
// if (!a.instance) {
|
||||||
return -1;
|
// return -1;
|
||||||
} else {
|
// } else {
|
||||||
return !b.instance || a.instance > b.instance ? 1 : -1;
|
// return !b.instance || a.instance > b.instance ? 1 : -1;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
const instance: number = affectedDevices.reduce((inst, dev) => {
|
// const instance: number = affectedDevices.reduce((inst, dev) => {
|
||||||
return dev.instance ? dev.instance + 1 : inst + 1;
|
// return dev.instance ? dev.instance + 1 : inst + 1;
|
||||||
}, 0);
|
// }, 0);
|
||||||
|
|
||||||
|
const instance: number = getNewInstance(state.devices, device);
|
||||||
|
|
||||||
const newDevice: TrezorDevice = {
|
const newDevice: TrezorDevice = {
|
||||||
...device,
|
...device,
|
||||||
acquiring: false,
|
// acquiring: false,
|
||||||
remember: false,
|
remember: false,
|
||||||
connected: device.connected,
|
// connected: device.connected,
|
||||||
available: device.available,
|
// available: device.available,
|
||||||
path: device.path,
|
// path: device.path,
|
||||||
label: device.label,
|
// label: device.label,
|
||||||
state: null,
|
state: null,
|
||||||
instance,
|
instance,
|
||||||
instanceLabel: `${device.label} (${instance})`,
|
// instanceLabel: `${device.label} (${instance})`,
|
||||||
|
instanceLabel: `${device.label} (${ device.instanceName || instance })`,
|
||||||
ts: new Date().getTime(),
|
ts: new Date().getTime(),
|
||||||
}
|
}
|
||||||
newState.devices.push(newDevice);
|
newState.devices.push(newDevice);
|
||||||
@ -450,7 +468,7 @@ export default function connect(state: State = initialState, action: Action): St
|
|||||||
case DEVICE.CONNECT_UNACQUIRED :
|
case DEVICE.CONNECT_UNACQUIRED :
|
||||||
return addDevice(state, action.device);
|
return addDevice(state, action.device);
|
||||||
case DEVICE.CHANGED :
|
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 :
|
||||||
case DEVICE.DISCONNECT_UNACQUIRED :
|
case DEVICE.DISCONNECT_UNACQUIRED :
|
||||||
return disconnectDevice(state, action.device);
|
return disconnectDevice(state, action.device);
|
||||||
|
Loading…
Reference in New Issue
Block a user