diff --git a/app/actions/wallet-backup-actions.js b/app/actions/wallet-backup-actions.js index 0a9c651edf..877011252a 100644 --- a/app/actions/wallet-backup-actions.js +++ b/app/actions/wallet-backup-actions.js @@ -15,4 +15,5 @@ export default class WalletBackupActions { restartWalletBackup: Action = new Action(); cancelWalletBackup: Action = new Action(); finishWalletBackup: Action = new Action(); + removeOneMnemonicWord: Action = new Action(); } diff --git a/app/components/wallet/WalletBackupDialog.js b/app/components/wallet/WalletBackupDialog.js index d2dab2aa41..2fc0dcd0af 100644 --- a/app/components/wallet/WalletBackupDialog.js +++ b/app/components/wallet/WalletBackupDialog.js @@ -11,7 +11,6 @@ type Props = { isPrivacyNoticeAccepted: boolean, countdownRemaining: number, isTermDeviceAccepted: boolean, - canFinishBackup: boolean, isTermRecoveryAccepted: boolean, isValid: boolean, isSubmitting: boolean, @@ -28,6 +27,8 @@ type Props = { onClear: Function, onFinishBackup: Function, onRestartBackup: Function, + removeWord: Function, + hasWord: Function }; @observer @@ -40,7 +41,7 @@ export default class WalletBackupDialog extends Component { countdownRemaining, onAcceptPrivacyNotice, onContinue, recoveryPhrase, onStartWalletBackup, isTermDeviceAccepted, - enteredPhrase, canFinishBackup, + enteredPhrase, removeWord, hasWord, isTermRecoveryAccepted, isValid, isSubmitting, onAcceptTermDevice, onAcceptTermRecovery, onAddWord, onClear, onFinishBackup, @@ -75,7 +76,6 @@ export default class WalletBackupDialog extends Component { { onAddWord={onAddWord} onCancelBackup={onCancelBackup} onClear={onClear} - onFinishBackup={onFinishBackup} onRestartBackup={onRestartBackup} recoveryPhraseSorted={recoveryPhraseSorted} + onFinishBackup={onFinishBackup} + removeWord={removeWord} + hasWord={hasWord} /> ); } diff --git a/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.js b/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.js index 112227dc70..e598219d48 100644 --- a/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.js +++ b/app/components/wallet/backup-recovery/WalletRecoveryPhraseEntryDialog.js @@ -20,6 +20,11 @@ const messages = defineMessages({ defaultMessage: '!!!Tap each word in the correct order to verify your recovery phrase', description: 'Instructions for verifying wallet recovery phrase on dialog for entering wallet recovery phrase.' }, + buttonLabelRemoveLast: { + id: 'wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast', + defaultMessage: '!!!Remove last', + description: 'Label for button "Remove Last" on wallet backup dialog' + }, buttonLabelConfirm: { id: 'wallet.recovery.phrase.show.entry.dialog.button.labelConfirm', defaultMessage: '!!!Confirm', @@ -51,13 +56,14 @@ type Props = { isTermRecoveryAccepted: boolean, isSubmitting: boolean, onAddWord: Function, - canFinishBackup: boolean, onClear: Function, onAcceptTermDevice: Function, onAcceptTermRecovery: Function, onRestartBackup: Function, onCancelBackup: Function, onFinishBackup: Function, + removeWord: Function, + hasWord: Function }; @observer @@ -80,10 +86,11 @@ export default class WalletRecoveryPhraseEntryDialog extends Component { onClear, onAcceptTermDevice, onAcceptTermRecovery, - canFinishBackup, + removeWord, onRestartBackup, onCancelBackup, - onFinishBackup + onFinishBackup, + hasWord } = this.props; const dialogClasses = classnames([ styles.component, @@ -94,19 +101,25 @@ export default class WalletRecoveryPhraseEntryDialog extends Component { const actions = []; - actions.push({ - className: isSubmitting ? styles.isSubmitting : null, - label: intl.formatMessage(messages.buttonLabelConfirm), - onClick: onFinishBackup, - disabled: !canFinishBackup, - primary: true - }); - // Only show "Clear" button when user is not yet done with entering mnemonic if (!isValid) { + actions.unshift({ + label: intl.formatMessage(messages.buttonLabelRemoveLast), + onClick: removeWord, + disabled: !hasWord, + primary: true + }); actions.unshift({ label: intl.formatMessage(messages.buttonLabelClear), onClick: onClear, + primary: true + }); + } else { + actions.push({ + className: isSubmitting ? styles.isSubmitting : null, + label: intl.formatMessage(messages.buttonLabelConfirm), + onClick: onFinishBackup, + primary: true }); } diff --git a/app/containers/wallet/dialogs/WalletBackupDialogContainer.js b/app/containers/wallet/dialogs/WalletBackupDialogContainer.js index 0af036b423..16a935f6c0 100644 --- a/app/containers/wallet/dialogs/WalletBackupDialogContainer.js +++ b/app/containers/wallet/dialogs/WalletBackupDialogContainer.js @@ -36,6 +36,7 @@ export default class WalletBackupDialogContainer extends Component { acceptWalletBackupTermRecovery, restartWalletBackup, finishWalletBackup, + removeOneMnemonicWord, acceptPrivacyNoticeForWalletBackup, continueToRecoveryPhraseForWalletBackup } = actions.walletBackup; @@ -57,7 +58,7 @@ export default class WalletBackupDialogContainer extends Component { // Props for WalletRecoveryPhraseEntryDialog isTermDeviceAccepted={isTermDeviceAccepted} enteredPhrase={enteredPhrase} - canFinishBackup={isRecoveryPhraseValid && isTermDeviceAccepted && isTermRecoveryAccepted} + hasWord={() => recoveryPhraseWords.length > 0} isTermRecoveryAccepted={isTermRecoveryAccepted} isValid={isRecoveryPhraseValid} isSubmitting={createWalletRequest.isExecuting} @@ -68,6 +69,9 @@ export default class WalletBackupDialogContainer extends Component { onFinishBackup={() => { finishWalletBackup.trigger(); }} + removeWord={() => { + removeOneMnemonicWord.trigger(); + }} onRestartBackup={restartWalletBackup.trigger} recoveryPhraseSorted={recoveryPhraseSorted} /> diff --git a/app/i18n/locales/de-DE.json b/app/i18n/locales/de-DE.json index 73bbfdb243..512a64c86b 100644 --- a/app/i18n/locales/de-DE.json +++ b/app/i18n/locales/de-DE.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "Teile diese Wallet-Adresse mit, um Zahlungen zu erhalten. Um deine Privatsphäre zu schützen, werden jedes Mal neue Adressen erzeugt, sobald sie verwendet wurden.", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "Löschen", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "Bestätigen", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "!!!Remove last", "wallet.redeem.choices.tab.title.forceVended": "do not translate", "wallet.redeem.choices.tab.title.paperVended": "do not translate", "wallet.redeem.choices.tab.title.recoveryForceVended": "do not translate", diff --git a/app/i18n/locales/en-US.json b/app/i18n/locales/en-US.json index 8ddebb6f0c..be04c4fb25 100644 --- a/app/i18n/locales/en-US.json +++ b/app/i18n/locales/en-US.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "Share this wallet address to receive payments. To protect your privacy, new addresses are generated automatically once you use them.", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "Clear", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "Confirm", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "Remove last", "wallet.redeem.choices.tab.title.forceVended": "Force vended", "wallet.redeem.choices.tab.title.paperVended": "Paper vended", "wallet.redeem.choices.tab.title.recoveryForceVended": "Recovery - force vended", diff --git a/app/i18n/locales/fr-FR.json b/app/i18n/locales/fr-FR.json index 485791f961..da1f3a178a 100644 --- a/app/i18n/locales/fr-FR.json +++ b/app/i18n/locales/fr-FR.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "Partagez cette adresse du wallet pour recevoir des paiements. Afin d'assurer votre sécurité, de nouvelles adresses sont générées à chaque utilisation.", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "Effacer", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "Confirmer", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "!!!Remove last", "wallet.redeem.choices.tab.title.forceVended": "do not translate", "wallet.redeem.choices.tab.title.paperVended": "do not translate", "wallet.redeem.choices.tab.title.recoveryForceVended": "do not translate", diff --git a/app/i18n/locales/ja-JP.json b/app/i18n/locales/ja-JP.json index dbf6dadedb..ee007a3e2e 100644 --- a/app/i18n/locales/ja-JP.json +++ b/app/i18n/locales/ja-JP.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "上記アドレスを送信元に共有し、ADAの送金を依頼してください。セキュリティーの観点から、一度使用したアドレスを再度使用することはできません。新たなアドレスが自動的に生成されます。", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "クリア", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "確認", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "!!!Remove last", "wallet.redeem.choices.tab.title.forceVended": "強制ヴェンド", "wallet.redeem.choices.tab.title.paperVended": "紙ヴェンド", "wallet.redeem.choices.tab.title.recoveryForceVended": "リカバリー:強制ヴェンド", @@ -376,5 +377,36 @@ "wallet.transaction.type": "{currency} 取引", "wallet.transaction.type.exchange": "交換", "wallet.transaction.type.intrawallet": "{currency} ウォレット内トランザクション", - "wallet.transaction.type.multiparty": "{currency} 複数人数トランザクション" + "wallet.transaction.type.multiparty": "{currency} 複数人数トランザクション", + "wallet.trezor.dialog.common.step.link.helpYoroiWithTrezor": "https://youtu.be/Dp0wXwtToX0", + "wallet.trezor.dialog.common.step.link.helpYoroiWithTrezor.text": "Trezorを使ったヨロイの使用方法はこちら", + "wallet.trezor.dialog.connect.button.label": "接続", + "wallet.trezor.dialog.next.button.label": "次へ", + "wallet.trezor.dialog.save.button.label": "保存", + "wallet.trezor.dialog.step.about.introText.line.1": "ハードウェアウォレットは小さなUSB機器で、ウォレットのセキュリティを高めます。", + "wallet.trezor.dialog.step.about.introText.line.2": "秘密鍵がハードウェアウォレットに残らないので、さらに高い安全性を担保します。", + "wallet.trezor.dialog.step.about.introText.line.3": "お使いのコンピューターがマルウェアやフィッシング詐欺などにより損なわれたとしても、資金は守られます。", + "wallet.trezor.dialog.step.about.label": "概要", + "wallet.trezor.dialog.step.about.prerequisite.1.part1": " ", + "wallet.trezor.dialog.step.about.prerequisite.1.part2.link": "https://github.com/trezor/trezor-core/blob/be58549fd9fe53df237a7318f754b5ec23975425/ChangeLog#L12", + "wallet.trezor.dialog.step.about.prerequisite.1.part2.link.text": "Trezor Model T のバージョン2.0.8", + "wallet.trezor.dialog.step.about.prerequisite.1.part3": "以上対応。", + "wallet.trezor.dialog.step.about.prerequisite.2": "あらかじめTrezorの初期設定を行ってください。", + "wallet.trezor.dialog.step.about.prerequisite.3": "Trezorデバイスの画面をロック解除してください。", + "wallet.trezor.dialog.step.about.prerequisite.4": "プロセスの実行中は、コンピューターをインターネットに接続したままにしてください。", + "wallet.trezor.dialog.step.about.prerequisite.5": "プロセスの実行中は、Trezorデバイスをインターネットに接続したままにしてください。", + "wallet.trezor.dialog.step.about.prerequisite.header": "前提条件", + "wallet.trezor.dialog.step.connect.introText.line.1": "Trezorデバイスをお使いのコンピューターに接続してから「接続」ボタンを押してください。", + "wallet.trezor.dialog.step.connect.introText.line.2": "新しいタブが開かれます。表示される指示に従ってください。", + "wallet.trezor.dialog.step.connect.introText.line.3": "このプロセスにより、CARDANOの秘密鍵がヨロイに共有されます。", + "wallet.trezor.dialog.step.connect.label": "接続", + "wallet.trezor.dialog.step.save.error.101": "保存に失敗しました。インターネット接続を確認して再試行してください。", + "wallet.trezor.dialog.step.save.label": "保存", + "wallet.trezor.dialog.step.save.walletName.hint": "ウォレット名を入力", + "wallet.trezor.dialog.step.save.walletName.info": "Trezorデバイス名を読み出しました。このまま使用するか、任意の名前を入力してください。", + "wallet.trezor.dialog.step.save.walletName.label": "ウォレット名", + "wallet.trezor.dialog.title.label": "Trezorハードウェアウォレットに接続", + "wallet.trezor.error.101": "Trezor.ioへの接続に失敗しました。インターネット接続を確認して再試行してください。", + "wallet.trezor.error.102": "アクセス許可が得られませんでした。再試行してください。", + "wallet.trezor.error.103": "キャンセルされました。再試行してください。" } \ No newline at end of file diff --git a/app/i18n/locales/ko-KR.json b/app/i18n/locales/ko-KR.json index c69136635a..4c10dd190e 100644 --- a/app/i18n/locales/ko-KR.json +++ b/app/i18n/locales/ko-KR.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "돈을 받으려면 이 지갑 주소를 공유하십시오. 개인정보를 보호하기 위해 새 주소를 사용하면 새 주소가 자동으로 생성됩니다.", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "지우기", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "확인", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "!!!Remove last", "wallet.redeem.choices.tab.title.forceVended": "강제 벤드", "wallet.redeem.choices.tab.title.paperVended": "페이퍼 벤드", "wallet.redeem.choices.tab.title.recoveryForceVended": "복구 - 강제 벤드", diff --git a/app/i18n/locales/ru-RU.json b/app/i18n/locales/ru-RU.json index 610b923d3f..58ad7a929e 100644 --- a/app/i18n/locales/ru-RU.json +++ b/app/i18n/locales/ru-RU.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "Поделитесь данным адресом кошелька для получения платежей. Для защиты Вашей конфиденциальности, новые адреса генерируются автоматически после того, как Вы их использовали.", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "Очистить", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "Подтвердить", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "!!!Remove last", "wallet.redeem.choices.tab.title.forceVended": "Форсированная продажа", "wallet.redeem.choices.tab.title.paperVended": "Продажа документа", "wallet.redeem.choices.tab.title.recoveryForceVended": "Восстановление - форсированная продажа", diff --git a/app/i18n/locales/zh-Hans.json b/app/i18n/locales/zh-Hans.json index a6e1deb9b8..52d3ef4ac7 100644 --- a/app/i18n/locales/zh-Hans.json +++ b/app/i18n/locales/zh-Hans.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "共享此钱包地址以接收付款。为保护您的隐私,在您使用地址后会自动生成新地址。", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "清除", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "确认", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "!!!Remove last", "wallet.redeem.choices.tab.title.forceVended": "强行执行", "wallet.redeem.choices.tab.title.paperVended": "Paper執行", "wallet.redeem.choices.tab.title.recoveryForceVended": "强行恢复", diff --git a/app/i18n/locales/zh-Hant.json b/app/i18n/locales/zh-Hant.json index fb56aeb340..fdf29634f7 100644 --- a/app/i18n/locales/zh-Hant.json +++ b/app/i18n/locales/zh-Hant.json @@ -244,6 +244,7 @@ "wallet.receive.page.walletReceiveInstructions": "分享此錢包地址,以接收付款。為保護您的隱私,一旦使用,就會自動產生新地址。", "wallet.recovery.phrase.show.entry.dialog.button.labelClear": "清除", "wallet.recovery.phrase.show.entry.dialog.button.labelConfirm": "確認", + "wallet.recovery.phrase.show.entry.dialog.button.labelRemoveLast": "!!!Remove last", "wallet.redeem.choices.tab.title.forceVended": "強行執行", "wallet.redeem.choices.tab.title.paperVended": "Paper執行", "wallet.redeem.choices.tab.title.recoveryForceVended": "強行恢復", diff --git a/app/stores/toplevel/WalletBackupStore.js b/app/stores/toplevel/WalletBackupStore.js index 05bdb539b0..d69f34dca9 100644 --- a/app/stores/toplevel/WalletBackupStore.js +++ b/app/stores/toplevel/WalletBackupStore.js @@ -42,6 +42,7 @@ class WalletBackupStore extends Store { a.restartWalletBackup.listen(this._restartWalletBackup); a.cancelWalletBackup.listen(this._cancelWalletBackup); a.finishWalletBackup.listen(this._finishWalletBackup); + a.removeOneMnemonicWord.listen(this._removeOneWord); } @action _initiateWalletBackup = (params: { recoveryPhrase: Array }) => { @@ -86,7 +87,7 @@ class WalletBackupStore extends Store { @action _addWordToWalletBackupVerification = (params: { word: string, index: number }) => { const { word, index } = params; - this.enteredPhrase.push({ word }); + this.enteredPhrase.push({ word, index }); const pickedWord = this.recoveryPhraseSorted[index]; if (pickedWord && pickedWord.word === word) pickedWord.isActive = false; }; @@ -98,6 +99,14 @@ class WalletBackupStore extends Store { ); }; + @action _removeOneWord = () => { + if (!this.enteredPhrase) { + return; + } + const poppedWord = this.enteredPhrase.pop(); + this.recoveryPhraseSorted[poppedWord.index].isActive = true; + }; + @computed get isRecoveryPhraseValid(): boolean { return ( this.recoveryPhraseWords.reduce((words, { word }) => words + word, '') ===