Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
43 changes: 39 additions & 4 deletions source/main/cardano/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -258,6 +266,11 @@ export const exportWallets = async (
errors,
});

// Remove Daedalus Flight migration data
if (isFlight) {
await removeMigrationData(stateDir);
}

return Promise.resolve({ wallets, errors });
};

Expand Down Expand Up @@ -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`;
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion source/renderer/app/actions/wallet-migration-actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow
import Action from './lib/Action';
import type { ImportFromOption } from '../types/walletExportTypes';

export default class WalletMigrationActions {
startMigration: Action<any> = new Action();
Expand All @@ -8,5 +9,8 @@ export default class WalletMigrationActions {
toggleWalletImportSelection: Action<{ index: number }> = new Action();
updateWalletName: Action<{ index: number, name: string }> = new Action();
nextStep: Action<any> = new Action();
selectExportSourcePath: Action<any> = new Action();
selectExportSourcePath: Action<{
importFrom: ImportFromOption,
}> = new Action();
resetExportSourcePath: Action<any> = new Action();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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',
Expand All @@ -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 = {
Expand All @@ -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<Props> {
export default class WalletImportFileDialog extends Component<Props, State> {
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,
Expand All @@ -94,23 +148,27 @@ export default class WalletImportFileDialog extends Component<Props> {
onOpenExternalLink,
onSelectExportSourcePath,
exportSourcePath,
defaultExportSourcePath,
} = this.props;
const title = intl.formatMessage(messages.title);
const description = <FormattedHTMLMessage {...messages.description} />;
const stateFolderLabel = intl.formatMessage(messages.stateFolderLabel);
const secretFileLabel = intl.formatMessage(messages.secretFileLabel);
const buttonLabel = !isSubmitting ? (
intl.formatMessage(messages.buttonLabel)
) : (
<LoadingSpinner />
);
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([
Expand All @@ -119,7 +177,11 @@ export default class WalletImportFileDialog extends Component<Props> {
]);

const buttonClasses = classNames(styles.actionButton, [
isSubmitting || error ? styles.disabled : null,
isSubmitting ||
error ||
(this.isImportFromSecretFile(importFrom) && !exportSourcePath)
? styles.disabled
: null,
]);

return (
Expand All @@ -141,39 +203,79 @@ export default class WalletImportFileDialog extends Component<Props> {
<div className={styles.content}>
<div className={styles.title}>{title}</div>
<div className={styles.description}>{description}</div>

<div className={styles.radioButtons}>
<RadioSet
label={intl.formatMessage(messages.importFromLabel)}
items={Object.keys(ImportFromOptions).map((key: string) => {
const importFromOption: ImportFromOption =
ImportFromOptions[key];
return {
key: importFromOption,
label: intl.formatMessage(
messages[`${importFromOption}OptionLabel`]
),
selected: importFrom === importFromOption,
onChange: () =>
this.onSetImportFromOption(importFromOption),
};
})}
verticallyAligned
/>
</div>

<div className={styles.stateFolderContainer}>
<p className={styles.stateFolderLabel}>{stateFolderLabel}</p>
<p className={styles.stateFolderLabel}>
{this.isImportFromStateDir(importFrom)
? stateFolderLabel
: secretFileLabel}
</p>
<div className={styles.stateFolderInputWrapper}>
<Input
type="text"
className={inputClasses}
ref={input => {
this.stateFolderInput = input;
this.importPathInput = input;
}}
skin={InputSkin}
value={exportSourcePath}
value={
exportSourcePath ||
(this.isImportFromStateDir(importFrom)
? defaultExportSourcePath
: '')
}
placeholder={
this.isImportFromStateDir(importFrom)
? defaultExportSourcePath
: 'secret.key'
}
/>
<Button
className={styles.selectStateDirectoryButton}
onClick={onSelectExportSourcePath}
onClick={() => onSelectExportSourcePath({ importFrom })}
label={<SVGInline svg={penIcon} className={styles.penIcon} />}
skin={ButtonSkin}
/>
</div>
{error && <p className={styles.noWalletError}>{noWalletError}</p>}
</div>

<div className={styles.action}>
<Button
className={buttonClasses}
disabled={isSubmitting || error}
disabled={
isSubmitting ||
error ||
(this.isImportFromSecretFile(importFrom) && !exportSourcePath)
}
label={buttonLabel}
onClick={onConfirm}
skin={ButtonSkin}
/>
</div>

<Link
className={styles.learnMoreLink}
disabled={isSubmitting}
onClick={onLinkClick}
label={linkLabel}
skin={LinkSkin}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@
line-height: 1.38;
margin-bottom: 20px;
max-width: 560px;
opacity: 0.7;

p {
color: var(--theme-wallet-import-description-color);
Expand All @@ -112,6 +111,23 @@
&:last-child {
margin-bottom: 0;
}

strong {
color: var(--theme-wallet-import-description-bold-color);
font-family: var(--font-semibold);
}
}
}

.radioButtons {
:global {
.SimpleRadio_circle {
border-color: var(--theme-wallet-import-checkbox-border-color);
}

.SimpleRadio_circle.SimpleRadio_selected {
background-color: var(--theme-wallet-import-checkbox-check-bg-color);
}
}
}

Expand Down
Loading