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