View on GitHub
File Changes
m
+1/-0

                      
### Features

                      
+
- Implemented Wallet mnemonics checker ([PR 1565](https://github.com/input-output-hk/daedalus/pull/1565))
- Removed select dropdown arrow ([PR 1550](https://github.com/input-output-hk/daedalus/pull/1550))
- Implemented automated and manual update flows unification ([PR 1491](https://github.com/input-output-hk/daedalus/pull/1491))
- Updated behavior of system dialogs ([PR 1494](https://github.com/input-output-hk/daedalus/pull/1494))
  isMainnet: boolean,
  buildLabel: string,
  timestamp: string,
+
  createdAt: Date,
  messages: {
    walletAddressLabel: string,
    recoveryPhraseLabel: string,
      hasSpendingPassword,
      spendingPasswordLastUpdate,
      syncState,
+
      createdAt,
    } = data;

                      
    return new Wallet({
      passwordUpdateDate: new Date(`${spendingPasswordLastUpdate}Z`),
      syncState,
      isLegacy: false,
+
      createdAt,
    });
  }
);
+
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
+
    <path fill="red" fill-rule="evenodd" d="M14 28C6.268 28 0 21.732 0 14S6.268 0 14 0s14 6.268 14 14-6.268 14-14 14zm-2.121-14l-3.94 3.94 2.122 2.12L14 16.122l3.94 3.94 2.12-2.122L16.122 14l3.94-3.94-2.122-2.12L14 11.878l-3.94-3.94-2.12 2.122L11.878 14z" opacity=".9"/>
+
</svg>
+
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
+
    <path fill="green" fill-rule="evenodd" d="M14 28C6.268 28 0 21.732 0 14S6.268 0 14 0s14 6.268 14 14-6.268 14-14 14zm-2-12.121l-3.94-3.94-2.12 2.122L12 20.12l10.06-10.06-2.12-2.122L12 15.88z" opacity=".9"/>
+
</svg>
+
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
+
    <path fill="#ecde00" fill-rule="evenodd" d="M14 28C6.268 28 0 21.732 0 14S6.268 0 14 0s14 6.268 14 14-6.268 14-14 14zm0-10a4 4 0 1 0 0-8 4 4 0 0 0 0 8z" opacity=".9"/>
+
</svg>
+
// @flow
+
import React, { Component } from 'react';
+
import type { Node } from 'react';
+
import { observer } from 'mobx-react';
+
import { capitalize } from 'lodash';
+
import classnames from 'classnames';
+
import { defineMessages, intlShape, FormattedHTMLMessage } from 'react-intl';
+
import moment from 'moment';
+
import SVGInline from 'react-svg-inline';
+
import iconMnemonicsOk from '../../../assets/images/mnemonics-verification-ok.inline.svg';
+
import iconMnemonicsWarning from '../../../assets/images/mnemonics-verification-warning.inline.svg';
+
import iconMnemonicsNotification from '../../../assets/images/mnemonics-verification-notification.inline.svg';
+
import styles from './WalletRecoveryPhrase.scss';
+
import {
+
  MNEMONICS_CHECKING_WARNING,
+
  MNEMONICS_CHECKING_NOTIFICATION,
+
} from '../../../config/walletsConfig';
+
import WalletRecoveryPhraseStep1Dialog from './WalletRecoveryPhraseStep1Dialog';
+
import WalletRecoveryPhraseStep2Dialog from './WalletRecoveryPhraseStep2Dialog';
+
import WalletRecoveryPhraseStep3Dialog from './WalletRecoveryPhraseStep3Dialog';
+
import WalletRecoveryPhraseStep4Dialog from './WalletRecoveryPhraseStep4Dialog';
+

                      
+
export const messages = defineMessages({
+
  recoveryPhraseValidationTitle: {
+
    id: 'wallet.settings.recoveryPhraseValidationTitle',
+
    defaultMessage: '!!!Do you have your wallet recovery phrase?',
+
    description:
+
      'Label for the recoveryPhraseValidationTitle on wallet settings.',
+
  },
+
  recoveryPhraseValidationDescription: {
+
    id: 'wallet.settings.recoveryPhraseValidationDescription',
+
    defaultMessage:
+
      '!!!Funds in this wallet can only be recovered on another computer using the correct wallet recovery phrase. You can re-enter your wallet recovery phrase to verify that you have the correct recovery phrase for this wallet.',
+
    description:
+
      'Label for the recoveryPhraseValidationDescription on wallet settings.',
+
  },
+
  recoveryPhraseValidationNeverOk: {
+
    id: 'wallet.settings.recoveryPhraseValidationNeverOk',
+
    defaultMessage:
+
      '!!!We recommend that you verify your wallet recovery phrase in <b>{timeUntilWarning}</b>',
+
    description:
+
      'Label for the recoveryPhraseValidationNeverOk on wallet settings.',
+
  },
+
  recoveryPhraseValidationNeverWarning: {
+
    id: 'wallet.settings.recoveryPhraseValidationNeverWarning',
+
    defaultMessage:
+
      '!!!We recommend that you verify your wallet recovery phrase in <b>{timeUntilNotification}</b>.',
+
    description:
+
      'Label for the recoveryPhraseValidationNeverWarning on wallet settings.',
+
  },
+
  recoveryPhraseValidationNeverNotification: {
+
    id: 'wallet.settings.recoveryPhraseValidationNeverNotification',
+
    defaultMessage:
+
      '!!!We recommend that you verify your wallet recovery phrase.',
+
    description:
+
      'Label for the recoveryPhraseValidationNeverNotification on wallet settings.',
+
  },
+
  recoveryPhraseValidationCheckedOk: {
+
    id: 'wallet.settings.recoveryPhraseValidationCheckedOk',
+
    defaultMessage:
+
      '!!!You verified the recovery phrase for this wallet <b>{timeAgo}</b>.',
+
    description:
+
      'Label for the recoveryPhraseValidationCheckedOk on wallet settings.',
+
  },
+
  recoveryPhraseValidationCheckedWarning: {
+
    id: 'wallet.settings.recoveryPhraseValidationCheckedWarning',
+
    defaultMessage:
+
      '!!!You verified the recovery phrase for this wallet <b>{timeAgo}</b>.',
+
    description:
+
      'Label for the recoveryPhraseValidationCheckedWarning on wallet settings.',
+
  },
+
  recoveryPhraseValidationCheckedNotification: {
+
    id: 'wallet.settings.recoveryPhraseValidationCheckedNotification',
+
    defaultMessage:
+
      '!!!You verified the recovery phrase for this wallet <b>{timeAgo}</b>. We recommend that you verify your wallet recovery phrase again.',
+
    description:
+
      'Label for the recoveryPhraseValidationCheckedNotification on wallet settings.',
+
  },
+
  recoveryPhraseValidationButton: {
+
    id: 'wallet.settings.recoveryPhraseValidationButton',
+
    defaultMessage: '!!!Confirm mnemonics.',
+
    description:
+
      'Label for the recoveryPhraseValidationButton on wallet settings.',
+
  },
+
});
+

                      
+
type Props = {
+
  mnemonicsConfirmationDate: Date,
+
  walletCreationDate: Date,
+
  openDialogAction: Function,
+
  isDialogOpen: Function,
+
  walletRecoveryPhraseStep1Container: Node,
+
  walletRecoveryPhraseStep2Container: Node,
+
  walletRecoveryPhraseStep3Container: Node,
+
  walletRecoveryPhraseStep4Container: Node,
+
};
+

                      
+
@observer
+
export default class WalletRecoveryPhrase extends Component<Props> {
+
  static contextTypes = {
+
    intl: intlShape.isRequired,
+
  };
+

                      
+
  get statuses() {
+
    return {
+
      neverChecked: {
+
        ok: {
+
          icon: iconMnemonicsOk,
+
          message: messages.recoveryPhraseValidationNeverOk,
+
        },
+
        warning: {
+
          icon: iconMnemonicsWarning,
+
          message: messages.recoveryPhraseValidationNeverWarning,
+
        },
+
        notification: {
+
          icon: iconMnemonicsNotification,
+
          message: messages.recoveryPhraseValidationNeverNotification,
+
        },
+
      },
+
      alreadyChecked: {
+
        ok: {
+
          icon: iconMnemonicsOk,
+
          message: messages.recoveryPhraseValidationCheckedOk,
+
        },
+
        warning: {
+
          icon: iconMnemonicsWarning,
+
          message: messages.recoveryPhraseValidationCheckedWarning,
+
        },
+
        notification: {
+
          icon: iconMnemonicsNotification,
+
          message: messages.recoveryPhraseValidationCheckedNotification,
+
        },
+
      },
+
    };
+
  }
+

                      
+
  get recoveryPhraseStatus() {
+
    const { mnemonicsConfirmationDate, walletCreationDate } = this.props;
+
    const dateToCheck = mnemonicsConfirmationDate || walletCreationDate;
+
    const daysSinceDate = moment().diff(moment(dateToCheck), 'days');
+
    let status = 'ok';
+
    if (daysSinceDate > MNEMONICS_CHECKING_NOTIFICATION)
+
      status = 'notification';
+
    else if (daysSinceDate > MNEMONICS_CHECKING_WARNING) status = 'warning';
+
    const type = mnemonicsConfirmationDate ? 'alreadyChecked' : 'neverChecked';
+
    const statuses = this.statuses[type];
+
    const { icon, message } = statuses[status];
+
    const timeAgo = moment(mnemonicsConfirmationDate).fromNow();
+
    const timeUntilWarning = 'few months, more or less';
+
    const timeUntilNotification = 'couple of days, more or less';
+
    return {
+
      icon,
+
      message,
+
      type,
+
      status,
+
      timeAgo,
+
      timeUntilWarning,
+
      timeUntilNotification,
+
    };
+
  }
+

                      
+
  render() {
+
    const { intl } = this.context;
+
    const {
+
      openDialogAction,
+
      isDialogOpen,
+
      walletRecoveryPhraseStep1Container,
+
      walletRecoveryPhraseStep2Container,
+
      walletRecoveryPhraseStep3Container,
+
      walletRecoveryPhraseStep4Container,
+
    } = this.props;
+
    const {
+
      icon,
+
      message,
+
      status,
+
      timeAgo,
+
      timeUntilWarning,
+
      timeUntilNotification,
+
    } = this.recoveryPhraseStatus;
+

                      
+
    const validationStatusStyles = classnames([
+
      styles.validationStatus,
+
      styles[`validationStatus${capitalize(status)}`],
+
    ]);
+

                      
+
    return (
+
      <div className={styles.component}>
+
        <h2>{intl.formatMessage(messages.recoveryPhraseValidationTitle)}</h2>
+
        <div className={styles.description}>
+
          {intl.formatMessage(messages.recoveryPhraseValidationDescription)}
+
        </div>
+
        <br />
+
        <div className={validationStatusStyles}>
+
          <SVGInline svg={icon} className={styles.validationStatusIcon} />
+
          <FormattedHTMLMessage
+
            {...message}
+
            className={styles.validationStatusMessage}
+
            values={{
+
              timeAgo,
+
.component {
+
  color: #5e6066;
+
  font-family: var(--font-light);
+
  line-height: 1.38;
+
  margin-bottom: 14px;
+

                      
+
  h2 {
+
    color: var(--theme-input-label-color);
+
    font-family: var(--font-medium);
+
    line-height: 1.38;
+
    margin-bottom: 14px;
+
  }
+
  b {
+
    color: var(--theme-input-label-color);
+
    font-family: var(--font-medium);
+
    line-height: 1.38;
+
  }
+

                      
+
  .validationStatus {
+
    align-items: center;
+
    border-radius: 4px;
+
    display: flex;
+
    font-style: italic;
+
    padding: 10px;
+
  }
+
  .validationStatusIcon {
+
    margin-right: 25px;
+
    svg {
+
      height: 28px;
+
      width: 28px;
+
    }
+
  }
+
  .validationStatusMessage {
+
    background: pink;
+
    color: #5e6066;
+
  }
+
  .validationStatusButton {
+
    border-radius: 5px;
+
    color: #fafbfc;
+
    cursor: pointer;
+
    display: block;
+
    font-size: 14px;
+
    font-weight: 500;
+
    height: 36px;
+
    line-height: 1.36;
+
    margin-left: 25px;
+
    padding: 0 27px;
+
    white-space: nowrap;
+
  }
+
  .validationStatusOk {
+
    background: rgba(0, 128, 0, 0.1);
+
    .validationStatusButton {
+
      background-color: green;
+
    }
+
  }
+
  .validationStatusWarning {
+
    background: rgba(228, 225, 4, 0.1);
+
    .validationStatusButton {
+
      background-color: #d2a80e;
+
    }
+
  }
+
  .validationStatusNotification {
+
    background-color: rgba(234, 76, 91, 0.1);
+
    .validationStatusButton {
+
      background-color: #ea4c5b;
+
    }
+
  }
+
}
+
.component {
+
  background: red;
+
}
+
// @flow
+
import React, { Component } from 'react';
+
import { observer } from 'mobx-react';
+
import { defineMessages, intlShape } from 'react-intl';
+
import { Checkbox } from 'react-polymorph/lib/components/Checkbox';
+
import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin';
+
import DialogCloseButton from '../../widgets/DialogCloseButton';
+
import Dialog from '../../widgets/Dialog';
+
import styles from './WalletRecoveryPhraseStepDialogs.scss';
+

                      
+
export const messages = defineMessages({
+
  recoveryPhraseStep1Title: {
+
    id: 'wallet.settings.recoveryPhraseStep1Title',
+
    defaultMessage: '!!!Wallet recovery phrase verification',
+
    description: 'Label for the recoveryPhraseStep1Title on wallet settings.',
+
  },
+
  recoveryPhraseStep1Paragraph1: {
+
    id: 'wallet.settings.recoveryPhraseStep1Paragraph1',
+
    defaultMessage:
+
      '!!!To verify that you have the correct recovery phrase for this wallet you can enter your 12-word wallet recovery phrase on the following screen.',
+
    description:
+
      'Label for the recoveryPhraseStep1Paragraph1 on wallet settings.',
+
  },
+
  recoveryPhraseStep1Paragraph2: {
+
    id: 'wallet.settings.recoveryPhraseStep1Paragraph2',
+
    defaultMessage:
+
      '!!!Are you being watched? Please make sure that nobody can see your screen while you are entering your wallet recovery phrase.',
+
    description:
+
      'Label for the recoveryPhraseStep1Paragraph2 on wallet settings.',
+
  },
+
  recoveryPhraseStep1Button: {
+
    id: 'wallet.settings.recoveryPhraseStep1Button',
+
    defaultMessage: '!!!Continue',
+
    description: 'Label for the recoveryPhraseStep1Button on wallet settings.',
+
  },
+
});
+

                      
+
type Props = {
+
  onContinue: Function,
+
  onClose: Function,
+
};
+

                      
+
type State = {
+
  safetyAgreement: boolean,
+
};
+

                      
+
@observer
+
export default class WalletRecoveryPhraseStep1 extends Component<Props, State> {
+
  static contextTypes = {
+
    intl: intlShape.isRequired,
+
  };
+

                      
+
  state = {
+
    safetyAgreement: false,
+
  };
+

                      
+
  onToggleSafetyAgreement = checked => {
+
    this.setState({
+
      safetyAgreement: checked,
+
    });
+
  };
+

                      
+
  render() {
+
    const { intl } = this.context;
+
    const { onContinue, onClose } = this.props;
+
    const { safetyAgreement } = this.state;
+
    const isSubmitting = false;
+

                      
+
    const actions = [
+
      {
+
        className: isSubmitting ? styles.isSubmitting : null,
+
        label: intl.formatMessage(messages.recoveryPhraseStep1Button),
+
        primary: true,
+
        onClick: onContinue,
+
        disabled: !safetyAgreement,
+
      },
+
    ];
+

                      
+
    return (
+
      <Dialog
+
        className={styles.dialog}
+
        title={intl.formatMessage(messages.recoveryPhraseStep1Title)}
+
        actions={actions}
+
        closeOnOverlayClick
+
        onClose={onClose}
+
        closeButton={<DialogCloseButton />}
+
      >
+
        <p>{intl.formatMessage(messages.recoveryPhraseStep1Paragraph1)}</p>
+
        <p className={styles.checkboxContainer}>
+
          <Checkbox
+
            onChange={this.onToggleSafetyAgreement}
+
            checked={safetyAgreement}
+
            skin={CheckboxSkin}
+
            className={styles.checkbox}
+
          />
+
          {intl.formatMessage(messages.recoveryPhraseStep1Paragraph2)}
+
        </p>
+
      </Dialog>
+
    );
+
  }
+
}
+
// @flow
+
import React, { Component } from 'react';
+
import { observer } from 'mobx-react';
+
import { defineMessages, intlShape } from 'react-intl';
+
import DialogCloseButton from '../../widgets/DialogCloseButton';
+
import Dialog from '../../widgets/Dialog';
+
import styles from './WalletRecoveryPhraseStepDialogs.scss';
+

                      
+
export const messages = defineMessages({
+
  recoveryPhraseStep2Title: {
+
    id: 'wallet.settings.recoveryPhraseStep2Title',
+
    defaultMessage: '!!!Wallet recovery phrase verification',
+
    description: 'Label for the recoveryPhraseStep2Title on wallet settings.',
+
  },
+
  recoveryPhraseStep2Description: {
+
    id: 'wallet.settings.recoveryPhraseStep2Description',
+
    defaultMessage:
+
      '!!!Please enter your 12-word wallet recovery phrase. Make sure you enter the words in the correct order.',
+
    description:
+
      'Label for the recoveryPhraseStep2Description on wallet settings.',
+
  },
+
  recoveryPhraseStep2Subtitle: {
+
    id: 'wallet.settings.recoveryPhraseStep2Subtitle',
+
    defaultMessage: '!!!Recovery phrase',
+
    description:
+
      'Label for the recoveryPhraseStep2Subtitle on wallet settings.',
+
  },
+
  recoveryPhraseStep2Button: {
+
    id: 'wallet.settings.recoveryPhraseStep2Button',
+
    defaultMessage: '!!!Verify',
+
    description: 'Label for the recoveryPhraseStep2Button on wallet settings.',
+
  },
+
});
+

                      
+
type Props = {
+
  onVerify: Function,
+
  onClose: Function,
+
};
+

                      
+
type State = {
+
  isVeryfying: boolean,
+
};
+

                      
+
@observer
+
export default class WalletRecoveryPhraseStep1 extends Component<Props, State> {
+
  static contextTypes = {
+
    intl: intlShape.isRequired,
+
  };
+

                      
+
  state = {
+
    isVeryfying: false,
+
  };
+

                      
+
  handleVerify = () => {
+
    this.setState({
+
      isVeryfying: true,
+
    });
+
    this.props.onVerify();
+
  };
+

                      
+
  render() {
+
    const { intl } = this.context;
+
    const { onClose, onVerify } = this.props;
+
    const { isVeryfying } = this.state;
+

                      
+
    const actions = [
+
      {
+
        className: isVeryfying ? styles.isVeryfying : null,
+
        label: `${intl.formatMessage(
+
          messages.recoveryPhraseStep2Button
+
        )} - successfuly`,
+
        primary: true,
+
        onClick: () => onVerify(true),
+
        disabled: isVeryfying,
+
      },
+
      {
+
        label: `${intl.formatMessage(
+
          messages.recoveryPhraseStep2Button
+
        )} - failure`,
+
        onClick: () => onVerify(false),
+
        className: 'attention',
+
        disabled: isVeryfying,
+
      },
+
    ];
+

                      
+
    return (
+
      <Dialog
+
        className={styles.dialog}
+
        title={intl.formatMessage(messages.recoveryPhraseStep2Title)}
+
        actions={actions}
+
        closeOnOverlayClick
+
        onClose={onClose}
+
        closeButton={<DialogCloseButton />}
+
      >
+
        <p>{intl.formatMessage(messages.recoveryPhraseStep2Description)}</p>
+
        <div className={styles.subtitle}>
+
          <h2>{intl.formatMessage(messages.recoveryPhraseStep2Subtitle)}</h2>
+
        </div>
+
      </Dialog>
+
    );
+
  }
+
}
+
// @flow
+
import React, { Component } from 'react';
+
import { observer } from 'mobx-react';
+
import { defineMessages, intlShape } from 'react-intl';
+
import { Checkbox } from 'react-polymorph/lib/components/Checkbox';
+
import { CheckboxSkin } from 'react-polymorph/lib/skins/simple/CheckboxSkin';
+
import DialogCloseButton from '../../widgets/DialogCloseButton';
+
import Dialog from '../../widgets/Dialog';
+
import styles from './WalletRecoveryPhraseStepDialogs.scss';
+

                      
+
export const messages = defineMessages({
+
  recoveryPhraseStep3Title: {
+
    id: 'wallet.settings.recoveryPhraseStep3Title',
+
    defaultMessage: '!!!verification successful',
+
    description: 'Label for the recoveryPhraseStep3Title on wallet settings.',
+
  },
+
  recoveryPhraseStep3Paragraph1: {
+
    id: 'wallet.settings.recoveryPhraseStep3Paragraph1',
+
    defaultMessage:
+
      '!!!You have verified the recovery phrase for this wallet. You can use it at any time to recover the funds in this wallet on another computer, even if using a different version of Daedalus.',
+
    description:
+
      'Label for the recoveryPhraseStep3Paragraph1 on wallet settings.',
+
  },
+
  recoveryPhraseStep3Paragraph2: {
+
    id: 'wallet.settings.recoveryPhraseStep3Paragraph2',
+
    defaultMessage:
+
      '!!!Please make sure to keep the paper with your wallet recovery phrase in a safe place. Anyone with access to your wallet recovery phrase can take control of your funds.',
+
    description:
+
      'Label for the recoveryPhraseStep3Paragraph2 on wallet settings.',
+
  },
+
  recoveryPhraseStep3Button: {
+
    id: 'wallet.settings.recoveryPhraseStep3Button',
+
    defaultMessage: '!!!Continue',
+
    description: 'Label for the recoveryPhraseStep3Button on wallet settings.',
+
  },
+
});
+

                      
+
type Props = {
+
  onClose: Function,
+
};
+

                      
+
type State = {
+
  safetyAgreement: boolean,
+
};
+

                      
+
@observer
+
export default class WalletRecoveryPhraseStep1 extends Component<Props, State> {
+
  static contextTypes = {
+
    intl: intlShape.isRequired,
+
  };
+

                      
+
  state = {
+
    safetyAgreement: false,
+
  };
+

                      
+
  onToggleSafetyAgreement = checked => {
+
    this.setState({
+
      safetyAgreement: checked,
+
    });
+
  };
+

                      
+
  render() {
+
    const { intl } = this.context;
+
    const { onClose } = this.props;
+
    const { safetyAgreement } = this.state;
+

                      
+
    const actions = [
+
      {
+
        label: 'Continue',
+
        primary: true,
+
        onClick: onClose,
+
        disabled: !safetyAgreement,
+
      },
+
    ];
+

                      
+
    return (
+
      <Dialog
+
        className={styles.dialog}
+
        title={intl.formatMessage(messages.recoveryPhraseStep3Title)}
+
        actions={actions}
+
        closeOnOverlayClick
+
        onClose={onClose}
+
        closeButton={<DialogCloseButton />}
+
      >
+
        <p>{intl.formatMessage(messages.recoveryPhraseStep3Paragraph1)}</p>
+
        <p className={styles.checkboxContainer}>
+
          <Checkbox
+
            onChange={this.onToggleSafetyAgreement}
+
            checked={safetyAgreement}
+
            skin={CheckboxSkin}
+
            className={styles.checkbox}
+
          />
+
          {intl.formatMessage(messages.recoveryPhraseStep3Paragraph2)}
+
        </p>
+
      </Dialog>
+
    );
+
  }
+
}
+
// @flow
+
import React, { Component } from 'react';
+
import { observer } from 'mobx-react';
+
import { defineMessages, intlShape } from 'react-intl';
+
import DialogCloseButton from '../../widgets/DialogCloseButton';
+
import Dialog from '../../widgets/Dialog';
+
import styles from './WalletRecoveryPhraseStepDialogs.scss';
+

                      
+
export const messages = defineMessages({
+
  recoveryPhraseStep4Title: {
+
    id: 'wallet.settings.recoveryPhraseStep4Title',
+
    defaultMessage: '!!!verification failure',
+
    description: 'Label for the recoveryPhraseStep4Title on wallet settings.',
+
  },
+
  recoveryPhraseStep4Paragraph1: {
+
    id: 'wallet.settings.recoveryPhraseStep4Paragraph1',
+
    defaultMessage:
+
      '!!!The wallet recovery phrase you have entered does not match the recovery phrase associated with this wallet. Make sure you have entered the wallet recovery phrase which was written down during the wallet creation process for this wallet and make sure the words are in the correct order.',
+
    description:
+
      'Label for the recoveryPhraseStep4Paragraph1 on wallet settings.',
+
  },
+
  recoveryPhraseStep4Paragraph2: {
+
    id: 'wallet.settings.recoveryPhraseStep4Paragraph2',
+
    defaultMessage:
+
      '!!!If you are unable to verify your wallet recovery phrase you should create a new wallet and move all of the funds from this wallet to the new wallet. If you do this, make sure you keep the wallet recovery phrase for the new wallet safe and secure.',
+
    description:
+
      'Label for the recoveryPhraseStep4Paragraph2 on wallet settings.',
+
  },
+
  recoveryPhraseStep4Button: {
+
    id: 'wallet.settings.recoveryPhraseStep4Button',
+
    defaultMessage: '!!!Verify recovery phrase again',
+
    description: 'Label for the recoveryPhraseStep4Button on wallet settings.',
+
  },
+
});
+

                      
+
type Props = {
+
  onClose: Function,
+
  onVerifyAgain: Function,
+
};
+

                      
+
@observer
+
export default class WalletRecoveryPhraseStep1 extends Component<Props> {
+
  static contextTypes = {
+
    intl: intlShape.isRequired,
+
  };
+
  render() {
+
    const { intl } = this.context;
+
    const { onClose, onVerifyAgain } = this.props;
+

                      
+
    const actions = [
+
      {
+
        label: intl.formatMessage(messages.recoveryPhraseStep4Button),
+
        onClick: onVerifyAgain,
+
        className: 'attention',
+
      },
+
    ];
+

                      
+
    return (
+
      <Dialog
+
        className={styles.dialog}
+
        title={intl.formatMessage(messages.recoveryPhraseStep4Title)}
+
        actions={actions}
+
        closeOnOverlayClick
+
        onClose={onClose}
+
        closeButton={<DialogCloseButton />}
+
      >
+
        <p>{intl.formatMessage(messages.recoveryPhraseStep4Paragraph1)}</p>
+
        <p>{intl.formatMessage(messages.recoveryPhraseStep4Paragraph2)}</p>
+
      </Dialog>
+
    );
+
  }
+
}
+
.dialog {
+
  p {
+
    color: #5e6066;
+
    font-family: var(--font-light);
+
    line-height: 1.38;
+
    &:first-child {
+
      margin-bottom: 20px;
+
    }
+
  }
+
}
+
.subtitle h2 {
+
  color: #5e6066;
+
  font-family: var(--font-regular);
+
  font-size: 16px;
+
  font-weight: 500;
+
  line-height: 1.38;
+
}
+
.checkboxContainer {
+
  display: flex;
+
}
+
.checkbox {
+
  margin-right: 20px;
+
}
import ChangeSpendingPasswordDialog from './ChangeSpendingPasswordDialog';
import globalMessages from '../../../i18n/global-messages';
import styles from './WalletSettings.scss';
+
import WalletRecoveryPhrase from './WalletRecoveryPhrase';

                      
export const messages = defineMessages({
  name: {
type Props = {
  assuranceLevels: Array<{ value: string, label: ReactIntlMessage }>,
  walletName: string,
+
  walletCreationDate: Date,
  walletAssurance: string,
  isSpendingPasswordSet: boolean,
  spendingPasswordUpdateDate: ?Date,
  changeSpendingPasswordDialog: Node,
  deleteWalletDialogContainer: Node,
  exportWalletDialogContainer: Node,
+
  walletRecoveryPhraseStep1Container: Node,
+
  walletRecoveryPhraseStep2Container: Node,
+
  walletRecoveryPhraseStep3Container: Node,
+
  walletRecoveryPhraseStep4Container: Node,
+
  mnemonicsConfirmationDate?: Date,
};

                      
@observer
      assuranceLevels,
      walletAssurance,
      walletName,
+
      walletCreationDate,
      isSpendingPasswordSet,
      spendingPasswordUpdateDate,
      error,
      changeSpendingPasswordDialog,
      deleteWalletDialogContainer,
      exportWalletDialogContainer,
+
      walletRecoveryPhraseStep1Container,
+
      walletRecoveryPhraseStep2Container,
+
      walletRecoveryPhraseStep3Container,
+
      walletRecoveryPhraseStep4Container,
+
      mnemonicsConfirmationDate,
    } = this.props;

                      
    const assuranceLevelOptions = assuranceLevels.map(assurance => ({
            }
          />

                      
+
          <WalletRecoveryPhrase
+
            mnemonicsConfirmationDate={mnemonicsConfirmationDate}
+
            walletCreationDate={walletCreationDate}
+
            openDialogAction={openDialogAction}
+
            isDialogOpen={isDialogOpen}
+
            walletRecoveryPhraseStep1Container={
+
              walletRecoveryPhraseStep1Container
+
            }
+
            walletRecoveryPhraseStep2Container={
+
              walletRecoveryPhraseStep2Container
+
            }
+
            walletRecoveryPhraseStep3Container={
+
              walletRecoveryPhraseStep3Container
+
            }
+
            walletRecoveryPhraseStep4Container={
+
              walletRecoveryPhraseStep4Container
+
            }
+
          />
+

                      
          {error && <p className={styles.error}>{intl.formatMessage(error)}</p>}

                      
          <div className={styles.actionButtons}>
  'hashImage',
  'config',
];
+

                      
+
// WALLET RECOVERY PHRASE CHECKING
+

                      
+
export const MNEMONICS_CHECKING_WARNING = 150; // days
+
export const MNEMONICS_CHECKING_NOTIFICATION = 365; // days
import WalletSettings from '../../components/wallet/settings/WalletSettings';
import type { InjectedProps } from '../../types/injectedPropsType';
import { isValidWalletName } from '../../utils/validations';
+
import { getWalletLocalData } from '../../utils/walletLocalStorage.js';
import ChangeSpendingPasswordDialogContainer from './dialogs/settings/ChangeSpendingPasswordDialogContainer';
import DeleteWalletDialogContainer from './dialogs/settings/DeleteWalletDialogContainer';
import ExportWalletToFileDialogContainer from './dialogs/settings/ExportWalletToFileDialogContainer';
+
import WalletRecoveryPhraseStep1Container from './dialogs/settings/WalletRecoveryPhraseStep1Container';
+
import WalletRecoveryPhraseStep2Container from './dialogs/settings/WalletRecoveryPhraseStep2Container';
+
import WalletRecoveryPhraseStep3Container from './dialogs/settings/WalletRecoveryPhraseStep3Container';
+
import WalletRecoveryPhraseStep4Container from './dialogs/settings/WalletRecoveryPhraseStep4Container';

                      
type Props = InjectedProps;

                      
+
type State = {
+
  mnemonicsConfirmationDate?: Date,
+
};
+

                      
@inject('stores', 'actions')
@observer
-
export default class WalletSettingsPage extends Component<Props> {
+
export default class WalletSettingsPage extends Component<Props, State> {
  static defaultProps = { actions: null, stores: null };

                      
+
  state = {
+
    mnemonicsConfirmationDate: null,
+
  };
+

                      
+
  componentDidMount() {
+
    this.getWalletLocalData();
+
  }
+

                      
+
  getWalletLocalData = async () => {
+
    const { id } = this.activeWallet;
+
    const { mnemonicsConfirmationDate } = await getWalletLocalData(id);
+
    this.setState({ mnemonicsConfirmationDate });
+
  };
+

                      
+
  get activeWallet() {
+
    const { active: activeWallet } = this.props.stores.wallets;
+

                      
+
    // Guard against potential null values
+
    if (!activeWallet)
+
      throw new Error('Active wallet required for WalletSettingsPage.');
+

                      
+
    return activeWallet;
+
  }
+

                      
  render() {
-
    const { uiDialogs, wallets, walletSettings, app } = this.props.stores;
+
    const { uiDialogs, walletSettings, app } = this.props.stores;
    const { actions } = this.props;
    const {
      environment: { isProduction },
    } = app;
-
    const activeWallet = wallets.active;
+
    const { activeWallet } = this;
    const {
      WALLET_ASSURANCE_LEVEL_OPTIONS,
      updateWalletRequest,
      cancelEditingWalletField,
      updateWalletField,
    } = actions.walletSettings;
-

                      
-
    // Guard against potential null values
-
    if (!activeWallet)
-
      throw new Error('Active wallet required for WalletSettingsPage.');
+
    const { mnemonicsConfirmationDate } = this.state;

                      
    return (
      <WalletSettings
        openDialogAction={actions.dialogs.open.trigger}
        isSpendingPasswordSet={activeWallet.hasPassword}
        spendingPasswordUpdateDate={activeWallet.passwordUpdateDate}
+
        mnemonicsConfirmationDate={mnemonicsConfirmationDate}
        isDialogOpen={uiDialogs.isOpen}
+
        walletId={activeWallet.id}
        walletName={activeWallet.name}
+
        walletCreationDate={activeWallet.createdAt}
        isSubmitting={updateWalletRequest.isExecuting}
        isInvalid={
          updateWalletRequest.wasExecuted &&
        changeSpendingPasswordDialog={<ChangeSpendingPasswordDialogContainer />}
        deleteWalletDialogContainer={<DeleteWalletDialogContainer />}
        exportWalletDialogContainer={<ExportWalletToFileDialogContainer />}
+
        walletRecoveryPhraseStep1Container={
+
          <WalletRecoveryPhraseStep1Container />
+
        }
+
        walletRecoveryPhraseStep2Container={
+
          <WalletRecoveryPhraseStep2Container />
+
        }
+
        walletRecoveryPhraseStep3Container={
+
          <WalletRecoveryPhraseStep3Container />
+
        }
+
        walletRecoveryPhraseStep4Container={
+
          <WalletRecoveryPhraseStep4Container />
+
        }
      />
    );
  }
+
// @flow
+
import React, { Component } from 'react';
+
import { observer, inject } from 'mobx-react';
+
import WalletRecoveryPhraseStep1Dialog from '../../../../components/wallet/settings/WalletRecoveryPhraseStep1Dialog';
+
import WalletRecoveryPhraseStep2Dialog from '../../../../components/wallet/settings/WalletRecoveryPhraseStep2Dialog';
+
import type { InjectedDialogContainerProps } from '../../../../types/injectedPropsType';
+

                      
+
type Props = InjectedDialogContainerProps;
+

                      
+
@inject('stores', 'actions')
+
@observer
+
export default class WalletRecoveryPhraseStep1Container extends Component<Props> {
+
  static defaultProps = {
+
    actions: null,
+
    stores: null,
+
    children: null,
+
    onClose: () => {},
+
  };
+

                      
+
  handleContinue = () => {
+
    this.props.actions.dialogs.open.trigger({
+
      dialog: WalletRecoveryPhraseStep2Dialog,
+
    });
+
  };
+

                      
+
  render() {
+
    const { closeActiveDialog } = this.props.actions.dialogs;
+
    return (
+
      <WalletRecoveryPhraseStep1Dialog
+
        onContinue={this.handleContinue}
+
        onClose={closeActiveDialog.trigger}
+
      />
+
    );
+
  }
+
}
+
// @flow
+
import React, { Component } from 'react';
+
import { observer, inject } from 'mobx-react';
+
import WalletRecoveryPhraseStep2Dialog from '../../../../components/wallet/settings/WalletRecoveryPhraseStep2Dialog';
+
import WalletRecoveryPhraseStep3Dialog from '../../../../components/wallet/settings/WalletRecoveryPhraseStep3Dialog';
+
import WalletRecoveryPhraseStep4Dialog from '../../../../components/wallet/settings/WalletRecoveryPhraseStep4Dialog';
+
import type { InjectedDialogContainerProps } from '../../../../types/injectedPropsType';
+

                      
+
type Props = InjectedDialogContainerProps;
+

                      
+
@inject('stores', 'actions')
+
@observer
+
export default class WalletRecoveryPhraseStep2Container extends Component<Props> {
+
  static defaultProps = {
+
    actions: null,
+
    stores: null,
+
    children: null,
+
    onClose: () => {},
+
  };
+

                      
+
  handleVerify = successful => {
+
    const dialog = successful
+
      ? WalletRecoveryPhraseStep3Dialog
+
      : WalletRecoveryPhraseStep4Dialog;
+
    this.props.actions.dialogs.open.trigger({
+
      dialog,
+
    });
+
  };
+

                      
+
  render() {
+
    const { closeActiveDialog } = this.props.actions.dialogs;
+
    return (
+
      <WalletRecoveryPhraseStep2Dialog
+
        onVerify={this.handleVerify}
+
        onClose={closeActiveDialog.trigger}
+
      />
+
    );
+
  }
+
}
+
// @flow
+
import React, { Component } from 'react';
+
import { observer, inject } from 'mobx-react';
+
import WalletRecoveryPhraseStep3Dialog from '../../../../components/wallet/settings/WalletRecoveryPhraseStep3Dialog';
+
import type { InjectedDialogContainerProps } from '../../../../types/injectedPropsType';
+

                      
+
type Props = InjectedDialogContainerProps;
+

                      
+
@inject('stores', 'actions')
+
@observer
+
export default class WalletRecoveryPhraseStep2Container extends Component<Props> {
+
  static defaultProps = {
+
    actions: null,
+
    stores: null,
+
    children: null,
+
    onClose: () => {},
+
  };
+

                      
+
  render() {
+
    const { closeActiveDialog } = this.props.actions.dialogs;
+
    return (
+
      <WalletRecoveryPhraseStep3Dialog onClose={closeActiveDialog.trigger} />
+
    );
+
  }
+
}
Diff too large – View on GitHub