Skip to content

Commit bdd6ccd

Browse files
authored
Merge pull request #2979 from input-output-hk/feature/ddw-409-new-mnemonic-input
[DDW-409] New mnemonic input
2 parents 6d19133 + bbbf8cb commit bdd6ccd

28 files changed

+781
-503
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## vNext
44

5+
### Features
6+
7+
- Added new Mnemonic input component ([PR 2979](https://github.com/input-output-hk/daedalus/pull/2979))
8+
59
### Fixes
610

711
- Ensured non-recommended decimal place setting alert is correctly shown ([PR 3007](https://github.com/input-output-hk/daedalus/pull/3007))
@@ -46,6 +50,7 @@
4650

4751
### Chores
4852

53+
- Upgraded webpack to version 5 ([PR 2772](https://github.com/input-output-hk/daedalus/pull/2772))
4954
- Bumped vulnerable dependencies versions ([PR 2943](https://github.com/input-output-hk/daedalus/pull/2943))
5055
- Added support for Trezor firmware 2.5.1 ([PR 2991](https://github.com/input-output-hk/daedalus/pull/2991))
5156
- Added steps on how to link with `react-polymorph` and other external UI libraries ([PR 2948](https://github.com/input-output-hk/daedalus/pull/2948))

source/renderer/app/components/staking/redeem-itn-rewards/Step1ConfigurationDialog.scss

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,6 @@
2323

2424
.recoveryPhrase {
2525
margin-bottom: 20px;
26-
27-
:global {
28-
.SimpleOptions_option {
29-
padding: 14px 20px;
30-
}
31-
.SimpleAutocomplete_requiredWordsInfo {
32-
color: var(--rp-autocomplete-required-words-invert-color);
33-
}
34-
}
3526
}
3627

3728
.walletsDropdownWrapper {

source/renderer/app/components/staking/redeem-itn-rewards/Step1ConfigurationDialog.tsx

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,32 @@
11
import React, { Component } from 'react';
22
import { observer } from 'mobx-react';
33
import { get } from 'lodash';
4-
import classnames from 'classnames';
54
import vjf from 'mobx-react-form/lib/validators/VJF';
6-
import { Autocomplete } from 'react-polymorph/lib/components/Autocomplete';
7-
import { AutocompleteSkin } from 'react-polymorph/lib/skins/simple/AutocompleteSkin';
85
import { Checkbox } from 'react-polymorph/lib/components/Checkbox';
96
import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin';
107
import { Link } from 'react-polymorph/lib/components/Link';
118
import { LinkSkin } from 'react-polymorph/lib/skins/simple/LinkSkin';
129
import {
1310
defineMessages,
14-
intlShape,
15-
FormattedMessage,
1611
FormattedHTMLMessage,
12+
FormattedMessage,
13+
intlShape,
1714
} from 'react-intl';
1815
import { BigNumber } from 'bignumber.js';
1916
import Wallet from '../../../domains/Wallet';
20-
import {
21-
errorOrIncompleteMarker,
22-
validateMnemonics,
23-
} from '../../../utils/validations';
17+
import { validateMnemonics } from '../../../utils/validations';
2418
import DialogCloseButton from '../../widgets/DialogCloseButton';
2519
import WalletsDropdown from '../../widgets/forms/WalletsDropdown';
2620
import Dialog from '../../widgets/Dialog';
2721
import styles from './Step1ConfigurationDialog.scss';
2822
import ReactToolboxMobxForm from '../../../utils/ReactToolboxMobxForm';
29-
import globalMessages from '../../../i18n/global-messages';
3023
import LocalizableError from '../../../i18n/LocalizableError';
3124
import { ITN_WALLET_RECOVERY_PHRASE_WORD_COUNT } from '../../../config/cryptoConfig';
3225
import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../../config/timingConfig';
3326
import { MIN_REWARDS_REDEMPTION_RECEIVER_BALANCE } from '../../../config/stakingConfig';
3427
// @ts-ignore ts-migrate(2307) FIXME: Cannot find module '../../../assets/images/close-c... Remove this comment to see the full error message
3528
import closeCrossThin from '../../../assets/images/close-cross-thin.inline.svg';
29+
import { MnemonicInput } from '../../wallet/mnemonic-input';
3630

3731
const messages = defineMessages({
3832
title: {
@@ -167,7 +161,6 @@ class Step1ConfigurationDialog extends Component<Props, State> {
167161
wasRecoveryPhraseValidAtLeastOnce: false,
168162
};
169163

170-
recoveryPhraseAutocomplete: Autocomplete;
171164
form = new ReactToolboxMobxForm<FormFields>(
172165
{
173166
fields: {
@@ -217,6 +210,7 @@ class Step1ConfigurationDialog extends Component<Props, State> {
217210
},
218211
options: {
219212
validateOnChange: true,
213+
showErrorsOnChange: false,
220214
validationDebounceWait: FORM_VALIDATION_DEBOUNCE_WAIT,
221215
},
222216
}
@@ -258,7 +252,6 @@ class Step1ConfigurationDialog extends Component<Props, State> {
258252
suggestedMnemonics,
259253
openExternalLink,
260254
wallets,
261-
recoveryPhrase,
262255
error,
263256
} = this.props;
264257
const calculatedMinRewardsReceiverBalance = new BigNumber(
@@ -339,6 +332,8 @@ class Step1ConfigurationDialog extends Component<Props, State> {
339332
onClose={onClose}
340333
/>
341334
);
335+
const { reset, ...mnemonicInputProps } = recoveryPhraseField.bind();
336+
342337
return (
343338
<Dialog
344339
title={intl.formatMessage(messages.title)}
@@ -360,28 +355,16 @@ class Step1ConfigurationDialog extends Component<Props, State> {
360355
/>{' '}
361356
<FormattedHTMLMessage {...messages.description2} />
362357
</p>
363-
<Autocomplete
364-
{...recoveryPhraseField.bind()}
365-
ref={(autocomplete) => {
366-
this.recoveryPhraseAutocomplete = autocomplete;
367-
}}
368-
options={suggestedMnemonics}
369-
requiredSelections={[ITN_WALLET_RECOVERY_PHRASE_WORD_COUNT]}
370-
requiredSelectionsInfo={(required, actual) =>
371-
intl.formatMessage(globalMessages.knownMnemonicWordCount, {
372-
actual,
373-
required,
374-
})
375-
}
376-
maxSelections={ITN_WALLET_RECOVERY_PHRASE_WORD_COUNT}
377-
error={errorOrIncompleteMarker(recoveryPhraseField.error)}
378-
maxVisibleOptions={5}
379-
noResultsMessage={intl.formatMessage(messages.noResults)}
380-
className={styles.recoveryPhrase}
381-
skin={AutocompleteSkin}
382-
optionHeight={50}
383-
preselectedOptions={[...(recoveryPhrase || [])]}
384-
/>
358+
<div className={styles.recoveryPhrase}>
359+
<MnemonicInput
360+
{...mnemonicInputProps}
361+
label={intl.formatMessage(messages.recoveryPhraseInputLabel)}
362+
availableWords={suggestedMnemonics}
363+
wordCount={ITN_WALLET_RECOVERY_PHRASE_WORD_COUNT}
364+
error={recoveryPhraseField.error}
365+
reset={form.resetting}
366+
/>
367+
</div>
385368
<div className={styles.walletsDropdownWrapper}>
386369
<WalletsDropdown
387370
{...walletsDropdownField.bind()}

source/renderer/app/components/wallet/WalletBackupDialog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class WalletBackupDialog extends Component<Props> {
8484
<WalletRecoveryPhraseEntryDialog
8585
isTermOfflineAccepted={isTermOfflineAccepted}
8686
enteredPhrase={enteredPhrase}
87+
recoveryPhrase={recoveryPhrase}
8788
canFinishBackup={canFinishBackup}
8889
isTermRecoveryAccepted={isTermRecoveryAccepted}
8990
isValid={isValid}

source/renderer/app/components/wallet/WalletRestoreDialog.tsx

Lines changed: 17 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
44
import classnames from 'classnames';
55
import { Autocomplete } from 'react-polymorph/lib/components/Autocomplete';
66
import { Input } from 'react-polymorph/lib/components/Input';
7-
import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl';
7+
import { defineMessages, FormattedHTMLMessage, intlShape } from 'react-intl';
88
import vjf from 'mobx-react-form/lib/validators/VJF';
99
import SVGInline from 'react-svg-inline';
1010
import { PopOver } from 'react-polymorph/lib/components/PopOver';
@@ -16,10 +16,9 @@ import ReactToolboxMobxForm, {
1616
import DialogCloseButton from '../widgets/DialogCloseButton';
1717
import Dialog from '../widgets/Dialog';
1818
import {
19-
isValidWalletName,
20-
isValidSpendingPassword,
2119
isValidRepeatPassword,
22-
errorOrIncompleteMarker,
20+
isValidSpendingPassword,
21+
isValidWalletName,
2322
validateMnemonics,
2423
} from '../../utils/validations';
2524
import globalMessages from '../../i18n/global-messages';
@@ -28,8 +27,8 @@ import { FORM_VALIDATION_DEBOUNCE_WAIT } from '../../config/timingConfig';
2827
import styles from './WalletRestoreDialog.scss';
2928
import { submitOnEnter } from '../../utils/form';
3029
import {
31-
WALLET_RESTORE_TYPES,
3230
RECOVERY_PHRASE_WORD_COUNT_OPTIONS,
31+
WALLET_RESTORE_TYPES,
3332
} from '../../config/walletsConfig';
3433
import {
3534
LEGACY_WALLET_RECOVERY_PHRASE_WORD_COUNT,
@@ -38,6 +37,7 @@ import {
3837
} from '../../config/cryptoConfig';
3938
import infoIconInline from '../../assets/images/info-icon.inline.svg';
4039
import LoadingSpinner from '../widgets/LoadingSpinner';
40+
import { MnemonicInput } from './mnemonic-input';
4141

4242
const messages = defineMessages({
4343
title: {
@@ -87,23 +87,11 @@ const messages = defineMessages({
8787
description:
8888
'Label for the recovery phrase input on the wallet restore dialog.',
8989
},
90-
recoveryPhraseInputHint: {
91-
id: 'wallet.restore.dialog.recovery.phrase.input.hint',
92-
defaultMessage: '!!!Enter recovery phrase',
93-
description:
94-
'Hint "Enter recovery phrase" for the recovery phrase input on the wallet restore dialog.',
95-
},
9690
newLabel: {
9791
id: 'wallet.restore.dialog.recovery.phrase.newLabel',
9892
defaultMessage: '!!!New',
9993
description: 'Label "new" on the wallet restore dialog.',
10094
},
101-
recoveryPhraseNoResults: {
102-
id: 'wallet.restore.dialog.recovery.phrase.input.noResults',
103-
defaultMessage: '!!!No results',
104-
description:
105-
'"No results" message for the recovery phrase input search results.',
106-
},
10795
importButtonLabel: {
10896
id: 'wallet.restore.dialog.restore.wallet.button.label',
10997
defaultMessage: '!!!Restore wallet',
@@ -167,19 +155,6 @@ const messages = defineMessages({
167155
description:
168156
'Label for the shielded recovery phrase input on the wallet restore dialog.',
169157
},
170-
shieldedRecoveryPhraseInputHint: {
171-
id: 'wallet.restore.dialog.shielded.recovery.phrase.input.hint',
172-
defaultMessage:
173-
'!!!Enter your {numberOfWords}-word paper wallet recovery phrase',
174-
description:
175-
'Hint "Enter your 27-word paper wallet recovery phrase." for the recovery phrase input on the wallet restore dialog.',
176-
},
177-
shieldedRecoveryPhraseInputPlaceholder: {
178-
id: 'wallet.restore.dialog.shielded.recovery.phrase.input.placeholder',
179-
defaultMessage: '!!!Enter word #{wordNumber}',
180-
description:
181-
'Placeholder "Enter word #" for the recovery phrase input on the wallet restore dialog.',
182-
},
183158
restorePaperWalletButtonLabel: {
184159
id: 'wallet.restore.dialog.paper.wallet.button.label',
185160
defaultMessage: '!!!Restore paper wallet',
@@ -326,6 +301,7 @@ class WalletRestoreDialog extends Component<Props, State> {
326301
},
327302
options: {
328303
validateOnChange: true,
304+
showErrorsOnChange: false,
329305
validationDebounceWait: FORM_VALIDATION_DEBOUNCE_WAIT,
330306
},
331307
}
@@ -364,8 +340,6 @@ class WalletRestoreDialog extends Component<Props, State> {
364340
recoveryPhraseField.debouncedValidation.cancel();
365341
recoveryPhraseField.reset();
366342
recoveryPhraseField.showErrors(false);
367-
// Autocomplete has to be reset manually
368-
this.recoveryPhraseAutocomplete.clear();
369343
};
370344

371345
render() {
@@ -406,6 +380,8 @@ class WalletRestoreDialog extends Component<Props, State> {
406380
'yoroiTab',
407381
this.isYoroi() ? styles.activeButton : '',
408382
]);
383+
const { reset, ...mnemonicInputProps } = recoveryPhraseField.bind();
384+
409385
return (
410386
<Dialog
411387
className={dialogClasses}
@@ -554,44 +530,18 @@ class WalletRestoreDialog extends Component<Props, State> {
554530
]}
555531
/>
556532
)}
557-
558-
<Autocomplete
559-
{...recoveryPhraseField.bind()}
560-
ref={(autocomplete) => {
561-
this.recoveryPhraseAutocomplete = autocomplete;
562-
}}
533+
<MnemonicInput
534+
{...mnemonicInputProps}
563535
label={
564-
!this.isCertificate()
565-
? intl.formatMessage(messages.recoveryPhraseInputLabel)
566-
: intl.formatMessage(messages.shieldedRecoveryPhraseInputLabel)
567-
}
568-
placeholder={
569-
!this.isCertificate()
570-
? intl.formatMessage(messages.recoveryPhraseInputHint)
571-
: intl.formatMessage(
572-
messages.shieldedRecoveryPhraseInputPlaceholder,
573-
{
574-
wordNumber: recoveryPhraseField.value.length + 1,
575-
}
576-
)
536+
this.isCertificate()
537+
? intl.formatMessage(messages.shieldedRecoveryPhraseInputLabel)
538+
: intl.formatMessage(messages.recoveryPhraseInputLabel)
577539
}
578-
options={suggestedMnemonics}
579-
requiredSelections={[RECOVERY_PHRASE_WORD_COUNT_OPTIONS[walletType]]}
580-
requiredSelectionsInfo={(required, actual) =>
581-
intl.formatMessage(globalMessages.knownMnemonicWordCount, {
582-
actual,
583-
required,
584-
})
585-
}
586-
maxSelections={RECOVERY_PHRASE_WORD_COUNT_OPTIONS[walletType]}
587-
error={errorOrIncompleteMarker(recoveryPhraseField.error)}
588-
maxVisibleOptions={5}
589-
noResultsMessage={intl.formatMessage(
590-
messages.recoveryPhraseNoResults
591-
)}
592-
optionHeight={50}
540+
availableWords={suggestedMnemonics}
541+
wordCount={RECOVERY_PHRASE_WORD_COUNT_OPTIONS[walletType]}
542+
error={recoveryPhraseField.error}
543+
reset={form.resetting}
593544
/>
594-
595545
<div className={styles.spendingPasswordWrapper}>
596546
<div className={styles.passwordSectionLabel}>
597547
{intl.formatMessage(messages.passwordSectionLabel)}

source/renderer/app/components/wallet/backup-recovery/WalletRecoveryPhraseDisplayDialog.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import React, { Component } from 'react';
22
import { observer } from 'mobx-react';
33
import classnames from 'classnames';
4-
import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl';
5-
import WalletRecoveryPhraseMnemonic from './WalletRecoveryPhraseMnemonic';
4+
import { defineMessages, FormattedHTMLMessage, intlShape } from 'react-intl';
65
import DialogCloseButton from '../../widgets/DialogCloseButton';
76
import Dialog from '../../widgets/Dialog';
87
import WalletRecoveryInstructions from './WalletRecoveryInstructions';
98
import globalMessages from '../../../i18n/global-messages';
109
import styles from './WalletRecoveryPhraseDisplayDialog.scss';
1110
import { WALLET_RECOVERY_PHRASE_WORD_COUNT } from '../../../config/cryptoConfig';
1211
import LoadingSpinner from '../../widgets/LoadingSpinner';
12+
import { MnemonicInput } from '../mnemonic-input';
1313

1414
const messages = defineMessages({
1515
backupInstructions: {
@@ -64,6 +64,7 @@ class WalletRecoveryPhraseDisplayDialog extends Component<Props> {
6464
primary: true,
6565
},
6666
];
67+
const mnemonicValues = recoveryPhrase.split(' ');
6768
return (
6869
<Dialog
6970
className={dialogClasses}
@@ -83,7 +84,11 @@ class WalletRecoveryPhraseDisplayDialog extends Component<Props> {
8384
/>
8485
}
8586
/>
86-
<WalletRecoveryPhraseMnemonic phrase={recoveryPhrase} />
87+
<MnemonicInput
88+
disabled
89+
value={mnemonicValues}
90+
wordCount={mnemonicValues.length}
91+
/>
8792
</Dialog>
8893
);
8994
}

0 commit comments

Comments
 (0)