diff options
Update import from seed screen on new ui.
-rw-r--r-- | app/_locales/en/messages.json | 9 | ||||
-rw-r--r-- | test/e2e/beta/metamask-beta-ui.spec.js | 9 | ||||
-rw-r--r-- | ui/app/app.js | 2 | ||||
-rw-r--r-- | ui/app/components/pages/keychains/restore-vault-new.js | 189 | ||||
-rw-r--r-- | ui/app/css/itcss/components/newui-sections.scss | 9 |
5 files changed, 214 insertions, 4 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 46fbdc1a7..621775592 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -664,6 +664,9 @@ "restoreVault": { "message": "Restore Vault" }, + "restoreAccountWithSeed": { + "message": "Restore your Account with Seed Phrase" + }, "required": { "message": "Required" }, @@ -673,6 +676,9 @@ "walletSeed": { "message": "Wallet Seed" }, + "restore": { + "message": "Restore" + }, "revealSeedWords": { "message": "Reveal Seed Words" }, @@ -777,6 +783,9 @@ "sendTokens": { "message": "Send Tokens" }, + "separateEachWord": { + "message": "Separate each word with a single space" + }, "onlySendToEtherAddress": { "message": "Only send ETH to an Ethereum address." }, diff --git a/test/e2e/beta/metamask-beta-ui.spec.js b/test/e2e/beta/metamask-beta-ui.spec.js index 5f270b52b..b07b1ecd7 100644 --- a/test/e2e/beta/metamask-beta-ui.spec.js +++ b/test/e2e/beta/metamask-beta-ui.spec.js @@ -355,9 +355,12 @@ describe('MetaMask', function () { await seedTextArea.sendKeys(testSeedPhrase) await delay(regularDelayMs) - await driver.findElement(By.id('password-box')).sendKeys('correct horse battery staple') - await driver.findElement(By.id('password-box-confirm')).sendKeys('correct horse battery staple') - await driver.findElement(By.css('button:nth-child(2)')).click() + const passwordInputs = await driver.findElements(By.css('input')) + await delay(regularDelayMs) + + 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) }) diff --git a/ui/app/app.js b/ui/app/app.js index d0e48a368..3ca18d4ca 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -23,7 +23,7 @@ const Authenticated = require('./components/pages/authenticated') const Initialized = require('./components/pages/initialized') const Settings = require('./components/pages/settings') const UnlockPage = require('./components/pages/unlock-page') -const RestoreVaultPage = require('./components/pages/keychains/restore-vault') +const RestoreVaultPage = require('./components/pages/keychains/restore-vault-new').default const RevealSeedConfirmation = require('./components/pages/keychains/reveal-seed') const AddTokenPage = require('./components/pages/add-token') const ConfirmAddTokenPage = require('./components/pages/confirm-add-token') diff --git a/ui/app/components/pages/keychains/restore-vault-new.js b/ui/app/components/pages/keychains/restore-vault-new.js new file mode 100644 index 000000000..ef38faf1e --- /dev/null +++ b/ui/app/components/pages/keychains/restore-vault-new.js @@ -0,0 +1,189 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import {connect} from 'react-redux' +import { + createNewVaultAndRestore, + unMarkPasswordForgotten, +} from '../../../actions' +import { DEFAULT_ROUTE } from '../../../routes' +import TextField from '../../text-field' + +class RestoreVaultPageNew extends Component { + static contextTypes = { + t: PropTypes.func, + } + + static propTypes = { + warning: PropTypes.string, + createNewVaultAndRestore: PropTypes.func.isRequired, + leaveImportSeedScreenState: PropTypes.func, + history: PropTypes.object, + isLoading: PropTypes.bool, + }; + + state = { + seedPhrase: '', + password: '', + confirmPassword: '', + seedPhraseError: null, + passwordError: null, + confirmPasswordError: null, + } + + parseSeedPhrase = (seedPhrase) => { + return seedPhrase + .match(/\w+/g) + .join(' ') + } + + handleSeedPhraseChange (seedPhrase) { + let seedPhraseError = null + + if (seedPhrase && this.parseSeedPhrase(seedPhrase).split(' ').length !== 12) { + seedPhraseError = this.context.t('seedPhraseReq') + } + + this.setState({ seedPhrase, seedPhraseError }) + } + + handlePasswordChange (password) { + const { confirmPassword } = this.state + let confirmPasswordError = null + let passwordError = null + + if (password && password.length < 8) { + passwordError = this.context.t('passwordNotLongEnough') + } + + if (confirmPassword && password !== confirmPassword) { + confirmPasswordError = this.context.t('passwordsDontMatch') + } + + this.setState({ password, passwordError, confirmPasswordError }) + } + + handleConfirmPasswordChange (confirmPassword) { + const { password } = this.state + let confirmPasswordError = null + + if (password !== confirmPassword) { + confirmPasswordError = this.context.t('passwordsDontMatch') + } + + this.setState({ confirmPassword, confirmPasswordError }) + } + + onClick = () => { + const { password, seedPhrase } = this.state + 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 () { + const { + 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 ( + <div className="first-view-main-wrapper"> + <div className="first-view-main"> + <div className="import-account"> + <a + className="import-account__back-button" + onClick={e => { + e.preventDefault() + this.props.history.goBack() + }} + href="#" + > + {`< Back`} + </a> + <div className="import-account__title"> + { this.context.t('restoreAccountWithSeed') } + </div> + <div className="import-account__selector-label"> + { this.context.t('secretPhrase') } + </div> + <div className="import-account__input-wrapper"> + <label className="import-account__input-label">Wallet Seed</label> + <textarea + className="import-account__secret-phrase" + onChange={e => this.handleSeedPhraseChange(e.target.value)} + value={this.state.seedPhrase} + placeholder={this.context.t('separateEachWord')} + /> + </div> + <span className="error"> + { seedPhraseError } + </span> + <TextField + id="password" + label={t('newPassword')} + type="password" + className="first-time-flow__input" + value={this.state.password} + onChange={event => this.handlePasswordChange(event.target.value)} + error={passwordError} + autoComplete="new-password" + margin="normal" + largeLabel + /> + <TextField + id="confirm-password" + label={t('confirmPassword')} + type="password" + className="first-time-flow__input" + value={this.state.confirmPassword} + onChange={event => this.handleConfirmPasswordChange(event.target.value)} + error={confirmPasswordError} + autoComplete="confirm-password" + margin="normal" + largeLabel + /> + <button + className="first-time-flow__button" + onClick={() => !disabled && this.onClick()} + disabled={disabled} + > + {this.context.t('restore')} + </button> + </div> + </div> + </div> + ) + } +} + +RestoreVaultPageNew.contextTypes = { + t: PropTypes.func, +} + +export default connect( + ({ appState: { warning, isLoading } }) => ({ warning, isLoading }), + dispatch => ({ + leaveImportSeedScreenState: () => { + dispatch(unMarkPasswordForgotten()) + }, + createNewVaultAndRestore: (pw, seed) => dispatch(createNewVaultAndRestore(pw, seed)), + }) +)(RestoreVaultPageNew) diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss index 667e45ba2..bbfd85c90 100644 --- a/ui/app/css/itcss/components/newui-sections.scss +++ b/ui/app/css/itcss/components/newui-sections.scss @@ -332,3 +332,12 @@ $wallet-view-bg: $alabaster; align-items: center; flex: 1 0 auto; } + +.first-view-main-wrapper { + display: flex; + width: 100%; + height: 100%; + justify-content: center; + padding: 0 10px; + background: white; +} |