diff options
Diffstat (limited to 'mascara')
-rw-r--r-- | mascara/src/app/first-time/confirm-seed-screen.js | 133 | ||||
-rw-r--r-- | mascara/src/app/first-time/create-password-screen.js | 63 | ||||
-rw-r--r-- | mascara/src/app/first-time/notice-screen.js | 87 | ||||
-rw-r--r-- | mascara/src/app/first-time/seed-screen.js (renamed from mascara/src/app/first-time/backup-phrase-screen.js) | 168 |
4 files changed, 258 insertions, 193 deletions
diff --git a/mascara/src/app/first-time/confirm-seed-screen.js b/mascara/src/app/first-time/confirm-seed-screen.js new file mode 100644 index 000000000..c9382689e --- /dev/null +++ b/mascara/src/app/first-time/confirm-seed-screen.js @@ -0,0 +1,133 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import classnames from 'classnames' +import shuffle from 'lodash.shuffle' +import { compose, onlyUpdateForPropTypes } from 'recompose' +import Identicon from '../../../../ui/app/components/identicon' +import { confirmSeedWords } from '../../../../ui/app/actions' +import Breadcrumbs from './breadcrumbs' +import LoadingScreen from './loading-screen' +import { DEFAULT_ROUTE } from '../../../../ui/app/routes' + +class ConfirmSeedScreen extends Component { + static propTypes = { + isLoading: PropTypes.bool, + address: PropTypes.string, + seedWords: PropTypes.string, + confirmSeedWords: PropTypes.func, + history: PropTypes.object, + }; + + static defaultProps = { + seedWords: '', + } + + constructor (props) { + super(props) + const { seedWords } = props + this.state = { + selectedSeeds: [], + shuffledSeeds: seedWords && shuffle(seedWords.split(' ')) || [], + } + } + + componentWillMount () { + const { seedWords, history } = this.props + if (!seedWords) { + history.push(DEFAULT_ROUTE) + } + } + + render () { + const { seedWords, confirmSeedWords, history } = this.props + const { selectedSeeds, shuffledSeeds } = this.state + const isValid = seedWords === selectedSeeds.map(([_, seed]) => seed).join(' ') + + return ( + <div className="first-time-flow"> + { + this.props.isLoading + ? <LoadingScreen loadingMessage="Creating your new account" /> + : ( + <div className="backup-phrase"> + <Identicon address={this.props.address} diameter={70} /> + <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"> + {shuffledSeeds.map((word, i) => { + const isSelected = selectedSeeds + .filter(([index, seed]) => seed === word && index === i) + .length + + 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, [i, word]], + }) + } else { + this.setState({ + selectedSeeds: selectedSeeds + .filter(([index, seed]) => !(seed === word && index === i)), + }) + } + }} + > + {word} + </button> + ) + })} + </div> + <button + className="first-time-flow__button" + onClick={() => isValid && confirmSeedWords().then(() => history.push(DEFAULT_ROUTE))} + disabled={!isValid} + > + Confirm + </button> + </div> + </div> + <Breadcrumbs total={3} currentIndex={1} /> + </div> + ) + } + </div> + ) + } +} + +export default compose( + onlyUpdateForPropTypes, + connect( + ({ metamask: { selectedAddress, seedWords }, appState: { isLoading } }) => ({ + seedWords, + isLoading, + address: selectedAddress, + }), + dispatch => ({ + confirmSeedWords: () => dispatch(confirmSeedWords()), + }) + ) +)(ConfirmSeedScreen) diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js index 21d29d72b..30acdcf65 100644 --- a/mascara/src/app/first-time/create-password-screen.js +++ b/mascara/src/app/first-time/create-password-screen.js @@ -1,23 +1,25 @@ -import EventEmitter from 'events' -import React, {Component, PropTypes} from 'react' -import {connect} from 'react-redux'; -import {createNewVaultAndKeychain} from '../../../../ui/app/actions' +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import {connect} from 'react-redux' +import { createNewVaultAndKeychain } from '../../../../ui/app/actions' import LoadingScreen from './loading-screen' import Breadcrumbs from './breadcrumbs' +import { DEFAULT_ROUTE, IMPORT_ACCOUNT_ROUTE } from '../../../../ui/app/routes' +import EventEmitter from 'events' import Mascot from '../../../../ui/app/components/mascot' class CreatePasswordScreen extends Component { static propTypes = { isLoading: PropTypes.bool.isRequired, createAccount: PropTypes.func.isRequired, - goToImportWithSeedPhrase: PropTypes.func.isRequired, - goToImportAccount: PropTypes.func.isRequired, - next: PropTypes.func.isRequired + history: PropTypes.object.isRequired, + isInitialized: PropTypes.bool, + isUnlocked: PropTypes.bool, } state = { password: '', - confirmPassword: '' + confirmPassword: '', } constructor () { @@ -25,40 +27,47 @@ class CreatePasswordScreen extends Component { this.animationEventEmitter = new EventEmitter() } - isValid() { - const {password, confirmPassword} = this.state; + componentWillMount () { + const { isInitialized, isUnlocked, history } = this.props + if (isInitialized || isUnlocked) { + history.push(DEFAULT_ROUTE) + } + } + + isValid () { + const { password, confirmPassword } = this.state if (!password || !confirmPassword) { - return false; + return false } if (password.length < 8) { - return false; + return false } - return password === confirmPassword; + return password === confirmPassword } createAccount = () => { if (!this.isValid()) { - return; + return } - const {password} = this.state; - const {createAccount, next} = this.props; + const { password } = this.state + const { createAccount, history } = this.props createAccount(password) - .then(next); + .then(() => history.push(DEFAULT_ROUTE)) } - render() { - const { isLoading, goToImportAccount, goToImportWithSeedPhrase } = this.props + render () { + const { isLoading } = this.props return isLoading ? <LoadingScreen loadingMessage="Creating your new account" /> : ( <div> - <h2 className="alpha-warning">Warning This is Experemental software and is a Developer BETA </h2> + <h2 className="alpha-warning">Warning This is Experimental software and is a Developer BETA </h2> <div className="first-view-main"> <div className="mascara-info"> <Mascot @@ -101,7 +110,7 @@ class CreatePasswordScreen extends Component { className="first-time-flow__link create-password__import-link" onClick={e => { e.preventDefault() - goToImportWithSeedPhrase() + history.push(IMPORT_ACCOUNT_ROUTE) }} > Import with seed phrase @@ -126,8 +135,18 @@ class CreatePasswordScreen extends Component { } } +const mapStateToProps = state => { + const { metamask: { isInitialized, isUnlocked }, appState: { isLoading } } = state + + return { + isLoading, + isInitialized, + isUnlocked, + } +} + export default connect( - ({ appState: { isLoading } }) => ({ isLoading }), + mapStateToProps, dispatch => ({ createAccount: password => dispatch(createNewVaultAndKeychain(password)), }) diff --git a/mascara/src/app/first-time/notice-screen.js b/mascara/src/app/first-time/notice-screen.js index d09070a95..be54b9fc0 100644 --- a/mascara/src/app/first-time/notice-screen.js +++ b/mascara/src/app/first-time/notice-screen.js @@ -1,10 +1,12 @@ -import React, {Component, PropTypes} from 'react' +import React, { Component } from 'react' +import PropTypes from 'prop-types' import Markdown from 'react-markdown' -import {connect} from 'react-redux' +import { connect } from 'react-redux' import debounce from 'lodash.debounce' -import {markNoticeRead} from '../../../../ui/app/actions' +import { markNoticeRead } from '../../../../ui/app/actions' import Identicon from '../../../../ui/app/components/identicon' import Breadcrumbs from './breadcrumbs' +import { DEFAULT_ROUTE } from '../../../../ui/app/routes' class NoticeScreen extends Component { static propTypes = { @@ -12,70 +14,77 @@ class NoticeScreen extends Component { lastUnreadNotice: PropTypes.shape({ title: PropTypes.string, date: PropTypes.string, - body: PropTypes.string + body: PropTypes.string, }), - next: PropTypes.func.isRequired + location: PropTypes.shape({ + state: PropTypes.shape({ + next: PropTypes.func.isRequired, + }), + }), + markNoticeRead: PropTypes.func, + history: PropTypes.object, }; static defaultProps = { - lastUnreadNotice: {} + lastUnreadNotice: {}, }; state = { atBottom: false, } - componentDidMount() { + componentDidMount () { this.onScroll() } acceptTerms = () => { - const { markNoticeRead, lastUnreadNotice, next } = this.props; - const defer = markNoticeRead(lastUnreadNotice) - .then(() => this.setState({ atBottom: false })) - - if ((/terms/gi).test(lastUnreadNotice.title)) { - defer.then(next) - } + const { markNoticeRead, lastUnreadNotice, history } = this.props + markNoticeRead(lastUnreadNotice) + .then(() => { + history.push(DEFAULT_ROUTE) + this.setState({ atBottom: false }) + }) } onScroll = debounce(() => { if (this.state.atBottom) return const target = document.querySelector('.tou__body') - const {scrollTop, offsetHeight, scrollHeight} = target; - const atBottom = scrollTop + offsetHeight >= scrollHeight; + const {scrollTop, offsetHeight, scrollHeight} = target + const atBottom = scrollTop + offsetHeight >= scrollHeight this.setState({atBottom: atBottom}) }, 25) - render() { + render () { const { address, - lastUnreadNotice: { title, body } - } = this.props; + lastUnreadNotice: { title, body }, + } = this.props const { atBottom } = this.state return ( - <div - className="tou" - onScroll={this.onScroll} - > - <Identicon address={address} diameter={70} /> - <div className="tou__title">{title}</div> - <Markdown - className="tou__body markdown" - source={body} - skipHtml - /> - <button - className="first-time-flow__button" - onClick={atBottom && this.acceptTerms} - disabled={!atBottom} + <div className="first-time-flow"> + <div + className="tou" + onScroll={this.onScroll} > - Accept - </button> - <Breadcrumbs total={3} currentIndex={2} /> + <Identicon address={address} diameter={70} /> + <div className="tou__title">{title}</div> + <Markdown + className="tou__body markdown" + source={body} + skipHtml + /> + <button + className="first-time-flow__button" + onClick={atBottom && this.acceptTerms} + disabled={!atBottom} + > + Accept + </button> + <Breadcrumbs total={3} currentIndex={2} /> + </div> </div> ) } @@ -84,9 +93,9 @@ class NoticeScreen extends Component { export default connect( ({ metamask: { selectedAddress, lastUnreadNotice } }) => ({ lastUnreadNotice, - address: selectedAddress + address: selectedAddress, }), dispatch => ({ - markNoticeRead: notice => dispatch(markNoticeRead(notice)) + markNoticeRead: notice => dispatch(markNoticeRead(notice)), }) )(NoticeScreen) diff --git a/mascara/src/app/first-time/backup-phrase-screen.js b/mascara/src/app/first-time/seed-screen.js index c68dacea2..dc1da851c 100644 --- a/mascara/src/app/first-time/backup-phrase-screen.js +++ b/mascara/src/app/first-time/seed-screen.js @@ -1,12 +1,12 @@ -import React, {Component, PropTypes} from 'react' -import {connect} from 'react-redux'; +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' import classnames from 'classnames' -import shuffle from 'lodash.shuffle' -import {compose, onlyUpdateForPropTypes} from 'recompose' +import { compose, onlyUpdateForPropTypes } from 'recompose' import Identicon from '../../../../ui/app/components/identicon' -import {confirmSeedWords} from '../../../../ui/app/actions' import Breadcrumbs from './breadcrumbs' import LoadingScreen from './loading-screen' +import { DEFAULT_ROUTE, CONFIRM_SEED_ROUTE } from '../../../../ui/app/routes' const LockIcon = props => ( <svg @@ -35,34 +35,31 @@ const LockIcon = props => ( /> </g> </svg> -); +) class BackupPhraseScreen extends Component { static propTypes = { isLoading: PropTypes.bool.isRequired, address: PropTypes.string.isRequired, - seedWords: PropTypes.string.isRequired, - next: PropTypes.func.isRequired, - confirmSeedWords: PropTypes.func.isRequired, + seedWords: PropTypes.string, + history: PropTypes.object, }; static defaultProps = { - seedWords: '' - }; - - static PAGE = { - SECRET: 'secret', - CONFIRM: 'confirm' - }; + seedWords: '', + } - constructor(props) { - const {seedWords} = props + constructor (props) { super(props) this.state = { isShowingSecret: false, - page: BackupPhraseScreen.PAGE.SECRET, - selectedSeeds: [], - shuffledSeeds: seedWords && shuffle(seedWords.split(' ')), + } + } + + componentWillMount () { + const { seedWords, history } = this.props + if (!seedWords) { + history.push(DEFAULT_ROUTE) } } @@ -72,7 +69,7 @@ class BackupPhraseScreen extends Component { return ( <div className="backup-phrase__secret"> <div className={classnames('backup-phrase__secret-words', { - 'backup-phrase__secret-words--hidden': !isShowingSecret + 'backup-phrase__secret-words--hidden': !isShowingSecret, })}> {this.props.seedWords} </div> @@ -88,11 +85,12 @@ class BackupPhraseScreen extends Component { </div> )} </div> - ); + ) } - renderSecretScreen() { + renderSecretScreen () { const { isShowingSecret } = this.state + const { history } = this.props return ( <div className="backup-phrase__content-wrapper"> @@ -107,10 +105,7 @@ class BackupPhraseScreen extends Component { {this.renderSecretWordsContainer()} <button className="first-time-flow__button" - onClick={() => isShowingSecret && this.setState({ - isShowingSecret: false, - page: BackupPhraseScreen.PAGE.CONFIRM - })} + onClick={() => isShowingSecret && history.push(CONFIRM_SEED_ROUTE)} disabled={!isShowingSecret} > Next @@ -133,109 +128,21 @@ class BackupPhraseScreen extends Component { ) } - renderConfirmationScreen() { - const { seedWords, confirmSeedWords, next } = this.props; - const { selectedSeeds, shuffledSeeds } = this.state; - const isValid = seedWords === selectedSeeds.map(([_, seed]) => seed).join(' ') - + render () { 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"> - {shuffledSeeds.map((word, i) => { - const isSelected = selectedSeeds - .filter(([index, seed]) => seed === word && index === i) - .length - - 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, [i, word]] - }) - } else { - this.setState({ - selectedSeeds: selectedSeeds - .filter(([index, seed]) => !(seed === word && index === i)) - }) - } - }} - > - {word} - </button> - ) - })} - </div> - <button - className="first-time-flow__button" - onClick={() => isValid && confirmSeedWords().then(next)} - disabled={!isValid} - > - Confirm - </button> - </div> + <div className="first-time-flow"> + { + this.props.isLoading + ? <LoadingScreen loadingMessage="Creating your new account" /> + : ( + <div className="backup-phrase"> + <Identicon address={this.props.address} diameter={70} /> + {this.renderSecretScreen()} + </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 this.props.isLoading - ? <LoadingScreen loadingMessage="Creating your new account" /> - : ( - <div className="backup-phrase"> - {this.renderBack()} - <Identicon address={this.props.address} diameter={70} /> - {this.renderContent()} - </div> - ) + ) } } @@ -246,9 +153,6 @@ export default compose( seedWords, isLoading, address: selectedAddress, - }), - dispatch => ({ - confirmSeedWords: () => dispatch(confirmSeedWords()), }) ) )(BackupPhraseScreen) |