Fix Duplicate DPaths & Deterministic Modal Improvements (#1682)
* Fix dPath uniqueness * Pass around dPath object, not just path, to maintain uniqueness * Improve deterministic modal UI * Fix custom dpaths
This commit is contained in:
parent
82e0530bec
commit
a57d17a3e0
|
@ -46,7 +46,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&-addresses {
|
&-addresses {
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
&-table {
|
&-table {
|
||||||
width: 732px;
|
width: 732px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -74,16 +74,22 @@
|
||||||
background-image: url('~assets/images/icon-external-link.svg');
|
background-image: url('~assets/images/icon-external-link.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-na {
|
||||||
|
font-size: $font-size-xs;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific selectors to override bootstrap
|
||||||
tbody {
|
tbody {
|
||||||
tr {
|
tr {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
td {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-nav {
|
&-nav {
|
||||||
&-btn {
|
&-btn {
|
||||||
|
|
|
@ -24,7 +24,7 @@ const WALLETS_PER_PAGE = 5;
|
||||||
interface Props {
|
interface Props {
|
||||||
// Passed props
|
// Passed props
|
||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
dPath: string;
|
dPath: DPath;
|
||||||
dPaths: DPath[];
|
dPaths: DPath[];
|
||||||
publicKey?: string;
|
publicKey?: string;
|
||||||
chainCode?: string;
|
chainCode?: string;
|
||||||
|
@ -42,11 +42,11 @@ interface Props {
|
||||||
|
|
||||||
onCancel(): void;
|
onCancel(): void;
|
||||||
onConfirmAddress(address: string, addressIndex: number): void;
|
onConfirmAddress(address: string, addressIndex: number): void;
|
||||||
onPathChange(path: string): void;
|
onPathChange(dPath: DPath): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
currentLabel: string;
|
currentDPath: DPath;
|
||||||
selectedAddress: string;
|
selectedAddress: string;
|
||||||
selectedAddrIndex: number;
|
selectedAddrIndex: number;
|
||||||
isCustomPath: boolean;
|
isCustomPath: boolean;
|
||||||
|
@ -65,7 +65,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
selectedAddrIndex: 0,
|
selectedAddrIndex: 0,
|
||||||
isCustomPath: false,
|
isCustomPath: false,
|
||||||
customPath: '',
|
customPath: '',
|
||||||
currentLabel: '',
|
currentDPath: this.props.dPath,
|
||||||
page: 0
|
page: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { wallets, desiredToken, network, tokens, dPath, dPaths, onCancel } = this.props;
|
const { wallets, desiredToken, network, tokens, dPaths, onCancel } = this.props;
|
||||||
const { selectedAddress, customPath, page } = this.state;
|
const { selectedAddress, customPath, page } = this.state;
|
||||||
|
|
||||||
const buttons: IButton[] = [
|
const buttons: IButton[] = [
|
||||||
|
@ -119,7 +119,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
<div className="DWModal-path-select">
|
<div className="DWModal-path-select">
|
||||||
<Select
|
<Select
|
||||||
name="fieldDPath"
|
name="fieldDPath"
|
||||||
value={this.state.currentLabel || this.findDPath('value', dPath).value}
|
value={this.state.currentDPath}
|
||||||
onChange={this.handleChangePath}
|
onChange={this.handleChangePath}
|
||||||
options={dPaths.concat([customDPath])}
|
options={dPaths.concat([customDPath])}
|
||||||
optionRenderer={this.renderDPathOption}
|
optionRenderer={this.renderDPathOption}
|
||||||
|
@ -128,7 +128,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
searchable={false}
|
searchable={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{this.state.currentLabel === customDPath.label && (
|
{this.state.currentDPath.label === customDPath.label && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className="DWModal-path-custom">
|
<div className="DWModal-path-custom">
|
||||||
<Input
|
<Input
|
||||||
|
@ -199,12 +199,12 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
const { dPath, publicKey, chainCode, seed } = props;
|
const { dPath, publicKey, chainCode, seed } = props;
|
||||||
|
|
||||||
if (dPath && ((publicKey && chainCode) || seed)) {
|
if (dPath && ((publicKey && chainCode) || seed)) {
|
||||||
if (isValidPath(dPath)) {
|
if (isValidPath(dPath.value)) {
|
||||||
this.props.getDeterministicWallets({
|
this.props.getDeterministicWallets({
|
||||||
seed,
|
seed,
|
||||||
dPath,
|
|
||||||
publicKey,
|
publicKey,
|
||||||
chainCode,
|
chainCode,
|
||||||
|
dPath: dPath.value,
|
||||||
limit: WALLETS_PER_PAGE,
|
limit: WALLETS_PER_PAGE,
|
||||||
offset: WALLETS_PER_PAGE * this.state.page
|
offset: WALLETS_PER_PAGE * this.state.page
|
||||||
});
|
});
|
||||||
|
@ -214,19 +214,12 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private findDPath = (prop: keyof DPath, cmp: string) => {
|
|
||||||
return this.props.dPaths.find(d => d[prop] === cmp) || customDPath;
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleChangePath = (newPath: DPath) => {
|
private handleChangePath = (newPath: DPath) => {
|
||||||
const { value: dPathLabel } = newPath;
|
if (newPath.value === customDPath.value) {
|
||||||
const { value } = this.findDPath('value', dPathLabel);
|
this.setState({ isCustomPath: true, currentDPath: newPath });
|
||||||
|
|
||||||
if (value === customDPath.value) {
|
|
||||||
this.setState({ isCustomPath: true, currentLabel: dPathLabel });
|
|
||||||
} else {
|
} else {
|
||||||
this.setState({ isCustomPath: false, currentLabel: dPathLabel });
|
this.setState({ isCustomPath: false, currentDPath: newPath });
|
||||||
this.props.onPathChange(value);
|
this.props.onPathChange(newPath);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -235,11 +228,14 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleSubmitCustomPath = (ev: React.FormEvent<HTMLFormElement>) => {
|
private handleSubmitCustomPath = (ev: React.FormEvent<HTMLFormElement>) => {
|
||||||
const { customPath, currentLabel } = this.state;
|
const { customPath, currentDPath } = this.state;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
if (currentLabel === customDPath.label && isValidPath(customPath)) {
|
if (currentDPath.value === customDPath.value && isValidPath(customPath)) {
|
||||||
this.props.onPathChange(customPath);
|
this.props.onPathChange({
|
||||||
|
label: customDPath.label,
|
||||||
|
value: customPath
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -309,16 +305,16 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{token ? (
|
{desiredToken ? (
|
||||||
<UnitDisplay
|
<UnitDisplay
|
||||||
decimal={token.decimal}
|
decimal={token ? token.decimal : 0}
|
||||||
value={token.value}
|
value={token ? token.value : null}
|
||||||
symbol={desiredToken}
|
symbol={desiredToken}
|
||||||
displayShortBalance={true}
|
displayShortBalance={true}
|
||||||
checkOffline={true}
|
checkOffline={true}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
'???'
|
<span className="DWModal-addresses-table-na">N/A</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -26,7 +26,7 @@ interface StateProps {
|
||||||
interface State {
|
interface State {
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
chainCode: string;
|
chainCode: string;
|
||||||
dPath: string;
|
dPath: DPath;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
showTip: boolean;
|
showTip: boolean;
|
||||||
|
@ -38,7 +38,7 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
public state: State = {
|
public state: State = {
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
chainCode: '',
|
||||||
dPath: this.props.dPath ? this.props.dPath.value : '',
|
dPath: this.props.dPath || this.props.dPaths[0],
|
||||||
error: null,
|
error: null,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
showTip: false
|
showTip: false
|
||||||
|
@ -51,8 +51,8 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentWillReceiveProps(nextProps: Props) {
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
if (this.props.dPath !== nextProps.dPath) {
|
if (this.props.dPath !== nextProps.dPath && nextProps.dPath) {
|
||||||
this.setState({ dPath: nextProps.dPath ? nextProps.dPath.value : '' });
|
this.setState({ dPath: nextProps.dPath });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,11 +123,11 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handlePathChange = (dPath: string) => {
|
private handlePathChange = (dPath: DPath) => {
|
||||||
this.handleConnect(dPath);
|
this.handleConnect(dPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleConnect = (dPath: string = this.state.dPath) => {
|
private handleConnect = (dPath: DPath) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -136,7 +136,7 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
|
|
||||||
ledger.comm_u2f.create_async().then((comm: any) => {
|
ledger.comm_u2f.create_async().then((comm: any) => {
|
||||||
new ledger.eth(comm)
|
new ledger.eth(comm)
|
||||||
.getAddress_async(dPath, false, true)
|
.getAddress_async(dPath.value, false, true)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.setState({
|
this.setState({
|
||||||
publicKey: res.publicKey,
|
publicKey: res.publicKey,
|
||||||
|
@ -182,19 +182,19 @@ class LedgerNanoSDecryptClass extends PureComponent<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleUnlock = (address: string, index: number) => {
|
private handleUnlock = (address: string, index: number) => {
|
||||||
this.props.onUnlock(new LedgerWallet(address, this.state.dPath, index));
|
this.props.onUnlock(new LedgerWallet(address, this.state.dPath.value, index));
|
||||||
this.reset();
|
this.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleNullConnect = (): void => {
|
private handleNullConnect = (): void => {
|
||||||
return this.handleConnect();
|
return this.handleConnect(this.state.dPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
private reset() {
|
private reset() {
|
||||||
this.setState({
|
this.setState({
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
chainCode: '',
|
||||||
dPath: this.props.dPath ? this.props.dPath.value : ''
|
dPath: this.props.dPath || this.props.dPaths[0]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ interface State {
|
||||||
formattedPhrase: string;
|
formattedPhrase: string;
|
||||||
pass: string;
|
pass: string;
|
||||||
seed: string;
|
seed: string;
|
||||||
dPath: string;
|
dPath: DPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MnemonicDecryptClass extends PureComponent<Props, State> {
|
class MnemonicDecryptClass extends PureComponent<Props, State> {
|
||||||
|
@ -36,12 +36,12 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
||||||
formattedPhrase: '',
|
formattedPhrase: '',
|
||||||
pass: '',
|
pass: '',
|
||||||
seed: '',
|
seed: '',
|
||||||
dPath: this.props.dPath.value
|
dPath: this.props.dPath
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentWillReceiveProps(nextProps: Props) {
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
if (this.props.dPath !== nextProps.dPath) {
|
if (this.props.dPath !== nextProps.dPath) {
|
||||||
this.setState({ dPath: nextProps.dPath.value });
|
this.setState({ dPath: nextProps.dPath });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
||||||
this.setState({ seed: '' });
|
this.setState({ seed: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
private handlePathChange = (dPath: string) => {
|
private handlePathChange = (dPath: DPath) => {
|
||||||
this.setState({ dPath });
|
this.setState({ dPath });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
||||||
const { formattedPhrase, pass, dPath } = this.state;
|
const { formattedPhrase, pass, dPath } = this.state;
|
||||||
|
|
||||||
this.props.onUnlock({
|
this.props.onUnlock({
|
||||||
path: `${dPath}/${index}`,
|
path: `${dPath.value}/${index}`,
|
||||||
pass,
|
pass,
|
||||||
phrase: formattedPhrase,
|
phrase: formattedPhrase,
|
||||||
address
|
address
|
||||||
|
|
|
@ -25,7 +25,7 @@ interface StateProps {
|
||||||
interface State {
|
interface State {
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
chainCode: string;
|
chainCode: string;
|
||||||
dPath: string;
|
dPath: DPath;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
|
@ -36,14 +36,14 @@ class TrezorDecryptClass extends PureComponent<Props, State> {
|
||||||
public state: State = {
|
public state: State = {
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
chainCode: '',
|
||||||
dPath: this.props.dPath ? this.props.dPath.value : '',
|
dPath: this.props.dPath || this.props.dPaths[0],
|
||||||
error: null,
|
error: null,
|
||||||
isLoading: false
|
isLoading: false
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentWillReceiveProps(nextProps: Props) {
|
public componentWillReceiveProps(nextProps: Props) {
|
||||||
if (this.props.dPath !== nextProps.dPath) {
|
if (this.props.dPath !== nextProps.dPath && nextProps.dPath) {
|
||||||
this.setState({ dPath: nextProps.dPath ? nextProps.dPath.value : '' });
|
this.setState({ dPath: nextProps.dPath });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,19 +98,19 @@ class TrezorDecryptClass extends PureComponent<Props, State> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handlePathChange = (dPath: string) => {
|
private handlePathChange = (dPath: DPath) => {
|
||||||
this.setState({ dPath });
|
this.setState({ dPath });
|
||||||
this.handleConnect(dPath);
|
this.handleConnect(dPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleConnect = (dPath: string = this.state.dPath): void => {
|
private handleConnect = (dPath: DPath): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
error: null
|
error: null
|
||||||
});
|
});
|
||||||
|
|
||||||
(TrezorConnect as any).getXPubKey(
|
(TrezorConnect as any).getXPubKey(
|
||||||
dPath,
|
dPath.value,
|
||||||
(res: any) => {
|
(res: any) => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -135,17 +135,19 @@ class TrezorDecryptClass extends PureComponent<Props, State> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleUnlock = (address: string, index: number) => {
|
private handleUnlock = (address: string, index: number) => {
|
||||||
this.props.onUnlock(new TrezorWallet(address, this.state.dPath, index));
|
this.props.onUnlock(new TrezorWallet(address, this.state.dPath.value, index));
|
||||||
this.reset();
|
this.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleNullConnect = (): void => this.handleConnect();
|
private handleNullConnect = (): void => {
|
||||||
|
this.handleConnect(this.state.dPath);
|
||||||
|
};
|
||||||
|
|
||||||
private reset() {
|
private reset() {
|
||||||
this.setState({
|
this.setState({
|
||||||
publicKey: '',
|
publicKey: '',
|
||||||
chainCode: '',
|
chainCode: '',
|
||||||
dPath: this.props.dPath ? this.props.dPath.value : ''
|
dPath: this.props.dPath || this.props.dPaths[0]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { InsecureWalletName, SecureWalletName, WalletName, walletNames } from 'config';
|
import { InsecureWalletName, SecureWalletName, WalletName, walletNames } from 'config';
|
||||||
import { EXTRA_PATHS } from 'config/dpaths';
|
import { EXTRA_PATHS } from 'config/dpaths';
|
||||||
import sortedUniq from 'lodash/sortedUniq';
|
import uniqBy from 'lodash/uniqBy';
|
||||||
import difference from 'lodash/difference';
|
import difference from 'lodash/difference';
|
||||||
import { StaticNetworkConfig, DPathFormats } from 'types/network';
|
import { StaticNetworkConfig, DPathFormats } from 'types/network';
|
||||||
import { AppState } from 'reducers';
|
import { AppState } from 'reducers';
|
||||||
|
@ -22,7 +22,7 @@ export function getPaths(state: AppState, pathType: PathType): DPath[] {
|
||||||
return networkPaths;
|
return networkPaths;
|
||||||
}, [])
|
}, [])
|
||||||
.concat(EXTRA_PATHS);
|
.concat(EXTRA_PATHS);
|
||||||
return sortedUniq(paths);
|
return uniqBy(paths, p => `${p.label}${p.value}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSingleDPath(state: AppState, format: DPathFormat): DPath | undefined {
|
export function getSingleDPath(state: AppState, format: DPathFormat): DPath | undefined {
|
||||||
|
|
Loading…
Reference in New Issue