diff --git a/CHANGELOG.md b/CHANGELOG.md index 5555a6ccad..441854b42c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Changelog ### Fixes -- Handle duplicate wallets in import process ([PR 1985](https://github.com/input-output-hk/daedalus/pull/1985)) +- Handle duplicate wallets in import process ([PR 1985](https://github.com/input-output-hk/daedalus/pull/1985), [PR 1989](https://github.com/input-output-hk/daedalus/pull/1989), [PR 1991](https://github.com/input-output-hk/daedalus/pull/1991)) - Treat wallets with 100% syncing progress as synced wallets ([PR 1984](https://github.com/input-output-hk/daedalus/pull/1984)) - Fixed `cardano-node` / `jormungandr` and `cardano-wallet` info on the "Diagnostics" screen ([PR 1980](https://github.com/input-output-hk/daedalus/pull/1980)) - Persist "Blank screen fix" / "--safe-mode" flag between Daedalus restarts ([PR 1979](https://github.com/input-output-hk/daedalus/pull/1979)) diff --git a/source/main/cardano/utils.js b/source/main/cardano/utils.js index 654e9a8b5e..70851c2fb8 100644 --- a/source/main/cardano/utils.js +++ b/source/main/cardano/utils.js @@ -191,8 +191,16 @@ export const exportWallets = async ( isFlight, }); - let legacySecretKeyPath = path.join(exportSourcePath, legacySecretKey); - let legacyWalletDBPath = path.join(exportSourcePath, legacyWalletDB); + let legacySecretKeyPath; + let legacyWalletDBPath; + + if (exportSourcePath.endsWith('secret.key')) { + legacySecretKeyPath = exportSourcePath; + legacyWalletDBPath = path.join(exportSourcePath, '../..', legacyWalletDB); + } else { + legacySecretKeyPath = path.join(exportSourcePath, legacySecretKey); + legacyWalletDBPath = path.join(exportSourcePath, legacyWalletDB); + } // In case of Daedalus Flight build we need to copy over // legacySecretKey and legacyWalletDB from mainnet state dir @@ -258,6 +266,11 @@ export const exportWallets = async ( errors, }); + // Remove Daedalus Flight migration data + if (isFlight) { + await removeMigrationData(stateDir); + } + return Promise.resolve({ wallets, errors }); }; @@ -294,7 +307,9 @@ const prepareMigrationData = async ( legacySecretKeyPath, }); } else { - logger.info('ipcMain: Secret key file not found'); + logger.info('ipcMain: Secret key file not found', { + legacySecretKey, + }); } const legacyWalletDBFullPath = `${legacyWalletDB}-acid`; @@ -315,7 +330,9 @@ const prepareMigrationData = async ( legacyWalletDBPath, }); } else { - logger.info('ipcMain: Wallet db directory not found'); + logger.info('ipcMain: Wallet db directory not found', { + legacyWalletDBFullPath, + }); } resolve({ legacySecretKeyPath, legacyWalletDBPath }); } catch (error) { @@ -351,6 +368,24 @@ const prepareMigrationData = async ( } }); +const removeMigrationData = async (stateDir: string) => { + try { + // Remove migration data dir if it exists + const migrationDataDirPath = path.join(stateDir, 'migration-data'); + logger.info('ipcMain: Removing Daedalus Flight migration data...', { + migrationDataDirPath, + }); + await fs.remove(migrationDataDirPath); + logger.info('ipcMain: Removed Daedalus Flight migration data', { + migrationDataDirPath, + }); + } catch (error) { + logger.info('ipcMain: Removing Daedalus Flight migration data failed', { + error, + }); + } +}; + const showExportWalletsWarning = ( mainWindow: BrowserWindow, locale: string diff --git a/source/renderer/app/actions/wallet-migration-actions.js b/source/renderer/app/actions/wallet-migration-actions.js index d074672c2e..43de8a5089 100644 --- a/source/renderer/app/actions/wallet-migration-actions.js +++ b/source/renderer/app/actions/wallet-migration-actions.js @@ -1,5 +1,6 @@ // @flow import Action from './lib/Action'; +import type { ImportFromOption } from '../types/walletExportTypes'; export default class WalletMigrationActions { startMigration: Action = new Action(); @@ -8,5 +9,8 @@ export default class WalletMigrationActions { toggleWalletImportSelection: Action<{ index: number }> = new Action(); updateWalletName: Action<{ index: number, name: string }> = new Action(); nextStep: Action = new Action(); - selectExportSourcePath: Action = new Action(); + selectExportSourcePath: Action<{ + importFrom: ImportFromOption, + }> = new Action(); + resetExportSourcePath: Action = new Action(); } diff --git a/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js b/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js index 659f45d10c..0fb13f5b17 100644 --- a/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js +++ b/source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js @@ -12,10 +12,13 @@ import { InputSkin } from 'react-polymorph/lib/skins/simple/InputSkin'; import SVGInline from 'react-svg-inline'; import classNames from 'classnames'; import styles from './WalletImportFileDialog.scss'; +import RadioSet from '../../widgets/RadioSet'; import DialogCloseButton from '../../widgets/DialogCloseButton'; import closeCrossThin from '../../../assets/images/close-cross-thin.inline.svg'; import penIcon from '../../../assets/images/pen.inline.svg'; import LoadingSpinner from '../../widgets/LoadingSpinner'; +import { ImportFromOptions } from '../../../types/walletExportTypes'; +import type { ImportFromOption } from '../../../types/walletExportTypes'; const messages = defineMessages({ title: { @@ -35,18 +38,30 @@ const messages = defineMessages({ defaultMessage: '!!!Select Daedalus state folder:', description: 'Select Daedalus state folder:', }, + secretFileLabel: { + id: 'wallet.import.file.dialog.secretFileLabel', + defaultMessage: "!!!Select Daedalus 'secret.key' file:", + description: "Select Daedalus 'secret.key' file:", + }, buttonLabel: { id: 'wallet.import.file.dialog.buttonLabel', defaultMessage: '!!!Import wallets', description: 'Import wallets', }, - noWallets: { - id: 'wallet.import.file.dialog.noWallets', + stateDirNoWallets: { + id: 'wallet.import.file.dialog.stateDirNoWallets', defaultMessage: '!!!No wallets found. Make sure you have selected a Daedalus state directory which contains the ‘Secrets’ or `Secrets-1.0` folder with a `secret.key` file inside.', description: 'No wallets found. Make sure you have selected a Daedalus state directory which contains the ‘Secrets’ or `Secrets-1.0` folder with a `secret.key` file inside.', }, + secretFileNoWallets: { + id: 'wallet.import.file.dialog.secretFileNoWallets', + defaultMessage: + '!!!No wallets found. Make sure you have selected a valid `secret.key` file.', + description: + 'No wallets found. Make sure you have selected a valid `secret.key` file.', + }, linkLabel: { id: 'wallet.import.file.dialog.linkLabel', defaultMessage: '!!!Learn more', @@ -58,6 +73,21 @@ const messages = defineMessages({ '!!!https://iohk.zendesk.com/hc/en-us/articles/900000623463', description: '"Learn more" link URL on the wallet import file dialog', }, + importFromLabel: { + id: 'wallet.import.file.dialog.importFromLabel', + defaultMessage: '!!!Import from:', + description: 'Import from:', + }, + stateDirOptionLabel: { + id: 'wallet.import.file.dialog.stateDirOptionLabel', + defaultMessage: '!!!Daedalus state directory', + description: 'Daedalus state directory', + }, + secretFileOptionLabel: { + id: 'wallet.import.file.dialog.secretFileOptionLabel', + defaultMessage: "!!!Daedalus 'secret.key' file", + description: "Daedalus 'secret.key' file", + }, }); type Props = { @@ -68,24 +98,48 @@ type Props = { onClose: Function, onOpenExternalLink: Function, onSelectExportSourcePath: Function, + onResetExportSourcePath: Function, exportSourcePath: string, + defaultExportSourcePath: string, +}; + +type State = { + importFrom: ImportFromOption, }; @observer -export default class WalletImportFileDialog extends Component { +export default class WalletImportFileDialog extends Component { static contextTypes = { intl: intlShape.isRequired, }; - stateFolderInput: Input; + state = { + importFrom: ImportFromOptions.STATE_DIR, + }; + + importPathInput: Input; componentWillMount() { // Reset migration data this.props.onOpen(); } + onSetImportFromOption = (importFrom: ImportFromOption) => { + if (this.state.importFrom !== importFrom) { + this.props.onResetExportSourcePath(); + this.setState({ importFrom }); + } + }; + + isImportFromStateDir = (importFrom: ImportFromOption) => + importFrom === ImportFromOptions.STATE_DIR; + + isImportFromSecretFile = (importFrom: ImportFromOption) => + importFrom === ImportFromOptions.SECRET_FILE; + render() { const { intl } = this.context; + const { importFrom } = this.state; const { exportErrors, isSubmitting, @@ -94,23 +148,27 @@ export default class WalletImportFileDialog extends Component { onOpenExternalLink, onSelectExportSourcePath, exportSourcePath, + defaultExportSourcePath, } = this.props; const title = intl.formatMessage(messages.title); const description = ; const stateFolderLabel = intl.formatMessage(messages.stateFolderLabel); + const secretFileLabel = intl.formatMessage(messages.secretFileLabel); const buttonLabel = !isSubmitting ? ( intl.formatMessage(messages.buttonLabel) ) : ( ); const linkLabel = intl.formatMessage(messages.linkLabel); - const noWalletError = intl.formatMessage(messages.noWallets); + const noWalletError = intl.formatMessage( + messages[`${importFrom}NoWallets`] + ); const onLinkClick = () => onOpenExternalLink(intl.formatMessage(messages.linkUrl)); const resetErrorCheck = - this.stateFolderInput && - this.stateFolderInput.inputElement.current.value !== exportSourcePath; + this.importPathInput && + this.importPathInput.inputElement.current.value !== exportSourcePath; const error = !resetErrorCheck && exportErrors !== ''; const inputClasses = classNames([ @@ -119,7 +177,11 @@ export default class WalletImportFileDialog extends Component { ]); const buttonClasses = classNames(styles.actionButton, [ - isSubmitting || error ? styles.disabled : null, + isSubmitting || + error || + (this.isImportFromSecretFile(importFrom) && !exportSourcePath) + ? styles.disabled + : null, ]); return ( @@ -141,39 +203,79 @@ export default class WalletImportFileDialog extends Component {
{title}
{description}
+ +
+ { + const importFromOption: ImportFromOption = + ImportFromOptions[key]; + return { + key: importFromOption, + label: intl.formatMessage( + messages[`${importFromOption}OptionLabel`] + ), + selected: importFrom === importFromOption, + onChange: () => + this.onSetImportFromOption(importFromOption), + }; + })} + verticallyAligned + /> +
+
-

{stateFolderLabel}

+

+ {this.isImportFromStateDir(importFrom) + ? stateFolderLabel + : secretFileLabel} +

{ - this.stateFolderInput = input; + this.importPathInput = input; }} skin={InputSkin} - value={exportSourcePath} + value={ + exportSourcePath || + (this.isImportFromStateDir(importFrom) + ? defaultExportSourcePath + : '') + } + placeholder={ + this.isImportFromStateDir(importFrom) + ? defaultExportSourcePath + : 'secret.key' + } />
{error &&

{noWalletError}

}
+
+ { pendingImportWalletsCount, onConfirm, onClose, + onOpenExternalLink, onWalletNameChange, nameValidator, } = this.props; @@ -208,6 +214,10 @@ export default class WalletSelectImportDialog extends Component { ); + const linkLabel = intl.formatMessage(messages.linkLabel); + const onLinkClick = () => + onOpenExternalLink(intl.formatMessage(messages.linkUrl)); + const isDisabled = isSubmitting || !pendingImportWalletsCount; const buttonClasses = classNames(styles.actionButton, [ isDisabled ? styles.disabled : null, @@ -307,17 +317,6 @@ export default class WalletSelectImportDialog extends Component {
)}

{intl.formatMessage(messages.unamedWalletsTitle)}

- - -
)} @@ -379,13 +378,22 @@ export default class WalletSelectImportDialog extends Component { onClick={onConfirm} skin={ButtonSkin} /> - +
+ + + +
diff --git a/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.scss b/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.scss index d4a59fcd58..d8bbdc1a4b 100644 --- a/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.scss +++ b/source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.scss @@ -154,19 +154,6 @@ } } - .unamedWalletsTooltip { - @extend .tooltip; - display: inline-block; - margin-left: 5px; - - :global { - .SimpleTooltip_bubble { - margin: 0 0 -7px -14px; - width: 330px; - } - } - } - .enterWalletNameTooltip { @extend .tooltip; display: block; @@ -381,6 +368,17 @@ } } + .learnMoreLink { + border-bottom: 1px solid var(--theme-wallet-import-description-color); + color: var(--theme-wallet-import-description-color); + margin-right: 30px; + margin-top: 20px; + opacity: 0.8; + &:after { + background-color: var(--theme-wallet-import-description-color); + } + } + .closeWindowLink { border-bottom: 1px solid var(--theme-wallet-import-description-color); color: var(--theme-wallet-import-description-color); diff --git a/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.js b/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.js index 476a772f01..db3f802e0c 100644 --- a/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.js +++ b/source/renderer/app/containers/wallet/dialogs/WalletImportDialogContainer.js @@ -5,6 +5,7 @@ import WalletImportFileDialog from '../../../components/wallet/wallet-import/Wal import WalletSelectImportDialog from '../../../components/wallet/wallet-import/WalletSelectImportDialog'; import { isValidWalletName } from '../../../utils/validations'; import type { InjectedProps } from '../../../types/injectedPropsType'; +import type { ImportFromOption } from '../../../types/walletExportTypes'; type Props = InjectedProps; @@ -35,8 +36,12 @@ export default class WalletImportDialogContainer extends Component { ); }; - onSelectExportSourcePath = () => { - this.props.actions.walletMigration.selectExportSourcePath.trigger(); + onSelectExportSourcePath = (params: { importFrom: ImportFromOption }) => { + this.props.actions.walletMigration.selectExportSourcePath.trigger(params); + }; + + onResetExportSourcePath = () => { + this.props.actions.walletMigration.resetExportSourcePath.trigger(); }; render() { @@ -45,6 +50,7 @@ export default class WalletImportDialogContainer extends Component { exportedWallets, exportErrors, exportSourcePath, + defaultExportSourcePath, pendingImportWalletsCount, isExportRunning, isRestorationRunning, @@ -58,6 +64,7 @@ export default class WalletImportDialogContainer extends Component { { onClose={this.onCancel} onOpenExternalLink={openExternalLink} onSelectExportSourcePath={this.onSelectExportSourcePath} + onResetExportSourcePath={this.onResetExportSourcePath} /> )} {walletMigrationStep === 2 && ( @@ -74,6 +82,7 @@ export default class WalletImportDialogContainer extends Component { exportedWallets={exportedWallets} pendingImportWalletsCount={pendingImportWalletsCount} onConfirm={this.onConfirm} + onOpenExternalLink={openExternalLink} onWalletNameChange={this.onWalletNameChange} onToggleWalletImportSelection={this.onToggleWalletImportSelection} onClose={this.onCancel} diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index 14fd10ab44..80eb3803b4 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -8922,13 +8922,13 @@ "description": "Import wallets dialog title", "end": { "column": 3, - "line": 25 + "line": 28 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", "id": "wallet.import.file.dialog.title", "start": { "column": 9, - "line": 21 + "line": 24 } }, { @@ -8936,13 +8936,13 @@ "description": "

This feature enables you to import wallets from the production version of Daedalus, or from the Daedalus state directory.

If you don’t have the complete state directory, then you will need either the ‘Secrets’ or ‘Secrets-1.0’ folder containing the ‘secret.key’ file to be able to import a wallet, although without the complete state directory Daedalus won’t be able to detect your wallet names.

If you don’t have either the ‘Secrets’ or the ‘Secrets-1.0’ folder containing the ‘secret.key’ file, then you cannot import wallets using this feature.

", "end": { "column": 3, - "line": 32 + "line": 35 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", "id": "wallet.import.file.dialog.description", "start": { "column": 15, - "line": 26 + "line": 29 } }, { @@ -8950,13 +8950,27 @@ "description": "Select Daedalus state folder:", "end": { "column": 3, - "line": 37 + "line": 40 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", "id": "wallet.import.file.dialog.stateFolderLabel", "start": { "column": 20, - "line": 33 + "line": 36 + } + }, + { + "defaultMessage": "!!!Select Daedalus 'secret.key' file:", + "description": "Select Daedalus 'secret.key' file:", + "end": { + "column": 3, + "line": 45 + }, + "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", + "id": "wallet.import.file.dialog.secretFileLabel", + "start": { + "column": 19, + "line": 41 } }, { @@ -8964,13 +8978,13 @@ "description": "Import wallets", "end": { "column": 3, - "line": 42 + "line": 50 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", "id": "wallet.import.file.dialog.buttonLabel", "start": { "column": 15, - "line": 38 + "line": 46 } }, { @@ -8978,13 +8992,27 @@ "description": "No wallets found. Make sure you have selected a Daedalus state directory which contains the ‘Secrets’ or `Secrets-1.0` folder with a `secret.key` file inside.", "end": { "column": 3, - "line": 49 + "line": 57 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", - "id": "wallet.import.file.dialog.noWallets", + "id": "wallet.import.file.dialog.stateDirNoWallets", "start": { - "column": 13, - "line": 43 + "column": 21, + "line": 51 + } + }, + { + "defaultMessage": "!!!No wallets found. Make sure you have selected a valid `secret.key` file.", + "description": "No wallets found. Make sure you have selected a valid `secret.key` file.", + "end": { + "column": 3, + "line": 64 + }, + "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", + "id": "wallet.import.file.dialog.secretFileNoWallets", + "start": { + "column": 23, + "line": 58 } }, { @@ -8992,13 +9020,13 @@ "description": "Learn more", "end": { "column": 3, - "line": 54 + "line": 69 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", "id": "wallet.import.file.dialog.linkLabel", "start": { "column": 13, - "line": 50 + "line": 65 } }, { @@ -9006,13 +9034,55 @@ "description": "\"Learn more\" link URL on the wallet import file dialog", "end": { "column": 3, - "line": 60 + "line": 75 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", "id": "wallet.import.file.dialog.linkUrl", "start": { "column": 11, - "line": 55 + "line": 70 + } + }, + { + "defaultMessage": "!!!Import from:", + "description": "Import from:", + "end": { + "column": 3, + "line": 80 + }, + "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", + "id": "wallet.import.file.dialog.importFromLabel", + "start": { + "column": 19, + "line": 76 + } + }, + { + "defaultMessage": "!!!Daedalus state directory", + "description": "Daedalus state directory", + "end": { + "column": 3, + "line": 85 + }, + "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", + "id": "wallet.import.file.dialog.stateDirOptionLabel", + "start": { + "column": 23, + "line": 81 + } + }, + { + "defaultMessage": "!!!Daedalus 'secret.key' file", + "description": "Daedalus 'secret.key' file", + "end": { + "column": 3, + "line": 90 + }, + "file": "source/renderer/app/components/wallet/wallet-import/WalletImportFileDialog.js", + "id": "wallet.import.file.dialog.secretFileOptionLabel", + "start": { + "column": 25, + "line": 86 } } ], @@ -9025,13 +9095,13 @@ "description": "Select import wallets dialog title", "end": { "column": 3, - "line": 32 + "line": 31 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.title", "start": { "column": 9, - "line": 28 + "line": 27 } }, { @@ -9039,13 +9109,13 @@ "description": "These wallets were found in your Daedalus state directory. Please select the wallets you want to import.", "end": { "column": 3, - "line": 39 + "line": 38 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.description", "start": { "column": 15, - "line": 33 + "line": 32 } }, { @@ -9053,27 +9123,13 @@ "description": "unamedWalletsTitle", "end": { "column": 3, - "line": 44 + "line": 43 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.unamedWalletsTitle", "start": { "column": 22, - "line": 40 - } - }, - { - "defaultMessage": "!!!The following wallets were found in your secret keys file, but matching entries were not found in your wallet database. They are probably wallets you have previously deleted, or are not in your wallet database because of data corruption or a similar issue. You can ignore them, or you can try importing them if wallets are missing from the list of found walle", - "description": "unamedWalletsTooltip", - "end": { - "column": 3, - "line": 50 - }, - "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", - "id": "wallet.select.import.dialog.unamedWalletsTooltip", - "start": { - "column": 24, - "line": 45 + "line": 39 } }, { @@ -9081,13 +9137,13 @@ "description": "Password protected", "end": { "column": 3, - "line": 55 + "line": 48 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.passwordProtected", "start": { "column": 21, - "line": 51 + "line": 44 } }, { @@ -9095,13 +9151,13 @@ "description": "Wallet already exists", "end": { "column": 3, - "line": 60 + "line": 53 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.walletExists", "start": { "column": 16, - "line": 56 + "line": 49 } }, { @@ -9109,13 +9165,13 @@ "description": "No password", "end": { "column": 3, - "line": 65 + "line": 58 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.noPassword", "start": { "column": 14, - "line": 61 + "line": 54 } }, { @@ -9123,13 +9179,13 @@ "description": "Importing wallet...", "end": { "column": 3, - "line": 70 + "line": 63 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.importingWallet", "start": { "column": 19, - "line": 66 + "line": 59 } }, { @@ -9137,13 +9193,13 @@ "description": "Enter wallet name", "end": { "column": 3, - "line": 75 + "line": 68 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.walletName", "start": { "column": 14, - "line": 71 + "line": 64 } }, { @@ -9151,13 +9207,13 @@ "description": "Name not found", "end": { "column": 3, - "line": 80 + "line": 73 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.notFound", "start": { "column": 12, - "line": 76 + "line": 69 } }, { @@ -9165,13 +9221,13 @@ "description": "Enter a wallet name first", "end": { "column": 3, - "line": 85 + "line": 78 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.enterWalletNameTooltip", "start": { "column": 26, - "line": 81 + "line": 74 } }, { @@ -9179,13 +9235,13 @@ "description": "Wallet imported", "end": { "column": 3, - "line": 90 + "line": 83 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.walletImported", "start": { "column": 18, - "line": 86 + "line": 79 } }, { @@ -9193,13 +9249,41 @@ "description": "Import selected wallets", "end": { "column": 3, - "line": 95 + "line": 88 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.buttonLabel", "start": { "column": 15, - "line": 91 + "line": 84 + } + }, + { + "defaultMessage": "!!!Learn more", + "description": "Learn more", + "end": { + "column": 3, + "line": 93 + }, + "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", + "id": "wallet.select.import.dialog.linkLabel", + "start": { + "column": 13, + "line": 89 + } + }, + { + "defaultMessage": "!!!https://iohk.zendesk.com/hc/en-us/articles/900000623463", + "description": "\"Learn more\" link URL on the wallet import file dialog", + "end": { + "column": 3, + "line": 99 + }, + "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", + "id": "wallet.select.import.dialog.linkUrl", + "start": { + "column": 11, + "line": 94 } }, { @@ -9207,13 +9291,13 @@ "description": "Close window", "end": { "column": 3, - "line": 100 + "line": 104 }, "file": "source/renderer/app/components/wallet/wallet-import/WalletSelectImportDialog.js", "id": "wallet.select.import.dialog.closeWindow", "start": { "column": 15, - "line": 96 + "line": 100 } } ], diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index 92168b5348..2166c684d9 100644 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -515,11 +515,16 @@ "wallet.create.dialog.validateStep": "Validate", "wallet.create.dialog.walletNameHint": "Enter wallet name", "wallet.import.file.dialog.buttonLabel": "Import wallets", - "wallet.import.file.dialog.description": "

This feature enables you to import wallets from the production version of Daedalus, or from the Daedalus state directory.

If you don’t have the complete state directory, then you will need either the ‘Secrets’ or ‘Secrets-1.0’ folder containing the ‘secret.key’ file to be able to import a wallet, although without the complete state directory Daedalus won’t be able to detect your wallet names.

If you don’t have either the ‘Secrets’ or the ‘Secrets-1.0’ folder containing the ‘secret.key’ file, then you cannot import wallets using this feature.

", + "wallet.import.file.dialog.description": "

This feature enables you to import wallets from the previous version of Daedalus, from the Daedalus state directory, or from a ‘secret.key’ file.

It can be used to import wallets quickly without entering the wallet recovery phrase for each wallet, or to import wallets for which you have lost your wallet recovery phrase.

After importing a wallet for which you have lost your wallet recovery phrase, please create a new wallet and transfer all funds from the old wallet to the new wallet. Keep the wallet recovery phrase for your new wallet secure.

", + "wallet.import.file.dialog.importFromLabel": "Import from:", "wallet.import.file.dialog.linkLabel": "Learn more", "wallet.import.file.dialog.linkUrl": "https://iohk.zendesk.com/hc/en-us/articles/900000623463", - "wallet.import.file.dialog.noWallets": "No wallets found. Make sure you have selected a Daedalus state directory which contains the ‘Secrets’ or `Secrets-1.0` folder with a `secret.key` file inside.", - "wallet.import.file.dialog.stateFolderLabel": "Select Daedalus state folder:", + "wallet.import.file.dialog.secretFileLabel": "Select Daedalus 'secret.key' file:", + "wallet.import.file.dialog.secretFileNoWallets": "No wallets found. Make sure you have selected a valid `secret.key` file.", + "wallet.import.file.dialog.secretFileOptionLabel": "Daedalus 'secret.key' file", + "wallet.import.file.dialog.stateDirNoWallets": "No wallets found. Make sure you have selected a Daedalus state directory which contains the ‘Secrets’ or `Secrets-1.0` folder with a `secret.key` file inside.", + "wallet.import.file.dialog.stateDirOptionLabel": "Daedalus state directory", + "wallet.import.file.dialog.stateFolderLabel": "Select Daedalus state directory:", "wallet.import.file.dialog.title": "Import wallets", "wallet.legacy.badge.label": "Balance", "wallet.legacy.notification.actionLearnMore": "Learn more", @@ -645,12 +650,13 @@ "wallet.select.import.dialog.description": "These wallets were found in your Daedalus state directory.

Please select the wallets you want to import.

", "wallet.select.import.dialog.enterWalletNameTooltip": "Enter a wallet name first", "wallet.select.import.dialog.importingWallet": "Importing wallet...", + "wallet.select.import.dialog.linkLabel": "Learn more", + "wallet.select.import.dialog.linkUrl": "https://iohk.zendesk.com/hc/en-us/articles/900000623463", "wallet.select.import.dialog.noPassword": "No password", "wallet.select.import.dialog.notFound": "Name not found", "wallet.select.import.dialog.passwordProtected": "Password protected", "wallet.select.import.dialog.title": "Found wallets", "wallet.select.import.dialog.unamedWalletsTitle": "Unnamed wallets", - "wallet.select.import.dialog.unamedWalletsTooltip": "The following wallets were found in your secret keys file, but matching entries were not found in your wallet database. They are probably wallets you have previously deleted, or are not in your wallet database because of data corruption or a similar issue. You can ignore them, or you can try importing them if wallets are missing from the list of found wallets above.", "wallet.select.import.dialog.walletExists": "Wallet already exists", "wallet.select.import.dialog.walletImported": "Wallet imported", "wallet.select.import.dialog.walletName": "Enter wallet name", diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json index c12bdfe6e4..1fe29a2453 100644 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -515,11 +515,16 @@ "wallet.create.dialog.validateStep": "!!!Validate", "wallet.create.dialog.walletNameHint": "ウォレット名を入力してください", "wallet.import.file.dialog.buttonLabel": "ウォレットをインポートする", - "wallet.import.file.dialog.description": "

この機能により、Daedalus製品バージョンまたはDaedalusステータスディレクトリーからウォレットをインポートすることができます。

完全なステータスディレクトリーがなくても、secret.keyファイルが保存されたSecretsフォルダーまたはSecrets-1.0フォルダーのいずれかがあればウォレットをインポートすることはできます。ただし完全なステータスディレクトリーがない場合、Daedalusはウォレット名を検知することができません。

secret.keyファイルが保存されたSecretsフォルダーまたはSecrets-1.0フォルダーのいずれもない場合は、本機能を使用してウォレットをインポートすることはできません。

", + "wallet.import.file.dialog.description": "

この機能により、Daedalusの旧バージョン、Daedalusステータスディレクトリー、またはsecret.keyファイルからウォレットをインポートすることができます。

各ウォレットの復元フレーズを入力せずに素早くウォレットをインポートできるほか、復元フレーズを紛失したウォレットのインポートも可能です。

復元フレーズを紛失したウォレットをインポートした場合は、インポート後に新規ウォレットを作成してすべての資金を旧ウォレットから移し、新しいウォレットの復元フレーズを安全な場所に保管してください。

", + "wallet.import.file.dialog.importFromLabel": "インポート元:", "wallet.import.file.dialog.linkLabel": "もっと知る", "wallet.import.file.dialog.linkUrl": "https://iohk.zendesk.com/hc/ja/articles/900000623463", - "wallet.import.file.dialog.noWallets": "ウォレットが見つかりません。選択したDaedalusステータスディレクトリーに、secret.keyファイルが保存されたSecretsフォルダーまたはSecrets-1.0フォルダーのいずれかが含まれていることを確認してください。", - "wallet.import.file.dialog.stateFolderLabel": "Daedalusステータスフォルダーを選択:", + "wallet.import.file.dialog.secretFileLabel": "Daedalusのsecret.keyファイルを選択してください:", + "wallet.import.file.dialog.secretFileNoWallets": "ウォレットが見つかりません。有効なsecret.keyファイルが選択されているか確認してください。", + "wallet.import.file.dialog.secretFileOptionLabel": "Daedalusのsecret.keyファイル", + "wallet.import.file.dialog.stateDirNoWallets": "ウォレットが見つかりません。選択したDaedalusステータスディレクトリーに、secret.keyファイルが保存されたSecretsフォルダーまたはSecrets-1.0フォルダーのいずれかが含まれていることを確認してください。", + "wallet.import.file.dialog.stateDirOptionLabel": "Daedalusステータスディレクトリー", + "wallet.import.file.dialog.stateFolderLabel": "Daedalusステータスディレクトリーを選択してください:", "wallet.import.file.dialog.title": "ウォレットをインポートする", "wallet.legacy.badge.label": "残高", "wallet.legacy.notification.actionLearnMore": "もっと知る", @@ -645,12 +650,13 @@ "wallet.select.import.dialog.description": "Daedalusステータスディレクトリーに以下のウォレットが見つかりました。

インポートするウォレットを選択してください。

", "wallet.select.import.dialog.enterWalletNameTooltip": "先にウォレット名を入力してください", "wallet.select.import.dialog.importingWallet": "ウォレットをインポート中…", + "wallet.select.import.dialog.linkLabel": "もっと知る", + "wallet.select.import.dialog.linkUrl": "https://iohk.zendesk.com/hc/ja/articles/900000623463", "wallet.select.import.dialog.noPassword": "パスワードなし", "wallet.select.import.dialog.notFound": "ウォレット名不明", "wallet.select.import.dialog.passwordProtected": "パスワード設定済み", "wallet.select.import.dialog.title": "既存のウォレット", "wallet.select.import.dialog.unamedWalletsTitle": "名称不明のウォレット", - "wallet.select.import.dialog.unamedWalletsTooltip": "secret.keyファイルに次のウォレットが見つかりましたが、ウォレットデータベースに対応するウォレットが見つかりません。これらは以前に削除されたウォレットか、データの損傷または類似する不具合によりデータベースに含まれていない可能性があります。これらは無視しても構いません。また、上記の検知されたウォレットリストに含まれていないウォレットである場合はインポートを試みることもできます。", "wallet.select.import.dialog.walletExists": "既存のウォレット", "wallet.select.import.dialog.walletImported": "ウォレットをインポート済み", "wallet.select.import.dialog.walletName": "ウォレット名を入力してください", diff --git a/source/renderer/app/stores/WalletMigrationStore.js b/source/renderer/app/stores/WalletMigrationStore.js index 0d4409d2d1..e4cd9bc1b4 100644 --- a/source/renderer/app/stores/WalletMigrationStore.js +++ b/source/renderer/app/stores/WalletMigrationStore.js @@ -1,5 +1,6 @@ // @flow import { action, computed, observable, runInAction } from 'mobx'; +import path from 'path'; import { orderBy } from 'lodash'; import Store from './lib/Store'; import Request from './lib/LocalizedRequest'; @@ -20,8 +21,12 @@ import type { import type { ExportedByronWallet, WalletImportStatus, + ImportFromOption, +} from '../types/walletExportTypes'; +import { + WalletImportStatuses, + ImportFromOptions, } from '../types/walletExportTypes'; -import { WalletImportStatuses } from '../types/walletExportTypes'; export type WalletMigrationStatus = | 'unstarted' @@ -50,7 +55,8 @@ export default class WalletMigrationStore extends Store { @observable isExportRunning = false; @observable exportedWallets: Array = []; @observable exportErrors: string = ''; - @observable exportSourcePath: string = global.legacyStateDir; + @observable exportSourcePath: string = ''; + @observable defaultExportSourcePath: string = global.legacyStateDir; @observable isRestorationRunning = false; @observable restoredWallets: Array = []; @@ -84,6 +90,7 @@ export default class WalletMigrationStore extends Store { walletMigration.updateWalletName.listen(this._updateWalletName); walletMigration.nextStep.listen(this._nextStep); walletMigration.selectExportSourcePath.listen(this._selectExportSourcePath); + walletMigration.resetExportSourcePath.listen(this._resetExportSourcePath); } getExportedWalletById = (id: string): ?ExportedByronWallet => @@ -98,11 +105,30 @@ export default class WalletMigrationStore extends Store { getExportedWalletByIndex = (index: number): ?ExportedByronWallet => this.exportedWallets.find(w => w.index === index); - @action _selectExportSourcePath = async () => { - const params = { - defaultPath: global.legacyStateDir, - properties: ['openDirectory'], - }; + @action _selectExportSourcePath = async ({ + importFrom, + }: { + importFrom: ImportFromOption, + }) => { + const params = + importFrom === ImportFromOptions.STATE_DIR + ? { + defaultPath: this.defaultExportSourcePath, + properties: ['openDirectory'], + } + : { + defaultPath: path.join( + this.stores.profile.desktopDirectoryPath, + 'secret.key' + ), + properties: ['openFile'], + filters: [ + { + name: 'secret', + extensions: ['key'], + }, + ], + }; const { filePaths } = await showOpenDialogChannel.send(params); if (!filePaths || filePaths.length === 0) { return; @@ -113,6 +139,11 @@ export default class WalletMigrationStore extends Store { }); }; + @action _resetExportSourcePath = () => { + this.exportSourcePath = ''; + this.exportErrors = ''; + }; + @action _nextStep = async () => { if (this.walletMigrationStep === 1) { await this._exportWallets(); @@ -188,7 +219,7 @@ export default class WalletMigrationStore extends Store { wallets, errors, }: ExportWalletsMainResponse = await exportWalletsChannel.request({ - exportSourcePath: this.exportSourcePath, + exportSourcePath: this.exportSourcePath || this.defaultExportSourcePath, locale: this.stores.profile.currentLocale, }); runInAction('update exportedWallets and exportErrors', () => { @@ -205,7 +236,7 @@ export default class WalletMigrationStore extends Store { : WalletImportStatuses.UNSTARTED; return { ...wallet, hasName, import: { status, error: null } }; }), - ['hasName', 'name', 'id', 'is_passphrase_empty'], + ['hasName', 'id', 'name', 'is_passphrase_empty'], ['desc', 'asc', 'asc', 'asc'] ); @@ -401,7 +432,7 @@ export default class WalletMigrationStore extends Store { @action _resetMigration = () => { this._resetExportData(); this._resetRestorationData(); - this.exportSourcePath = global.legacyStateDir || ''; + this.exportSourcePath = ''; }; @action _finishMigration = async () => { diff --git a/source/renderer/app/themes/daedalus/cardano.js b/source/renderer/app/themes/daedalus/cardano.js index 653603e6e8..8ece84061b 100644 --- a/source/renderer/app/themes/daedalus/cardano.js +++ b/source/renderer/app/themes/daedalus/cardano.js @@ -896,7 +896,8 @@ export const CARDANO_THEME_OUTPUT = { '--theme-wallet-import-background-color': 'rgba(32, 34, 37, 0.96)', '--theme-wallet-import-description-close-hover-background': 'rgba(0, 0, 0, 0.2)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/daedalus/dark-blue.js b/source/renderer/app/themes/daedalus/dark-blue.js index b618226c45..bbe70a86d5 100644 --- a/source/renderer/app/themes/daedalus/dark-blue.js +++ b/source/renderer/app/themes/daedalus/dark-blue.js @@ -899,7 +899,8 @@ export const DARK_BLUE_THEME_OUTPUT = { }, walletImportDialog: { '--theme-wallet-import-background-color': 'rgba(38, 51, 69, 0.96)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/daedalus/dark-cardano.js b/source/renderer/app/themes/daedalus/dark-cardano.js index 88d731ee74..a163c5cbd9 100644 --- a/source/renderer/app/themes/daedalus/dark-cardano.js +++ b/source/renderer/app/themes/daedalus/dark-cardano.js @@ -883,7 +883,8 @@ export const DARK_CARDANO_THEME_OUTPUT = { '--theme-wallet-import-background-color': '#36374df5', '--theme-wallet-import-description-close-hover-background': 'rgba(0, 0, 0, 0.16)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/daedalus/flight-candidate.js b/source/renderer/app/themes/daedalus/flight-candidate.js index baa569a763..ac46c30e71 100644 --- a/source/renderer/app/themes/daedalus/flight-candidate.js +++ b/source/renderer/app/themes/daedalus/flight-candidate.js @@ -883,7 +883,8 @@ export const FLIGHT_CANDIDATE_THEME_OUTPUT = { '--theme-wallet-import-background-color': '#36374df5', '--theme-wallet-import-description-close-hover-background': 'rgba(0, 0, 0, 0.16)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/daedalus/incentivized-testnet.js b/source/renderer/app/themes/daedalus/incentivized-testnet.js index 200c5e6f03..4a65ac159c 100644 --- a/source/renderer/app/themes/daedalus/incentivized-testnet.js +++ b/source/renderer/app/themes/daedalus/incentivized-testnet.js @@ -886,7 +886,8 @@ export const INCENTIVIZED_TESTNET_THEME_OUTPUT = { '--theme-wallet-import-background-color': '#36374df5', '--theme-wallet-import-description-close-hover-background': 'rgba(0, 0, 0, 0.16)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/daedalus/light-blue.js b/source/renderer/app/themes/daedalus/light-blue.js index 47cf31dc2b..7aeb9a69fc 100644 --- a/source/renderer/app/themes/daedalus/light-blue.js +++ b/source/renderer/app/themes/daedalus/light-blue.js @@ -894,7 +894,8 @@ export const LIGHT_BLUE_THEME_OUTPUT = { '--theme-wallet-import-background-color': 'rgba(36, 62, 98, 0.96)', '--theme-wallet-import-description-close-hover-background': 'rgba(0, 0, 0, 0.2)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/daedalus/white.js b/source/renderer/app/themes/daedalus/white.js index 312928f707..d0d5646a0c 100644 --- a/source/renderer/app/themes/daedalus/white.js +++ b/source/renderer/app/themes/daedalus/white.js @@ -887,7 +887,8 @@ export const WHITE_THEME_OUTPUT = { '--theme-wallet-import-background-color': '#fffffff5', '--theme-wallet-import-description-close-hover-background': 'rgba(41, 181, 149, 0.1)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/daedalus/yellow.js b/source/renderer/app/themes/daedalus/yellow.js index 657386147c..259fb3a4d9 100644 --- a/source/renderer/app/themes/daedalus/yellow.js +++ b/source/renderer/app/themes/daedalus/yellow.js @@ -889,7 +889,8 @@ export const YELLOW_THEME_OUTPUT = { '--theme-wallet-import-background-color': '#fdcd68f5', '--theme-wallet-import-description-close-hover-background': 'rgba(0, 0, 0, 0.1)', - '--theme-wallet-import-description-color': '#fff', + '--theme-wallet-import-description-color': 'rgba(255, 255, 255, 0.7)', + '--theme-wallet-import-description-bold-color': '#fff', '--theme-wallet-import-title-color': '#fff', '--theme-wallet-import-stateFolder-border-color': 'rgba(255, 255, 255, 0.5)', diff --git a/source/renderer/app/themes/utils/createTheme.js b/source/renderer/app/themes/utils/createTheme.js index 825b34be51..f2d67ab29b 100644 --- a/source/renderer/app/themes/utils/createTheme.js +++ b/source/renderer/app/themes/utils/createTheme.js @@ -729,7 +729,10 @@ export const createDaedalusComponentsTheme = ( '--theme-wallet-import-description-close-hover-background': `${chroma( background.secondary.regular ).alpha(0.16)}`, - '--theme-wallet-import-description-color': `${text.primary}`, + '--theme-wallet-import-description-color': `${chroma(text.primary).alpha( + 0.7 + )}`, + '--theme-wallet-import-description-bold-color': `${text.primary}`, '--theme-wallet-import-title-color': `${text.primary}`, '--theme-wallet-import-stateFolder-border-color': `${chroma( text.primary diff --git a/source/renderer/app/types/walletExportTypes.js b/source/renderer/app/types/walletExportTypes.js index e66b961102..fc94449019 100644 --- a/source/renderer/app/types/walletExportTypes.js +++ b/source/renderer/app/types/walletExportTypes.js @@ -43,3 +43,13 @@ export type ExportedByronWallet = { }, index: number, }; + +export type ImportFromOption = 'stateDir' | 'secretFile'; + +export const ImportFromOptions: { + STATE_DIR: ImportFromOption, + SECRET_FILE: ImportFromOption, +} = { + STATE_DIR: 'stateDir', + SECRET_FILE: 'secretFile', +}; diff --git a/storybook/stories/wallets/import/WalletImportFile.stories.js b/storybook/stories/wallets/import/WalletImportFile.stories.js index 68ea7c592f..7bc2de8009 100644 --- a/storybook/stories/wallets/import/WalletImportFile.stories.js +++ b/storybook/stories/wallets/import/WalletImportFile.stories.js @@ -31,12 +31,14 @@ storiesOf('Wallets|Import File', module) )) @@ -61,6 +63,7 @@ storiesOf('Wallets|Import File', module) exportedWallets={[...namedWallets, ...unnamedWallets]} pendingImportWalletsCount={number('pendingImportWalletsCount', 0)} onConfirm={action('onConfirm')} + onOpenExternalLink={action('onOpenExternalLink')} onWalletNameChange={action('onWalletNameChange')} onToggleWalletImportSelection={action( 'onToggleWalletImportSelection'