Merge pull request #4648 from MetaMask/update-import-from-seed-screen

Update import from seed screen on new ui.
feature/default_network_editable
Dan J Miller 6 years ago committed by GitHub
commit 6595f84d07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      app/_locales/en/messages.json
  2. 9
      test/e2e/beta/metamask-beta-ui.spec.js
  3. 2
      ui/app/app.js
  4. 313
      ui/app/components/pages/keychains/restore-vault.js
  5. 9
      ui/app/css/itcss/components/newui-sections.scss

@ -664,6 +664,9 @@
"restoreVault": { "restoreVault": {
"message": "Restore Vault" "message": "Restore Vault"
}, },
"restoreAccountWithSeed": {
"message": "Restore your Account with Seed Phrase"
},
"required": { "required": {
"message": "Required" "message": "Required"
}, },
@ -673,6 +676,9 @@
"walletSeed": { "walletSeed": {
"message": "Wallet Seed" "message": "Wallet Seed"
}, },
"restore": {
"message": "Restore"
},
"revealSeedWords": { "revealSeedWords": {
"message": "Reveal Seed Words" "message": "Reveal Seed Words"
}, },
@ -777,6 +783,9 @@
"sendTokens": { "sendTokens": {
"message": "Send Tokens" "message": "Send Tokens"
}, },
"separateEachWord": {
"message": "Separate each word with a single space"
},
"onlySendToEtherAddress": { "onlySendToEtherAddress": {
"message": "Only send ETH to an Ethereum address." "message": "Only send ETH to an Ethereum address."
}, },

@ -355,9 +355,12 @@ describe('MetaMask', function () {
await seedTextArea.sendKeys(testSeedPhrase) await seedTextArea.sendKeys(testSeedPhrase)
await delay(regularDelayMs) await delay(regularDelayMs)
await driver.findElement(By.id('password-box')).sendKeys('correct horse battery staple') const passwordInputs = await driver.findElements(By.css('input'))
await driver.findElement(By.id('password-box-confirm')).sendKeys('correct horse battery staple') await delay(regularDelayMs)
await driver.findElement(By.css('button:nth-child(2)')).click()
passwordInputs[0].sendKeys('correct horse battery staple')
passwordInputs[1].sendKeys('correct horse battery staple')
await driver.findElement(By.css('.first-time-flow__button')).click()
await delay(regularDelayMs) await delay(regularDelayMs)
}) })

@ -23,7 +23,7 @@ const Authenticated = require('./components/pages/authenticated')
const Initialized = require('./components/pages/initialized') const Initialized = require('./components/pages/initialized')
const Settings = require('./components/pages/settings') const Settings = require('./components/pages/settings')
const UnlockPage = require('./components/pages/unlock-page') const UnlockPage = require('./components/pages/unlock-page')
const RestoreVaultPage = require('./components/pages/keychains/restore-vault') const RestoreVaultPage = require('./components/pages/keychains/restore-vault').default
const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed') const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed')
const AddTokenPage = require('./components/pages/add-token') const AddTokenPage = require('./components/pages/add-token')
const ConfirmAddTokenPage = require('./components/pages/confirm-add-token') const ConfirmAddTokenPage = require('./components/pages/confirm-add-token')

@ -1,178 +1,189 @@
const { withRouter } = require('react-router-dom') import React, { Component } from 'react'
const PropTypes = require('prop-types') import PropTypes from 'prop-types'
const { compose } = require('recompose') import {connect} from 'react-redux'
const PersistentForm = require('../../../../lib/persistent-form') import {
const connect = require('../../../metamask-connect') createNewVaultAndRestore,
const h = require('react-hyperscript') unMarkPasswordForgotten,
const { createNewVaultAndRestore, unMarkPasswordForgotten } = require('../../../actions') } from '../../../actions'
const { DEFAULT_ROUTE } = require('../../../routes') import { DEFAULT_ROUTE } from '../../../routes'
const log = require('loglevel') import TextField from '../../text-field'
class RestoreVaultPage extends PersistentForm { class RestoreVaultPage extends Component {
constructor (props) { static contextTypes = {
super(props) t: PropTypes.func,
this.state = {
error: null,
}
} }
createOnEnter (event) { static propTypes = {
if (event.key === 'Enter') { warning: PropTypes.string,
this.createNewVaultAndRestore() createNewVaultAndRestore: PropTypes.func.isRequired,
} leaveImportSeedScreenState: PropTypes.func,
history: PropTypes.object,
isLoading: PropTypes.bool,
};
state = {
seedPhrase: '',
password: '',
confirmPassword: '',
seedPhraseError: null,
passwordError: null,
confirmPasswordError: null,
} }
cancel () { parseSeedPhrase = (seedPhrase) => {
this.props.unMarkPasswordForgotten() return seedPhrase
.then(this.props.history.push(DEFAULT_ROUTE)) .match(/\w+/g)
.join(' ')
} }
createNewVaultAndRestore () { handleSeedPhraseChange (seedPhrase) {
this.setState({ error: null }) let seedPhraseError = null
if (seedPhrase && this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) {
seedPhraseError = this.context.t('seedPhraseReq')
}
this.setState({ seedPhrase, seedPhraseError })
}
// check password handlePasswordChange (password) {
var passwordBox = document.getElementById('password-box') const { confirmPassword } = this.state
var password = passwordBox.value let confirmPasswordError = null
var passwordConfirmBox = document.getElementById('password-box-confirm') let passwordError = null
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) { if (password && password.length < 8) {
this.setState({ error: 'Password not long enough' }) passwordError = this.context.t('passwordNotLongEnough')
return
} }
if (password !== passwordConfirm) { if (confirmPassword && password !== confirmPassword) {
this.setState({ error: 'Passwords don\'t match' }) confirmPasswordError = this.context.t('passwordsDontMatch')
return
} }
// check seed this.setState({ password, passwordError, confirmPasswordError })
var seedBox = document.querySelector('textarea.twelve-word-phrase') }
var seed = seedBox.value.trim()
if (seed.split(' ').length !== 12) { handleConfirmPasswordChange (confirmPassword) {
this.setState({ error: 'Seed phrases are 12 words long' }) const { password } = this.state
return let confirmPasswordError = null
if (password !== confirmPassword) {
confirmPasswordError = this.context.t('passwordsDontMatch')
} }
// submit this.setState({ confirmPassword, confirmPasswordError })
this.props.createNewVaultAndRestore(password, seed) }
.then(() => this.props.history.push(DEFAULT_ROUTE))
.catch(({ message }) => { onClick = () => {
this.setState({ error: message }) const { password, seedPhrase } = this.state
log.error(message) const {
}) createNewVaultAndRestore,
leaveImportSeedScreenState,
history,
} = this.props
leaveImportSeedScreenState()
createNewVaultAndRestore(password, this.parseSeedPhrase(seedPhrase))
.then(() => history.push(DEFAULT_ROUTE))
}
hasError () {
const { passwordError, confirmPasswordError, seedPhraseError } = this.state
return passwordError || confirmPasswordError || seedPhraseError
} }
render () { render () {
const { error } = this.state const {
this.persistentFormParentId = 'restore-vault-form' seedPhrase,
password,
confirmPassword,
seedPhraseError,
passwordError,
confirmPasswordError,
} = this.state
const { t } = this.context
const { isLoading } = this.props
const disabled = !seedPhrase || !password || !confirmPassword || isLoading || this.hasError()
return ( return (
h('.initialize-screen.flex-column.flex-center.flex-grow', [ <div className="first-view-main-wrapper">
<div className="first-view-main">
h('h3.flex-center.text-transform-uppercase', { <div className="import-account">
style: { <a
background: '#EBEBEB', className="import-account__back-button"
color: '#AEAEAE', onClick={e => {
marginBottom: 24, e.preventDefault()
width: '100%', this.props.history.goBack()
fontSize: '20px', }}
padding: 6, href="#"
}, >
}, [ {`< Back`}
this.props.t('restoreVault'), </a>
]), <div className="import-account__title">
{ this.context.t('restoreAccountWithSeed') }
// wallet seed entry </div>
h('h3', 'Wallet Seed'), <div className="import-account__selector-label">
h('textarea.twelve-word-phrase.letter-spacey', { { this.context.t('secretPhrase') }
dataset: { </div>
persistentFormId: 'wallet-seed', <div className="import-account__input-wrapper">
}, <label className="import-account__input-label">Wallet Seed</label>
placeholder: this.props.t('secretPhrase'), <textarea
}), className="import-account__secret-phrase"
onChange={e => this.handleSeedPhraseChange(e.target.value)}
// password value={this.state.seedPhrase}
h('input.large-input.letter-spacey', { placeholder={this.context.t('separateEachWord')}
type: 'password', />
id: 'password-box', </div>
placeholder: this.props.t('newPassword8Chars'), <span className="error">
dataset: { { seedPhraseError }
persistentFormId: 'password', </span>
}, <TextField
style: { id="password"
width: 260, label={t('newPassword')}
marginTop: 12, type="password"
}, className="first-time-flow__input"
}), value={this.state.password}
onChange={event => this.handlePasswordChange(event.target.value)}
// confirm password error={passwordError}
h('input.large-input.letter-spacey', { autoComplete="new-password"
type: 'password', margin="normal"
id: 'password-box-confirm', largeLabel
placeholder: this.props.t('confirmPassword'), />
onKeyPress: this.createOnEnter.bind(this), <TextField
dataset: { id="confirm-password"
persistentFormId: 'password-confirmation', label={t('confirmPassword')}
}, type="password"
style: { className="first-time-flow__input"
width: 260, value={this.state.confirmPassword}
marginTop: 16, onChange={event => this.handleConfirmPasswordChange(event.target.value)}
}, error={confirmPasswordError}
}), autoComplete="confirm-password"
margin="normal"
error && ( largeLabel
h('span.error.in-progress-notification', error) />
), <button
className="first-time-flow__button"
// submit onClick={() => !disabled && this.onClick()}
h('.flex-row.flex-space-between', { disabled={disabled}
style: { >
marginTop: 30, {this.context.t('restore')}
width: '50%', </button>
}, </div>
}, [ </div>
</div>
// cancel
h('button.primary', {
onClick: () => this.cancel(),
}, this.props.t('cancel')),
// submit
h('button.primary', {
onClick: this.createNewVaultAndRestore.bind(this),
}, this.props.t('ok')),
]),
])
) )
} }
} }
RestoreVaultPage.propTypes = { RestoreVaultPage.contextTypes = {
history: PropTypes.object, t: PropTypes.func,
}
const mapStateToProps = state => {
const { appState: { warning, forgottenPassword } } = state
return {
warning,
forgottenPassword,
}
} }
const mapDispatchToProps = dispatch => { export default connect(
return { ({ appState: { warning, isLoading } }) => ({ warning, isLoading }),
createNewVaultAndRestore: (password, seed) => { dispatch => ({
return dispatch(createNewVaultAndRestore(password, seed)) leaveImportSeedScreenState: () => {
dispatch(unMarkPasswordForgotten())
}, },
unMarkPasswordForgotten: () => dispatch(unMarkPasswordForgotten()), createNewVaultAndRestore: (pw, seed) => dispatch(createNewVaultAndRestore(pw, seed)),
} })
}
module.exports = compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(RestoreVaultPage) )(RestoreVaultPage)

@ -332,3 +332,12 @@ $wallet-view-bg: $alabaster;
align-items: center; align-items: center;
flex: 1 0 auto; flex: 1 0 auto;
} }
.first-view-main-wrapper {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
padding: 0 10px;
background: white;
}

Loading…
Cancel
Save