From e226b10a89d87af07c7c35ff1251a8264f3bb1b8 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Tue, 28 Nov 2017 20:24:35 -0800 Subject: Add react-router to allow use of the browser back button --- ui/app/components/pages/keychains/restore-vault.js | 167 ++++++++++++++++++ ui/app/components/pages/keychains/reveal-seed.js | 192 +++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 ui/app/components/pages/keychains/restore-vault.js create mode 100644 ui/app/components/pages/keychains/reveal-seed.js (limited to 'ui/app/components/pages/keychains') diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js new file mode 100644 index 000000000..5573f2dd2 --- /dev/null +++ b/ui/app/components/pages/keychains/restore-vault.js @@ -0,0 +1,167 @@ +const { withRouter } = require('react-router-dom') +const PropTypes = require('prop-types') +const { compose } = require('recompose') +const PersistentForm = require('../../../../lib/persistent-form') +const { connect } = require('react-redux') +const h = require('react-hyperscript') +const { createNewVaultAndRestore } = require('../../../actions') +const { DEFAULT_ROUTE } = require('../../../routes') + +class RestoreVaultPage extends PersistentForm { + constructor (props) { + super(props) + + this.state = { + error: null, + } + } + + createOnEnter (event) { + if (event.key === 'Enter') { + this.createNewVaultAndRestore() + } + } + + createNewVaultAndRestore () { + this.setState({ error: null }) + + // check password + var passwordBox = document.getElementById('password-box') + var password = passwordBox.value + var passwordConfirmBox = document.getElementById('password-box-confirm') + var passwordConfirm = passwordConfirmBox.value + + if (password.length < 8) { + this.setState({ error: 'Password not long enough' }) + return + } + + if (password !== passwordConfirm) { + this.setState({ error: 'Passwords don\'t match' }) + return + } + + // check seed + var seedBox = document.querySelector('textarea.twelve-word-phrase') + var seed = seedBox.value.trim() + if (seed.split(' ').length !== 12) { + this.setState({ error: 'Seed phrases are 12 words long' }) + return + } + + // submit + this.props.createNewVaultAndRestore(password, seed) + .then(() => history.push(DEFAULT_ROUTE)) + .catch(({ message }) => this.setState({ error: message })) + } + + render () { + const { error } = this.state + const { history } = this.props + this.persistentFormParentId = 'restore-vault-form' + + return ( + h('.initialize-screen.flex-column.flex-center.flex-grow', [ + + h('h3.flex-center.text-transform-uppercase', { + style: { + background: '#EBEBEB', + color: '#AEAEAE', + marginBottom: 24, + width: '100%', + fontSize: '20px', + padding: 6, + }, + }, [ + 'Restore Vault', + ]), + + // wallet seed entry + h('h3', 'Wallet Seed'), + h('textarea.twelve-word-phrase.letter-spacey', { + dataset: { + persistentFormId: 'wallet-seed', + }, + placeholder: 'Enter your secret twelve word phrase here to restore your vault.', + }), + + // password + h('input.large-input.letter-spacey', { + type: 'password', + id: 'password-box', + placeholder: 'New Password (min 8 chars)', + dataset: { + persistentFormId: 'password', + }, + style: { + width: 260, + marginTop: 12, + }, + }), + + // confirm password + h('input.large-input.letter-spacey', { + type: 'password', + id: 'password-box-confirm', + placeholder: 'Confirm Password', + onKeyPress: this.createOnEnter.bind(this), + dataset: { + persistentFormId: 'password-confirmation', + }, + style: { + width: 260, + marginTop: 16, + }, + }), + + error && ( + h('span.error.in-progress-notification', error) + ), + + // submit + h('.flex-row.flex-space-between', { + style: { + marginTop: 30, + width: '50%', + }, + }, [ + + // cancel + h('button.primary', { onClick: () => history.goBack() }, 'CANCEL'), + + // submit + h('button.primary', { + onClick: this.createNewVaultAndRestore.bind(this), + }, 'OK'), + + ]), + ]) + ) + } +} + +RestoreVaultPage.propTypes = { + history: PropTypes.object, +} + +const mapStateToProps = state => { + const { appState: { warning, forgottenPassword } } = state + + return { + warning, + forgottenPassword, + } +} + +const mapDispatchToProps = dispatch => { + return { + createNewVaultAndRestore: (password, seed) => { + return dispatch(createNewVaultAndRestore(password, seed)) + }, + } +} + +module.exports = compose( + withRouter, + connect(mapStateToProps, mapDispatchToProps) +)(RestoreVaultPage) diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js new file mode 100644 index 000000000..dc2cfc23e --- /dev/null +++ b/ui/app/components/pages/keychains/reveal-seed.js @@ -0,0 +1,192 @@ +const { Component } = require('react') +const { connect } = require('react-redux') +const PropTypes = require('prop-types') +const h = require('react-hyperscript') +const { exportAsFile } = require('../../../util') +const { requestRevealSeed, confirmSeedWords } = require('../../../actions') +const { DEFAULT_ROUTE } = require('../../../routes') + +class RevealSeedPage extends Component { + componentDidMount () { + document.getElementById('password-box').focus() + } + + checkConfirmation (event) { + if (event.key === 'Enter') { + event.preventDefault() + this.revealSeedWords() + } + } + + revealSeedWords () { + const password = document.getElementById('password-box').value + this.props.requestRevealSeed(password) + } + + renderSeed () { + const { seedWords, confirmSeedWords, history } = this.props + + return ( + h('.initialize-screen.flex-column.flex-center.flex-grow', [ + + h('h3.flex-center.text-transform-uppercase', { + style: { + background: '#EBEBEB', + color: '#AEAEAE', + marginTop: 36, + marginBottom: 8, + width: '100%', + fontSize: '20px', + padding: 6, + }, + }, [ + 'Vault Created', + ]), + + h('div', { + style: { + fontSize: '1em', + marginTop: '10px', + textAlign: 'center', + }, + }, [ + h('span.error', 'These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret.'), + ]), + + h('textarea.twelve-word-phrase', { + readOnly: true, + value: seedWords, + }), + + h('button.primary', { + onClick: () => confirmSeedWords().then(() => history.push(DEFAULT_ROUTE)), + style: { + margin: '24px', + fontSize: '0.9em', + marginBottom: '10px', + }, + }, 'I\'ve copied it somewhere safe'), + + h('button.primary', { + onClick: () => exportAsFile(`MetaMask Seed Words`, seedWords), + style: { + margin: '10px', + fontSize: '0.9em', + }, + }, 'Save Seed Words As File'), + ]) + ) + } + + renderConfirmation () { + const { history, warning, inProgress } = this.props + + return ( + h('.initialize-screen.flex-column.flex-center.flex-grow', { + style: { maxWidth: '420px' }, + }, [ + + h('h3.flex-center.text-transform-uppercase', { + style: { + background: '#EBEBEB', + color: '#AEAEAE', + marginBottom: 24, + width: '100%', + fontSize: '20px', + padding: 6, + }, + }, [ + 'Reveal Seed Words', + ]), + + h('.div', { + style: { + display: 'flex', + flexDirection: 'column', + padding: '20px', + justifyContent: 'center', + }, + }, [ + + h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'), + + // confirmation + h('input.large-input.letter-spacey', { + type: 'password', + id: 'password-box', + placeholder: 'Enter your password to confirm', + onKeyPress: this.checkConfirmation.bind(this), + style: { + width: 260, + marginTop: '12px', + }, + }), + + h('.flex-row.flex-start', { + style: { + marginTop: 30, + width: '50%', + }, + }, [ + // cancel + h('button.primary', { + onClick: () => history.goBack(), + }, 'CANCEL'), + + // submit + h('button.primary', { + style: { marginLeft: '10px' }, + onClick: this.revealSeedWords.bind(this), + }, 'OK'), + + ]), + + warning && ( + h('span.error', { + style: { + margin: '20px', + }, + }, warning.split('-')) + ), + + inProgress && ( + h('span.in-progress-notification', 'Generating Seed...') + ), + ]), + ]) + ) + } + + render () { + return this.props.seedWords + ? this.renderSeed() + : this.renderConfirmation() + } +} + +RevealSeedPage.propTypes = { + requestRevealSeed: PropTypes.func, + confirmSeedWords: PropTypes.func, + seedWords: PropTypes.string, + inProgress: PropTypes.bool, + history: PropTypes.object, + warning: PropTypes.string, +} + +const mapStateToProps = state => { + const { appState: { warning }, metamask: { seedWords } } = state + + return { + warning, + seedWords, + } +} + +const mapDispatchToProps = dispatch => { + return { + requestRevealSeed: password => dispatch(requestRevealSeed(password)), + confirmSeedWords: () => dispatch(confirmSeedWords()), + } +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(RevealSeedPage) -- cgit From dde39e82b5723ba8056b73f0f823d40c3e702a99 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 4 Dec 2017 01:24:30 -0500 Subject: Add routes for mascara --- ui/app/components/pages/keychains/reveal-seed.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'ui/app/components/pages/keychains') diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js index dc2cfc23e..029eb7d8e 100644 --- a/ui/app/components/pages/keychains/reveal-seed.js +++ b/ui/app/components/pages/keychains/reveal-seed.js @@ -8,7 +8,10 @@ const { DEFAULT_ROUTE } = require('../../../routes') class RevealSeedPage extends Component { componentDidMount () { - document.getElementById('password-box').focus() + const passwordBox = document.getElementById('password-box') + if (passwordBox) { + passwordBox.focus() + } } checkConfirmation (event) { -- cgit From bdc4a6964ae83faa8229c50870e3bcc9b9074989 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Fri, 30 Mar 2018 14:51:11 -0700 Subject: Fix merge conflicts. Refactor renderPrimary into Home component --- ui/app/components/pages/keychains/restore-vault.js | 26 ++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'ui/app/components/pages/keychains') diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js index 749da9758..24ebf89e3 100644 --- a/ui/app/components/pages/keychains/restore-vault.js +++ b/ui/app/components/pages/keychains/restore-vault.js @@ -2,9 +2,9 @@ const { withRouter } = require('react-router-dom') const PropTypes = require('prop-types') const { compose } = require('recompose') const PersistentForm = require('../../../../lib/persistent-form') -const { connect } = require('react-redux') +const connect = require('../../../metamask-connect') const h = require('react-hyperscript') -const { createNewVaultAndRestore } = require('../../../actions') +const { createNewVaultAndRestore, unMarkPasswordForgotten } = require('../../../actions') const { DEFAULT_ROUTE } = require('../../../routes') class RestoreVaultPage extends PersistentForm { @@ -22,6 +22,11 @@ class RestoreVaultPage extends PersistentForm { } } + cancel () { + this.props.unMarkPasswordForgotten() + .then(this.props.history.goBack()) + } + createNewVaultAndRestore () { this.setState({ error: null }) @@ -51,7 +56,7 @@ class RestoreVaultPage extends PersistentForm { // submit this.props.createNewVaultAndRestore(password, seed) - .then(() => history.push(DEFAULT_ROUTE)) + .then(() => this.props.history.push(DEFAULT_ROUTE)) .catch(({ message }) => { this.setState({ error: message }) log.error(message) @@ -76,7 +81,7 @@ class RestoreVaultPage extends PersistentForm { padding: 6, }, }, [ - 'Restore Vault', + this.props.t('restoreVault'), ]), // wallet seed entry @@ -85,14 +90,14 @@ class RestoreVaultPage extends PersistentForm { dataset: { persistentFormId: 'wallet-seed', }, - placeholder: 'Enter your secret twelve word phrase here to restore your vault.', + placeholder: this.props.t('secretPhrase'), }), // password h('input.large-input.letter-spacey', { type: 'password', id: 'password-box', - placeholder: 'New Password (min 8 chars)', + placeholder: this.props.t('newPassword8Chars'), dataset: { persistentFormId: 'password', }, @@ -106,7 +111,7 @@ class RestoreVaultPage extends PersistentForm { h('input.large-input.letter-spacey', { type: 'password', id: 'password-box-confirm', - placeholder: 'Confirm Password', + placeholder: this.props.t('confirmPassword'), onKeyPress: this.createOnEnter.bind(this), dataset: { persistentFormId: 'password-confirmation', @@ -130,12 +135,14 @@ class RestoreVaultPage extends PersistentForm { }, [ // cancel - h('button.primary', { onClick: () => history.goBack() }, 'CANCEL'), + h('button.primary', { + onClick: () => this.cancel(), + }, this.props.t('cancel')), // submit h('button.primary', { onClick: this.createNewVaultAndRestore.bind(this), - }, 'OK'), + }, this.props.t('ok')), ]), ]) @@ -161,6 +168,7 @@ const mapDispatchToProps = dispatch => { createNewVaultAndRestore: (password, seed) => { return dispatch(createNewVaultAndRestore(password, seed)) }, + unMarkPasswordForgotten: () => dispatch(unMarkPasswordForgotten()), } } -- cgit From 6277a4c46aa2fd94f0fff047aff346d7f255224d Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 2 Apr 2018 02:59:49 -0700 Subject: Refactor onboarding flow --- ui/app/components/pages/keychains/restore-vault.js | 2 +- ui/app/components/pages/keychains/reveal-seed.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'ui/app/components/pages/keychains') diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js index 24ebf89e3..77ec32efe 100644 --- a/ui/app/components/pages/keychains/restore-vault.js +++ b/ui/app/components/pages/keychains/restore-vault.js @@ -24,7 +24,7 @@ class RestoreVaultPage extends PersistentForm { cancel () { this.props.unMarkPasswordForgotten() - .then(this.props.history.goBack()) + .then(this.props.history.push(DEFAULT_ROUTE)) } createNewVaultAndRestore () { diff --git a/ui/app/components/pages/keychains/reveal-seed.js b/ui/app/components/pages/keychains/reveal-seed.js index 029eb7d8e..247f3c8e2 100644 --- a/ui/app/components/pages/keychains/reveal-seed.js +++ b/ui/app/components/pages/keychains/reveal-seed.js @@ -133,7 +133,7 @@ class RevealSeedPage extends Component { }, [ // cancel h('button.primary', { - onClick: () => history.goBack(), + onClick: () => history.push(DEFAULT_ROUTE), }, 'CANCEL'), // submit -- cgit From 516c1869b0f366a42282a66e14185ce630f883dd Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 2 Apr 2018 16:24:37 -0700 Subject: Fix lint errors --- ui/app/components/pages/keychains/restore-vault.js | 1 - 1 file changed, 1 deletion(-) (limited to 'ui/app/components/pages/keychains') diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js index 77ec32efe..d57894e00 100644 --- a/ui/app/components/pages/keychains/restore-vault.js +++ b/ui/app/components/pages/keychains/restore-vault.js @@ -65,7 +65,6 @@ class RestoreVaultPage extends PersistentForm { render () { const { error } = this.state - const { history } = this.props this.persistentFormParentId = 'restore-vault-form' return ( -- cgit From 7129d7c0f3ce11839f8b8a576eb1e2c093fb96cc Mon Sep 17 00:00:00 2001 From: bitpshr Date: Thu, 12 Apr 2018 17:06:59 -0400 Subject: Require loglevel singleton in each module that uses it --- ui/app/components/pages/keychains/restore-vault.js | 1 + 1 file changed, 1 insertion(+) (limited to 'ui/app/components/pages/keychains') diff --git a/ui/app/components/pages/keychains/restore-vault.js b/ui/app/components/pages/keychains/restore-vault.js index d57894e00..33575bfbb 100644 --- a/ui/app/components/pages/keychains/restore-vault.js +++ b/ui/app/components/pages/keychains/restore-vault.js @@ -6,6 +6,7 @@ const connect = require('../../../metamask-connect') const h = require('react-hyperscript') const { createNewVaultAndRestore, unMarkPasswordForgotten } = require('../../../actions') const { DEFAULT_ROUTE } = require('../../../routes') +const log = require('loglevel') class RestoreVaultPage extends PersistentForm { constructor (props) { -- cgit