Skip to content

Commit 567112a

Browse files
authored
[DDW-172] Handle duplicate wallets in import process (#1985)
* [DDW-172] Adds CHANGELOG entry * [DDW-172] Use wallet indexes for wallet import logic * [DDW-172] Finish logic implementation * [DDW-172] Bump cardano-wallet version
1 parent 0d21e1e commit 567112a

File tree

8 files changed

+125
-46
lines changed

8 files changed

+125
-46
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Changelog
55

66
### Fixes
77

8+
- Handle duplicate wallets in import process ([PR 1985](https://github.com/input-output-hk/daedalus/pull/1985))
89
- Treat wallets with 100% syncing progress as synced wallets ([PR 1984](https://github.com/input-output-hk/daedalus/pull/1984))
910
- Fixed `cardano-node` / `jormungandr` and `cardano-wallet` info on the "Diagnostics" screen ([PR 1980](https://github.com/input-output-hk/daedalus/pull/1980))
1011
- Persist "Blank screen fix" / "--safe-mode" flag between Daedalus restarts ([PR 1979](https://github.com/input-output-hk/daedalus/pull/1979))

nix/sources.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
"homepage": null,
4242
"owner": "input-output-hk",
4343
"repo": "cardano-wallet",
44-
"rev": "745aaad67004855a84c51e400c6fa1d10aedb910",
45-
"sha256": "03y85ipp283bwn5jk1dg4cc3304dak2szs31qhys9facjyygdf81",
44+
"rev": "b8ae9821f27eb24a119aa06532519cce2ab11cbc",
45+
"sha256": "1j5wv2mjbpqbjl9r9647gxq7jsv14xghizslrhcahrvkv1f5p8m4",
4646
"type": "tarball",
47-
"url": "https://github.com/input-output-hk/cardano-wallet/archive/745aaad67004855a84c51e400c6fa1d10aedb910.tar.gz",
47+
"url": "https://github.com/input-output-hk/cardano-wallet/archive/b8ae9821f27eb24a119aa06532519cce2ab11cbc.tar.gz",
4848
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz",
4949
"version": "v2020-04-07"
5050
},

source/renderer/app/actions/wallet-migration-actions.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export default class WalletMigrationActions {
55
startMigration: Action<any> = new Action();
66
finishMigration: Action<any> = new Action();
77
resetMigration: Action<any> = new Action();
8-
toggleWalletImportSelection: Action<string> = new Action();
9-
updateWalletName: Action<{ id: string, name: string }> = new Action();
8+
toggleWalletImportSelection: Action<{ index: number }> = new Action();
9+
updateWalletName: Action<{ index: number, name: string }> = new Action();
1010
nextStep: Action<any> = new Action();
1111
selectExportSourcePath: Action<any> = new Action();
1212
}

source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export default class WalletSelectImportDialog extends Component<Props> {
152152
statusIcon = (
153153
<Checkbox
154154
onChange={() => {
155-
onToggleWalletImportSelection(wallet.id);
155+
onToggleWalletImportSelection({ index: wallet.index });
156156
}}
157157
checked={wallet.import.status === WalletImportStatuses.PENDING}
158158
disabled={disabled}
@@ -219,7 +219,10 @@ export default class WalletSelectImportDialog extends Component<Props> {
219219
const walletsWithoutNames = exportedWallets.filter(
220220
({ hasName }: ExportedByronWallet) => !hasName
221221
);
222-
let walletIndex = 0;
222+
223+
// We use previous wallet id to detect wallet duplicates
224+
let previousWalletId = '';
225+
let rowNumber = 1;
223226

224227
return (
225228
<ReactModal
@@ -248,12 +251,15 @@ export default class WalletSelectImportDialog extends Component<Props> {
248251
</div>
249252
<div className={styles.walletsContainer}>
250253
{walletsWithNames.map(wallet => {
251-
walletIndex++;
252-
return (
253-
<div className={styles.walletsRow} key={wallet.id}>
254-
<div
255-
className={styles.walletsCounter}
256-
>{`${walletIndex}.`}</div>
254+
const isDuplicate = previousWalletId === wallet.id;
255+
const walletRow = (
256+
<div
257+
className={styles.walletsRow}
258+
key={`${wallet.id}-${wallet.index}`}
259+
>
260+
<div className={styles.walletsCounter}>
261+
{!isDuplicate && `${rowNumber}.`}
262+
</div>
257263
<div className={styles.walletsInputField}>
258264
<InlineEditingSmallInput
259265
isActive={false}
@@ -272,7 +278,7 @@ export default class WalletSelectImportDialog extends Component<Props> {
272278
)}
273279
onSubmit={(name: string) =>
274280
onWalletNameChange({
275-
id: wallet.id,
281+
index: wallet.index,
276282
name,
277283
})
278284
}
@@ -288,6 +294,11 @@ export default class WalletSelectImportDialog extends Component<Props> {
288294
</div>
289295
</div>
290296
);
297+
if (!isDuplicate) {
298+
previousWalletId = wallet.id;
299+
rowNumber++;
300+
}
301+
return walletRow;
291302
})}
292303

293304
{!!walletsWithoutNames.length && (
@@ -311,12 +322,15 @@ export default class WalletSelectImportDialog extends Component<Props> {
311322
)}
312323

313324
{walletsWithoutNames.map(wallet => {
314-
walletIndex++;
315-
return (
316-
<div className={styles.walletsRow} key={wallet.id}>
317-
<div
318-
className={styles.walletsCounter}
319-
>{`${walletIndex}.`}</div>
325+
const isDuplicate = previousWalletId === wallet.id;
326+
const walletRow = (
327+
<div
328+
className={styles.walletsRow}
329+
key={`${wallet.id}-${wallet.index}`}
330+
>
331+
<div className={styles.walletsCounter}>
332+
{!isDuplicate && `${rowNumber}.`}
333+
</div>
320334
<div className={styles.walletsInputField}>
321335
<InlineEditingSmallInput
322336
isActive={false}
@@ -335,7 +349,7 @@ export default class WalletSelectImportDialog extends Component<Props> {
335349
)}
336350
onSubmit={(name: string) =>
337351
onWalletNameChange({
338-
id: wallet.id,
352+
index: wallet.index,
339353
name,
340354
})
341355
}
@@ -350,6 +364,11 @@ export default class WalletSelectImportDialog extends Component<Props> {
350364
</div>
351365
</div>
352366
);
367+
if (!isDuplicate) {
368+
previousWalletId = wallet.id;
369+
rowNumber++;
370+
}
371+
return walletRow;
353372
})}
354373
</div>
355374
<div className={styles.action}>

source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@
204204
font-size: 16px;
205205
letter-spacing: 0.5px;
206206
line-height: 1.38;
207+
min-width: 35px;
207208
padding-right: 20px;
208209
}
209210

source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ export default class WalletImportDialogContainer extends Component<Props> {
2525
this.props.actions.walletMigration.finishMigration.trigger();
2626
};
2727

28-
onWalletNameChange = (params: { id: string, name: string }) => {
28+
onWalletNameChange = (params: { index: number, name: string }) => {
2929
this.props.actions.walletMigration.updateWalletName.trigger(params);
3030
};
3131

32-
onToggleWalletImportSelection = (id: string) => {
33-
this.props.actions.walletMigration.toggleWalletImportSelection.trigger(id);
32+
onToggleWalletImportSelection = (params: { index: number }) => {
33+
this.props.actions.walletMigration.toggleWalletImportSelection.trigger(
34+
params
35+
);
3436
};
3537

3638
onSelectExportSourcePath = () => {

source/renderer/app/stores/WalletMigrationStore.js

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @flow
22
import { action, computed, observable, runInAction } from 'mobx';
3+
import { orderBy } from 'lodash';
34
import Store from './lib/Store';
45
import Request from './lib/LocalizedRequest';
56
import Wallet from '../domains/Wallet';
@@ -88,6 +89,15 @@ export default class WalletMigrationStore extends Store {
8889
getExportedWalletById = (id: string): ?ExportedByronWallet =>
8990
this.exportedWallets.find(w => w.id === id);
9091

92+
getExportedWalletDuplicatesById = (
93+
id: string,
94+
index: number
95+
): Array<ExportedByronWallet> =>
96+
this.exportedWallets.filter(w => w.id === id && w.index !== index);
97+
98+
getExportedWalletByIndex = (index: number): ?ExportedByronWallet =>
99+
this.exportedWallets.find(w => w.index === index);
100+
91101
@action _selectExportSourcePath = async () => {
92102
const params = {
93103
defaultPath: global.legacyStateDir,
@@ -116,34 +126,52 @@ export default class WalletMigrationStore extends Store {
116126
}
117127
};
118128

119-
@action _toggleWalletImportSelection = (id: string) => {
120-
const wallet = this.getExportedWalletById(id);
129+
@action _toggleWalletImportSelection = ({ index }: { index: number }) => {
130+
const wallet = this.getExportedWalletByIndex(index);
121131
if (wallet) {
122132
const { status } = wallet.import;
123133
const isPending = status === WalletImportStatuses.PENDING;
124134
this._updateWalletImportStatus(
125-
id,
135+
index,
126136
isPending
127137
? WalletImportStatuses.UNSTARTED
128138
: WalletImportStatuses.PENDING
129139
);
140+
141+
const walletDuplicates = this.getExportedWalletDuplicatesById(
142+
wallet.id,
143+
index
144+
);
145+
if (walletDuplicates.length) {
146+
walletDuplicates.forEach(w => {
147+
if (w.import.status === WalletImportStatuses.PENDING) {
148+
w.import.status = WalletImportStatuses.UNSTARTED;
149+
}
150+
});
151+
}
130152
}
131153
};
132154

133155
@action _updateWalletImportStatus = (
134-
id: string,
156+
index: number,
135157
status: WalletImportStatus,
136158
error?: LocalizableError
137159
) => {
138-
const wallet = this.getExportedWalletById(id);
160+
const wallet = this.getExportedWalletByIndex(index);
139161
if (wallet) {
140162
wallet.import.status = status;
141163
wallet.import.error = error || null;
142164
}
143165
};
144166

145-
@action _updateWalletName = ({ id, name }: { id: string, name: string }) => {
146-
const wallet = this.getExportedWalletById(id);
167+
@action _updateWalletName = ({
168+
index,
169+
name,
170+
}: {
171+
index: number,
172+
name: string,
173+
}) => {
174+
const wallet = this.getExportedWalletByIndex(index);
147175
if (wallet) {
148176
wallet.name = name;
149177
}
@@ -164,18 +192,28 @@ export default class WalletMigrationStore extends Store {
164192
locale: this.stores.profile.currentLocale,
165193
});
166194
runInAction('update exportedWallets and exportErrors', () => {
167-
this.exportedWallets = wallets.map(wallet => {
168-
const hasName = wallet.name !== null;
169-
const importedWallet = this.stores.wallets.getWalletById(
170-
`legacy_${wallet.id}`
171-
);
172-
const isImported = typeof importedWallet !== 'undefined';
173-
if (isImported && importedWallet) wallet.name = importedWallet.name;
174-
const status = isImported
175-
? WalletImportStatuses.EXISTS
176-
: WalletImportStatuses.UNSTARTED;
177-
return { ...wallet, hasName, import: { status, error: null } };
195+
this.exportedWallets = orderBy(
196+
wallets.map(wallet => {
197+
const hasName = wallet.name !== null;
198+
const importedWallet = this.stores.wallets.getWalletById(
199+
`legacy_${wallet.id}`
200+
);
201+
const isImported = typeof importedWallet !== 'undefined';
202+
if (isImported && importedWallet) wallet.name = importedWallet.name;
203+
const status = isImported
204+
? WalletImportStatuses.EXISTS
205+
: WalletImportStatuses.UNSTARTED;
206+
return { ...wallet, hasName, import: { status, error: null } };
207+
}),
208+
['hasName', 'name', 'id', 'is_passphrase_empty'],
209+
['desc', 'asc', 'asc', 'asc']
210+
);
211+
212+
// Guard against duplicated wallet ids
213+
this.exportedWallets.forEach((wallet, index) => {
214+
wallet.index = index + 1;
178215
});
216+
179217
this.exportErrors =
180218
errors || !this.exportedWalletsCount ? 'No wallets found' : '';
181219
});
@@ -227,8 +265,8 @@ export default class WalletMigrationStore extends Store {
227265
// Reset restore requests to clear previous errors
228266
this.restoreExportedWalletRequest.reset();
229267

230-
const { id } = exportedWallet;
231-
this._updateWalletImportStatus(id, WalletImportStatuses.RUNNING);
268+
const { id, index } = exportedWallet;
269+
this._updateWalletImportStatus(index, WalletImportStatuses.RUNNING);
232270
try {
233271
const restoredWallet = await this.restoreExportedWalletRequest.execute(
234272
exportedWallet
@@ -237,13 +275,30 @@ export default class WalletMigrationStore extends Store {
237275
throw new Error('Restored wallet was not received correctly');
238276

239277
runInAction('update restoredWallets', () => {
240-
this._updateWalletImportStatus(id, WalletImportStatuses.COMPLETED);
278+
this._updateWalletImportStatus(index, WalletImportStatuses.COMPLETED);
279+
280+
const walletDuplicates = this.getExportedWalletDuplicatesById(
281+
id,
282+
index
283+
);
284+
if (walletDuplicates.length) {
285+
walletDuplicates.forEach(w => {
286+
if (w.import.status !== WalletImportStatuses.COMPLETED) {
287+
w.import.status = WalletImportStatuses.COMPLETED;
288+
}
289+
});
290+
}
291+
241292
this.restoredWallets.push(restoredWallet);
242293
});
243294
} catch (error) {
244295
runInAction('update restorationErrors', () => {
245296
const { name, is_passphrase_empty: hasPassword } = exportedWallet;
246-
this._updateWalletImportStatus(id, WalletImportStatuses.ERRORED, error);
297+
this._updateWalletImportStatus(
298+
index,
299+
WalletImportStatuses.ERRORED,
300+
error
301+
);
247302
this.restorationErrors.push({
248303
error,
249304
wallet: { id, name, hasPassword },

source/renderer/app/types/walletExportTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ export type ExportedByronWallet = {
4141
status: WalletImportStatus,
4242
error: ?LocalizableError,
4343
},
44+
index: number,
4445
};

0 commit comments

Comments
 (0)