Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Require phrase confirmation. #5731

Merged
merged 6 commits into from
Jun 9, 2017
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
113 changes: 96 additions & 17 deletions js/src/modals/CreateAccount/AccountDetails/accountDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,16 @@ import styles from '../createAccount.css';
@observer
export default class AccountDetails extends Component {
static propTypes = {
isConfirming: PropTypes.bool,
withRequiredBackup: PropTypes.bool,
createStore: PropTypes.object.isRequired
}

static defaultPropTypes = {
isConfirming: false,
withRequiredBackup: false
}

render () {
const { address, description, name } = this.props.createStore;

Expand Down Expand Up @@ -78,31 +85,103 @@ export default class AccountDetails extends Component {
);
}

renderPhrase () {
const { phrase } = this.props.createStore;
renderRequiredBackup () {
const { phraseBackedUp, phraseBackedUpError } = this.props.createStore;

if (!phrase) {
if (!this.props.withRequiredBackup) {
return null;
}

return (
<Input
allowCopy
hint={
<FormattedMessage
id='createAccount.accountDetails.phrase.hint'
defaultMessage='the account recovery phrase'
<div>
<Input
error={ phraseBackedUpError }
hint={
<FormattedMessage
id='createAccount.accountDetails.phrase.hint'
defaultMessage='the account recovery phrase'
/>
}
label={
<FormattedMessage
id='createAccount.accountDetails.phrase.backedUp'
defaultMessage='Type "I have written down the phrase" below to confirm it is backed up.'
/>
}
onChange={ this.onEditPhraseBackedUp }
value={ phraseBackedUp }
/>
</div>
);
}

renderPhrase () {
const { isConfirming } = this.props;
const { isTest, phrase, backupPhraseError } = this.props.createStore;

const hint = (
<FormattedMessage
id='createAccount.accountDetails.phrase.hint'
defaultMessage='the account recovery phrase'
/>
);
const label = (
<FormattedMessage
id='createAccount.accountDetails.phrase.label'
defaultMessage='owner recovery phrase'
/>
);

if (!isConfirming) {
if (!phrase) {
return null;
}

return (
<div>
<Input
allowCopy
hint={ hint }
label={ label }
readOnly
value={ phrase }
/>
}
label={
<div className={ styles.backupPhrase }>
<FormattedMessage
id='createAccount.accountDetails.phrase.backup'
defaultMessage='Please back up the recovery phrase now. Make sure to keep it private and secure, it allows full and unlimited access to the account.'
/>
</div>
{ this.renderRequiredBackup() }
</div>
);
}

return (
<div>
<Input
allowPaste={ isTest }
error={ backupPhraseError }
hint={ hint }
label={ label }
onChange={ this.onEditPhrase }
value={ phrase }
/>
<div className={ styles.backupPhrase }>
<FormattedMessage
id='createAccount.accountDetails.phrase.label'
defaultMessage='owner recovery phrase (keep private and secure, it allows full and unlimited access to the account)'
id='createAccount.accountDetails.phrase.backupConfirm'
defaultMessage='Type your recovery phrase now.'
/>
}
readOnly
value={ phrase }
/>
</div>
</div>
);
}

onEditPhraseBackedUp = (ev) => {
this.props.createStore.setPhraseBackedUp(ev.target.value);
}

onEditPhrase = (ev) => {
this.props.createStore.setPhrase(ev.target.value);
}
}
5 changes: 5 additions & 0 deletions js/src/modals/CreateAccount/createAccount.css
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,8 @@
padding: 0 4em 1.5em 4em;
text-align: center;
}

.backupPhrase {
line-height: 1.618em;
margin-top: 1.5em;
}
78 changes: 70 additions & 8 deletions js/src/modals/CreateAccount/createAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import NewImport from './NewImport';
import NewQr from './NewQr';
import RawKey from './RawKey';
import RecoveryPhrase from './RecoveryPhrase';
import Store, { STAGE_CREATE, STAGE_INFO, STAGE_SELECT_TYPE } from './store';
import Store, { STAGE_CREATE, STAGE_INFO, STAGE_SELECT_TYPE, STAGE_CONFIRM_BACKUP } from './store';
import TypeIcon from './TypeIcon';
import print from './print';
import recoveryPage from './recoveryPage.ejs';
Expand All @@ -61,6 +61,12 @@ const TITLES = {
defaultMessage='account information'
/>
),
backup: (
<FormattedMessage
id='createAccount.title.backupPhrase'
defaultMessage='confirm recovery phrase'
/>
),
import: (
<FormattedMessage
id='createAccount.title.importAccount'
Expand All @@ -80,7 +86,7 @@ const TITLES = {
/>
)
};
const STAGE_NAMES = [TITLES.type, TITLES.create, TITLES.info];
const STAGE_NAMES = [TITLES.type, TITLES.create, TITLES.info, TITLES.backup];
const STAGE_IMPORT = [TITLES.type, TITLES.import, TITLES.info];
const STAGE_RESTORE = [TITLES.restore, TITLES.info];
const STAGE_QR = [TITLES.type, TITLES.qr, TITLES.info];
Expand Down Expand Up @@ -213,14 +219,25 @@ class CreateAccount extends Component {
}

return (
<AccountDetails createStore={ this.createStore } />
<AccountDetails
createStore={ this.createStore }
withRequiredBackup={ createType === 'fromNew' }
/>
);

case STAGE_CONFIRM_BACKUP:
return (
<AccountDetails
createStore={ this.createStore }
isConfirming
/>
);
}
}

renderDialogActions () {
const { restore } = this.props;
const { createType, canCreate, isBusy, stage } = this.createStore;
const { createType, canCreate, isBusy, stage, phraseBackedUpError } = this.createStore;

const cancelBtn = (
<Button
Expand Down Expand Up @@ -281,8 +298,8 @@ class CreateAccount extends Component {
createType === 'fromNew'
? (
<FormattedMessage
id='createAccount.button.create'
defaultMessage='Create'
id='createAccount.button.next'
defaultMessage='Next'
/>
)
: (
Expand All @@ -292,7 +309,7 @@ class CreateAccount extends Component {
/>
)
}
onClick={ this.onCreate }
onClick={ createType === 'fromNew' ? this.createStore.nextStage : this.onCreate }
/>
];

Expand All @@ -314,6 +331,7 @@ class CreateAccount extends Component {
)
: null,
<Button
disabled={ createType === 'fromNew' && !!phraseBackedUpError }
icon={ <DoneIcon /> }
key='done'
label={
Expand All @@ -322,12 +340,55 @@ class CreateAccount extends Component {
defaultMessage='Done'
/>
}
onClick={ this.onClose }
onClick={ createType === 'fromNew' ? this.onConfirmPhraseBackup : this.onClose }
/>
];

case STAGE_CONFIRM_BACKUP:
return [
<Button
icon={ <DoneIcon /> }
key='done'
label={
<FormattedMessage
id='createAccount.button.create'
defaultMessage='Create'
/>
}
onClick={ this.onCreateNew }
/>
];
}
}

onConfirmPhraseBackup = () => {
this.createStore.clearPhrase();
this.createStore.nextStage();
}

onCreateNew = () => {
this.createStore.setBusy(true);
this.createStore.computeBackupPhraseAddress()
.then(err => {
if (err) {
this.createStore.setBusy(false);
return;
}

return this.createStore.createAccount(this.vaultStore)
.then(() => {
this.createStore.clearPhrase();
this.createStore.setBusy(false);
this.props.onUpdate && this.props.onUpdate();
this.onClose();
});
})
.catch((error) => {
this.createStore.setBusy(false);
this.props.newError(error);
});
}

onCreate = () => {
this.createStore.setBusy(true);

Expand All @@ -345,6 +406,7 @@ class CreateAccount extends Component {
}

onClose = () => {
this.createStore.clearPhrase();
this.props.onClose && this.props.onClose();
}

Expand Down
15 changes: 15 additions & 0 deletions js/src/modals/CreateAccount/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ export default {
/>
),

noMatchBackupPhrase: (
<FormattedMessage
id='errors.noMatchBackupPhrase'
defaultMessage='the supplied recovery phrase does not match'
/>
),

noMatchPhraseBackedUp: (
<FormattedMessage
id='errors.noMatchPhraseBackedUp'
defaultMessage='type "I have written down the phrase"'
/>
),

noName: (
<FormattedMessage
id='errors.noName'
Expand All @@ -59,4 +73,5 @@ export default {
defaultMessage='the raw key needs to be hex, 64 characters in length and contain the prefix "0x"'
/>
)

};
Loading