mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Add BackupPhraseScreen
This commit is contained in:
parent
fd4fbdc0cd
commit
1a9b217558
232
mascara/src/app/first-time/backup-phrase-screen.js
Normal file
232
mascara/src/app/first-time/backup-phrase-screen.js
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import React, {Component, PropTypes} from 'react'
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import Identicon from '../../../../ui/app/components/identicon'
|
||||||
|
import {confirmSeedWords} from '../../../../ui/app/actions'
|
||||||
|
import Breadcrumbs from './breadcrumbs'
|
||||||
|
|
||||||
|
const LockIcon = props => (
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
id="Capa_1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="401.998px"
|
||||||
|
height="401.998px"
|
||||||
|
viewBox="0 0 401.998 401.998"
|
||||||
|
style={{enableBackground: 'new 0 0 401.998 401.998'}}
|
||||||
|
xmlSpace="preserve"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d="M357.45,190.721c-5.331-5.33-11.8-7.993-19.417-7.993h-9.131v-54.821c0-35.022-12.559-65.093-37.685-90.218
|
||||||
|
C266.093,12.563,236.025,0,200.998,0c-35.026,0-65.1,12.563-90.222,37.688C85.65,62.814,73.091,92.884,73.091,127.907v54.821
|
||||||
|
h-9.135c-7.611,0-14.084,2.663-19.414,7.993c-5.33,5.326-7.994,11.799-7.994,19.417V374.59c0,7.611,2.665,14.086,7.994,19.417
|
||||||
|
c5.33,5.325,11.803,7.991,19.414,7.991H338.04c7.617,0,14.085-2.663,19.417-7.991c5.325-5.331,7.994-11.806,7.994-19.417V210.135
|
||||||
|
C365.455,202.523,362.782,196.051,357.45,190.721z M274.087,182.728H127.909v-54.821c0-20.175,7.139-37.402,21.414-51.675
|
||||||
|
c14.277-14.275,31.501-21.411,51.678-21.411c20.179,0,37.399,7.135,51.677,21.411c14.271,14.272,21.409,31.5,21.409,51.675V182.728
|
||||||
|
z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
class BackupPhraseScreen extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
address: PropTypes.string.isRequired,
|
||||||
|
seedWords: PropTypes.string.isRequired,
|
||||||
|
next: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
seedWords: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
static PAGE = {
|
||||||
|
SECRET: 'secret',
|
||||||
|
CONFIRM: 'confirm'
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
isShowingSecret: false,
|
||||||
|
page: BackupPhraseScreen.PAGE.SECRET,
|
||||||
|
selectedSeeds: []
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSecretWordsContainer() {
|
||||||
|
const { isShowingSecret } = this.state;
|
||||||
|
return (
|
||||||
|
<div className="backup-phrase__secret">
|
||||||
|
<div className={classnames('backup-phrase__secret-words', {
|
||||||
|
'backup-phrase__secret-words--hidden': !isShowingSecret
|
||||||
|
})}>
|
||||||
|
{this.props.seedWords}
|
||||||
|
</div>
|
||||||
|
{!isShowingSecret && (
|
||||||
|
<div className="backup-phrase__secret-blocker">
|
||||||
|
<LockIcon width="28px" height="35px" fill="#FFFFFF" />
|
||||||
|
<button
|
||||||
|
className="backup-phrase__reveal-button"
|
||||||
|
onClick={() => this.setState({ isShowingSecret: true })}
|
||||||
|
>
|
||||||
|
Click here to reveal secret words
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSecretScreen() {
|
||||||
|
const { isShowingSecret } = this.state
|
||||||
|
return (
|
||||||
|
<div className="backup-phrase__content-wrapper">
|
||||||
|
<div>
|
||||||
|
<div className="backup-phrase__title">Secret Backup Phrase</div>
|
||||||
|
<div className="backup-phrase__body-text">
|
||||||
|
Your secret backup phrase makes it easy to back up and restore your account.
|
||||||
|
</div>
|
||||||
|
<div className="backup-phrase__body-text">
|
||||||
|
WARNING: Never disclose your backup phrase. Anyone with this phrase can take your Ether forever.
|
||||||
|
</div>
|
||||||
|
{this.renderSecretWordsContainer()}
|
||||||
|
<button
|
||||||
|
className="first-time-flow__button"
|
||||||
|
onClick={() => isShowingSecret && this.setState({
|
||||||
|
isShowingSecret: false,
|
||||||
|
page: BackupPhraseScreen.PAGE.CONFIRM
|
||||||
|
})}
|
||||||
|
disabled={!isShowingSecret}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
|
<Breadcrumbs total={3} currentIndex={1} />
|
||||||
|
</div>
|
||||||
|
<div className="backup-phrase__tips">
|
||||||
|
<div className="backup-phrase__tips-text">Tips:</div>
|
||||||
|
<div className="backup-phrase__tips-text">
|
||||||
|
Store this phrase in a password manager like 1password.
|
||||||
|
</div>
|
||||||
|
<div className="backup-phrase__tips-text">
|
||||||
|
Write this phrase on a piece of paper and store in a secure location. If you want even more security, write it down on multiple pieces of paper and store each in 2 - 3 different locations.
|
||||||
|
</div>
|
||||||
|
<div className="backup-phrase__tips-text">
|
||||||
|
Memorize this phrase.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderConfirmationScreen() {
|
||||||
|
const { seedWords, confirmSeedWords, next } = this.props;
|
||||||
|
const { selectedSeeds } = this.state;
|
||||||
|
const isValid = seedWords === selectedSeeds.join(' ')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="backup-phrase__content-wrapper">
|
||||||
|
<div>
|
||||||
|
<div className="backup-phrase__title">Confirm your Secret Backup Phrase</div>
|
||||||
|
<div className="backup-phrase__body-text">
|
||||||
|
Please select each phrase in order to make sure it is correct.
|
||||||
|
</div>
|
||||||
|
<div className="backup-phrase__confirm-secret">
|
||||||
|
{selectedSeeds.map((word, i) => (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
className="backup-phrase__confirm-seed-option"
|
||||||
|
>
|
||||||
|
{word}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="backup-phrase__confirm-seed-options">
|
||||||
|
{seedWords.split(' ').map((word, i) => {
|
||||||
|
const isSelected = selectedSeeds.includes(word)
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
className={classnames('backup-phrase__confirm-seed-option', {
|
||||||
|
'backup-phrase__confirm-seed-option--selected': isSelected
|
||||||
|
})}
|
||||||
|
onClick={() => {
|
||||||
|
if (!isSelected) {
|
||||||
|
this.setState({
|
||||||
|
selectedSeeds: [...selectedSeeds, word]
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
selectedSeeds: selectedSeeds.filter(seed => seed !== word)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{word}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="first-time-flow__button"
|
||||||
|
onClick={() => isValid && confirmSeedWords().then(next)}
|
||||||
|
disabled={!isValid}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBack() {
|
||||||
|
return this.state.page === BackupPhraseScreen.PAGE.CONFIRM
|
||||||
|
? (
|
||||||
|
<a
|
||||||
|
className="backup-phrase__back-button"
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault()
|
||||||
|
this.setState({
|
||||||
|
page: BackupPhraseScreen.PAGE.SECRET
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
href="#"
|
||||||
|
>
|
||||||
|
{`< Back`}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent() {
|
||||||
|
switch(this.state.page) {
|
||||||
|
case BackupPhraseScreen.PAGE.CONFIRM:
|
||||||
|
return this.renderConfirmationScreen();
|
||||||
|
case BackupPhraseScreen.PAGE.SECRET:
|
||||||
|
default:
|
||||||
|
return this.renderSecretScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="backup-phrase">
|
||||||
|
{this.renderBack()}
|
||||||
|
<Identicon address={this.props.address} diameter={70} />
|
||||||
|
{this.renderContent()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
({ metamask: { selectedAddress, seedWords } }) => ({
|
||||||
|
seedWords,
|
||||||
|
address: selectedAddress
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
confirmSeedWords: () => dispatch(confirmSeedWords())
|
||||||
|
})
|
||||||
|
)(BackupPhraseScreen)
|
@ -8,7 +8,8 @@ $primary
|
|||||||
|
|
||||||
.create-password,
|
.create-password,
|
||||||
.unique-image,
|
.unique-image,
|
||||||
.tou {
|
.tou,
|
||||||
|
.backup-phrase {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
margin: 67px 0 0 146px;
|
margin: 67px 0 0 146px;
|
||||||
@ -19,9 +20,14 @@ $primary
|
|||||||
max-width: 46rem;
|
max-width: 46rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backup-phrase {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.create-password__title,
|
.create-password__title,
|
||||||
.unique-image__title,
|
.unique-image__title,
|
||||||
.tou__title {
|
.tou__title,
|
||||||
|
.backup-phrase__title {
|
||||||
width: 280px;
|
width: 280px;
|
||||||
color: #1B344D;
|
color: #1B344D;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
@ -30,6 +36,11 @@ $primary
|
|||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tou__title,
|
||||||
|
.backup-phrase__title {
|
||||||
|
width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
.create-password__confirm-input {
|
.create-password__confirm-input {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
@ -39,20 +50,29 @@ $primary
|
|||||||
}
|
}
|
||||||
|
|
||||||
.unique-image__title,
|
.unique-image__title,
|
||||||
.tou__title {
|
.tou__title,
|
||||||
|
.backup-phrase__title {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unique-image__body-text {
|
.unique-image__body-text,
|
||||||
width: 335px;
|
.backup-phrase__body-text {
|
||||||
color: #1B344D;
|
color: #1B344D;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 23px;
|
line-height: 23px;
|
||||||
font-family: Montserrat UltraLight;
|
font-family: Montserrat UltraLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unique-image__body-text +
|
|
||||||
.unique-image__body-text {
|
.unique-image__body-text {
|
||||||
|
width: 335px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unique-image__body-text +
|
||||||
|
.unique-image__body-text,
|
||||||
|
.backup-phrase__body-text +
|
||||||
|
.backup-phrase__body-text,
|
||||||
|
.backup-phrase__tips-text +
|
||||||
|
.backup-phrase__tips-text {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +91,134 @@ $primary
|
|||||||
padding: 22px 30px;
|
padding: 22px 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.backup-phrase__content-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex: row nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__body-text {
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__tips {
|
||||||
|
margin: 40px 85px;
|
||||||
|
width: 285px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__tips-text {
|
||||||
|
color: #5B5D67;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 23px;
|
||||||
|
font-family: Montserrat UltraLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__secret {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 349px;
|
||||||
|
border: 1px solid #CDCDCD;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
padding: 20px 0;
|
||||||
|
margin-top: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__secret-words {
|
||||||
|
width: 310px;
|
||||||
|
color: #5B5D67;
|
||||||
|
font-family: Montserrat Light;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 26px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__secret-words--hidden {
|
||||||
|
filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__secret-blocker {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: rgba(0,0,0,0.6);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
align-items: center;
|
||||||
|
padding: 13px 0 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__reveal-button {
|
||||||
|
border: 1px solid #979797;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-family: Montserrat Regular;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 15px;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__back-button,
|
||||||
|
.backup-phrase__back-button:hover {
|
||||||
|
position: absolute;
|
||||||
|
top: 24px;
|
||||||
|
color: #22232C;
|
||||||
|
font-family: Montserrat Regular;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.backup-phrase__reveal-button:hover {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__confirm-secret {
|
||||||
|
height: 190px;
|
||||||
|
width: 495px;
|
||||||
|
border: 1px solid #CDCDCD;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
margin: 25px 0 36px;
|
||||||
|
padding: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__confirm-seed-options {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
width: 465px;
|
||||||
|
position: relative;
|
||||||
|
left: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__confirm-seed-option {
|
||||||
|
color: #5B5D67;
|
||||||
|
font-family: Montserrat Light;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 21px;
|
||||||
|
background-color: #E7E7E7;
|
||||||
|
padding: 8px 19px;
|
||||||
|
box-shadow: none;
|
||||||
|
min-width: 65px;
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backup-phrase__confirm-seed-option--selected {
|
||||||
|
background-color: #85D1CC;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.backup-phrase__confirm-seed-option:hover {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
.first-time-flow__input {
|
.first-time-flow__input {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
@ -3,6 +3,7 @@ import {connect} from 'react-redux';
|
|||||||
import CreatePasswordScreen from './create-password-screen'
|
import CreatePasswordScreen from './create-password-screen'
|
||||||
import UniqueImageScreen from './unique-image-screen'
|
import UniqueImageScreen from './unique-image-screen'
|
||||||
import NoticeScreen from './notice-screen'
|
import NoticeScreen from './notice-screen'
|
||||||
|
import BackupPhraseScreen from './backup-phrase-screen'
|
||||||
|
|
||||||
class FirstTimeFlow extends Component {
|
class FirstTimeFlow extends Component {
|
||||||
|
|
||||||
@ -79,6 +80,12 @@ class FirstTimeFlow extends Component {
|
|||||||
next={() => this.setScreenType(SCREEN_TYPE.BACK_UP_PHRASE)}
|
next={() => this.setScreenType(SCREEN_TYPE.BACK_UP_PHRASE)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
case SCREEN_TYPE.BACK_UP_PHRASE:
|
||||||
|
return (
|
||||||
|
<BackupPhraseScreen
|
||||||
|
next={() => this.setScreenType(SCREEN_TYPE.BUY_ETHER)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return <noscript />
|
return <noscript />
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import Breadcrumbs from './breadcrumbs'
|
|||||||
|
|
||||||
class UniqueImageScreen extends Component {
|
class UniqueImageScreen extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
address: PropTypes.string.isRequired,
|
address: PropTypes.string,
|
||||||
next: PropTypes.func.isRequired
|
next: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
"bluebird": "^3.5.0",
|
"bluebird": "^3.5.0",
|
||||||
"bn.js": "^4.11.7",
|
"bn.js": "^4.11.7",
|
||||||
"browserify-derequire": "^0.9.4",
|
"browserify-derequire": "^0.9.4",
|
||||||
|
"classnames": "^2.2.5",
|
||||||
"client-sw-ready-event": "^3.3.0",
|
"client-sw-ready-event": "^3.3.0",
|
||||||
"clone": "^2.1.1",
|
"clone": "^2.1.1",
|
||||||
"copy-to-clipboard": "^3.0.8",
|
"copy-to-clipboard": "^3.0.8",
|
||||||
|
@ -215,14 +215,18 @@ function confirmSeedWords () {
|
|||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(actions.showLoadingIndication())
|
dispatch(actions.showLoadingIndication())
|
||||||
log.debug(`background.clearSeedWordCache`)
|
log.debug(`background.clearSeedWordCache`)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
background.clearSeedWordCache((err, account) => {
|
background.clearSeedWordCache((err, account) => {
|
||||||
dispatch(actions.hideLoadingIndication())
|
dispatch(actions.hideLoadingIndication())
|
||||||
if (err) {
|
if (err) {
|
||||||
return dispatch(actions.displayWarning(err.message))
|
dispatch(actions.displayWarning(err.message))
|
||||||
|
reject(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('Seed word cache cleared. ' + account)
|
log.info('Seed word cache cleared. ' + account)
|
||||||
dispatch(actions.showAccountDetail(account))
|
dispatch(actions.showAccountDetail(account))
|
||||||
|
resolve(account)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user