From dde39e82b5723ba8056b73f0f823d40c3e702a99 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 4 Dec 2017 01:24:30 -0500 Subject: [PATCH] Add routes for mascara --- .../app/first-time/backup-phrase-screen.js | 79 ++++++---- .../app/first-time/create-password-screen.js | 144 +++++++++--------- mascara/src/app/first-time/index.css | 5 +- mascara/src/app/first-time/notice-screen.js | 87 ++++++----- ui/app/app.js | 48 ++++-- ui/app/components/account-menu/index.js | 22 +-- ui/app/components/pages/authenticated.js | 16 +- .../components/pages/keychains/reveal-seed.js | 5 +- ui/app/components/pages/metamask-route.js | 28 ++++ ui/app/components/pages/notice.js | 16 -- .../components/pages/unauthenticated/index.js | 38 +++++ ui/app/first-time/init-menu.js | 2 +- ui/app/routes.js | 4 +- 13 files changed, 296 insertions(+), 198 deletions(-) create mode 100644 ui/app/components/pages/metamask-route.js create mode 100644 ui/app/components/pages/unauthenticated/index.js diff --git a/mascara/src/app/first-time/backup-phrase-screen.js b/mascara/src/app/first-time/backup-phrase-screen.js index c68dacea2..266a82cdb 100644 --- a/mascara/src/app/first-time/backup-phrase-screen.js +++ b/mascara/src/app/first-time/backup-phrase-screen.js @@ -1,5 +1,6 @@ -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' @@ -7,6 +8,7 @@ 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' const LockIcon = props => ( ( /> -); +) class BackupPhraseScreen extends Component { static propTypes = { isLoading: PropTypes.bool.isRequired, address: PropTypes.string.isRequired, - seedWords: PropTypes.string.isRequired, - next: PropTypes.func.isRequired, + seedWords: PropTypes.string, confirmSeedWords: PropTypes.func.isRequired, + history: PropTypes.object, }; static defaultProps = { - seedWords: '' - }; + seedWords: '', + } static PAGE = { SECRET: 'secret', - CONFIRM: 'confirm' - }; + CONFIRM: 'confirm', + } - constructor(props) { + constructor (props) { const {seedWords} = props super(props) this.state = { @@ -66,13 +68,20 @@ class BackupPhraseScreen extends Component { } } + componentWillMount () { + const { seedWords, history } = this.props + if (!seedWords) { + history.push(DEFAULT_ROUTE) + } + } + renderSecretWordsContainer () { const { isShowingSecret } = this.state return (
{this.props.seedWords}
@@ -88,10 +97,10 @@ class BackupPhraseScreen extends Component {
)} - ); + ) } - renderSecretScreen() { + renderSecretScreen () { const { isShowingSecret } = this.state return ( @@ -109,7 +118,7 @@ class BackupPhraseScreen extends Component { className="first-time-flow__button" onClick={() => isShowingSecret && this.setState({ isShowingSecret: false, - page: BackupPhraseScreen.PAGE.CONFIRM + page: BackupPhraseScreen.PAGE.CONFIRM, })} disabled={!isShowingSecret} > @@ -133,9 +142,9 @@ class BackupPhraseScreen extends Component { ) } - renderConfirmationScreen() { - const { seedWords, confirmSeedWords, next } = this.props; - const { selectedSeeds, shuffledSeeds } = this.state; + renderConfirmationScreen () { + const { seedWords, confirmSeedWords, history } = this.props + const { selectedSeeds, shuffledSeeds } = this.state const isValid = seedWords === selectedSeeds.map(([_, seed]) => seed).join(' ') return ( @@ -165,17 +174,17 @@ class BackupPhraseScreen extends Component { - ) + ) } renderBack () { @@ -205,7 +214,7 @@ class BackupPhraseScreen extends Component { onClick={e => { e.preventDefault() this.setState({ - page: BackupPhraseScreen.PAGE.SECRET + page: BackupPhraseScreen.PAGE.SECRET, }) }} href="#" @@ -227,15 +236,21 @@ class BackupPhraseScreen extends Component { } render () { - return this.props.isLoading - ? - : ( -
- {this.renderBack()} - - {this.renderContent()} -
- ) + return ( +
+ { + this.props.isLoading + ? + : ( +
+ {this.renderBack()} + + {this.renderContent()} +
+ ) + } +
+ ) } } diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js index 2f4b81e7c..102d8a7c3 100644 --- a/mascara/src/app/first-time/create-password-screen.js +++ b/mascara/src/app/first-time/create-password-screen.js @@ -1,103 +1,109 @@ -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' 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, } state = { password: '', - confirmPassword: '' + confirmPassword: '', } - isValid() { - const {password, confirmPassword} = this.state; + 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 - ? - : ( -
-
- Create Password -
- this.setState({password: e.target.value})} - /> - this.setState({confirmPassword: e.target.value})} - /> - - { - e.preventDefault() - goToImportWithSeedPhrase() - }} - > - Import with seed phrase - - { /* } - { - e.preventDefault() - goToImportAccount() - }} - > - Import an account - - { */ } - -
- ) + return ( +
+ { + isLoading + ? + : ( +
+
+ Create Password +
+ this.setState({password: e.target.value})} + /> + this.setState({confirmPassword: e.target.value})} + /> + + { + e.preventDefault() + history.push(IMPORT_ACCOUNT_ROUTE) + }} + > + Import with seed phrase + + { /* } + { + e.preventDefault() + goToImportAccount() + }} + > + Import an account + + { */ } + +
+ ) + } +
+ ) } } diff --git a/mascara/src/app/first-time/index.css b/mascara/src/app/first-time/index.css index 28aa3060a..157989d07 100644 --- a/mascara/src/app/first-time/index.css +++ b/mascara/src/app/first-time/index.css @@ -75,7 +75,7 @@ .backup-phrase__tips { margin: 40px 0 !important; - width: initial !important; + width: initial !important; } .backup-phrase__confirm-secret, @@ -337,7 +337,7 @@ button.backup-phrase__confirm-seed-option:hover { padding: 14px 21px; appearance: none; -webkit-appearance: none; - -moz-appearance: none; + -moz-appearance: none; cursor: pointer; } @@ -540,6 +540,7 @@ button.backup-phrase__confirm-seed-option:hover { text-transform: uppercase; margin: 35px 0 14px; transition: 200ms ease-in-out; + background: #f7861c; } button.first-time-flow__button[disabled] { 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 ( -
- -
{title}
- - - + +
{title}
+ + + +
) } @@ -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/ui/app/app.js b/ui/app/app.js index 168ec6559..68384f30d 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -5,8 +5,10 @@ const { compose } = require('recompose') const h = require('react-hyperscript') const actions = require('./actions') // mascara -const MascaraFirstTime = require('../../mascara/src/app/first-time').default +const MascaraCreatePassword = require('../../mascara/src/app/first-time/create-password-screen').default const MascaraBuyEtherScreen = require('../../mascara/src/app/first-time/buy-ether-screen').default +const MascaraNoticeScreen = require('../../mascara/src/app/first-time/notice-screen').default +const MascaraBackupPhraseScreen = require('../../mascara/src/app/first-time/backup-phrase-screen').default // init const InitializeMenuScreen = require('./first-time/init-menu') const NewKeyChainScreen = require('./new-keychain') @@ -22,6 +24,8 @@ const WalletView = require('./components/wallet-view') // other views const Authenticated = require('./components/pages/authenticated') +const Unauthenticated = require('./components/pages/unauthenticated') +const MetamaskRoute = require('./components/pages/metamask-route') const Settings = require('./components/pages/settings') const UnlockPage = require('./components/pages/unauthenticated/unlock') const RestoreVaultPage = require('./components/pages/keychains/restore-vault') @@ -53,7 +57,7 @@ const { IMPORT_ACCOUNT_ROUTE, SEND_ROUTE, CONFIRM_TRANSACTION_ROUTE, - INITIALIZE_MENU_ROUTE, + INITIALIZE_ROUTE, NOTICE_ROUTE, } = require('./routes') @@ -65,7 +69,7 @@ class App extends Component { } componentWillMount () { - const { currentCurrency, setCurrentCurrency } = this.props + const { currentCurrency, setCurrentCurrencyToUSD } = this.props if (!currentCurrency) { setCurrentCurrencyToUSD() @@ -77,14 +81,29 @@ class App extends Component { return ( h(Switch, [ - h(Route, { path: INITIALIZE_MENU_ROUTE, exact, component: InitializeMenuScreen }), - h(Route, { path: UNLOCK_ROUTE, exact, component: UnlockPage }), - h(Route, { path: SETTINGS_ROUTE, component: Settings }), - h(Route, { path: RESTORE_VAULT_ROUTE, exact, component: RestoreVaultPage }), - h(Route, { path: NOTICE_ROUTE, exact, component: NoticeScreen }), + h(MetamaskRoute, { + path: INITIALIZE_ROUTE, + exact, + component: InitializeMenuScreen, + mascaraComponent: MascaraCreatePassword, + }), + h(MetamaskRoute, { + path: REVEAL_SEED_ROUTE, + exact, + component: RevealSeedPage, + mascaraComponent: MascaraBackupPhraseScreen, + }), + h(Unauthenticated, { path: UNLOCK_ROUTE, exact, component: UnlockPage }), + h(Unauthenticated, { path: SETTINGS_ROUTE, component: Settings }), + h(Unauthenticated, { path: RESTORE_VAULT_ROUTE, exact, component: RestoreVaultPage }), + h(Unauthenticated, { + path: NOTICE_ROUTE, + exact, + component: NoticeScreen, + mascaraComponent: MascaraNoticeScreen, + }), h(Authenticated, { path: CONFIRM_TRANSACTION_ROUTE, exact, component: ConfirmTxScreen }), h(Authenticated, { path: SEND_ROUTE, exact, component: SendTransactionScreen2 }), - h(Authenticated, { path: REVEAL_SEED_ROUTE, exact, component: RevealSeedPage }), h(Authenticated, { path: ADD_TOKEN_ROUTE, exact, component: AddTokenPage }), h(Authenticated, { path: IMPORT_ACCOUNT_ROUTE, exact, component: ImportAccountPage }), h(Authenticated, { path: DEFAULT_ROUTE, exact, component: this.renderPrimary }), @@ -327,10 +346,17 @@ class App extends Component { currentView, activeAddress, unapprovedTxs = {}, + seedWords, } = this.props - if (isMascara && isOnboarding) { - return h(MascaraFirstTime) + // seed words + if (seedWords) { + log.debug('rendering seed words') + return h(Redirect, { + to: { + pathname: REVEAL_SEED_ROUTE, + }, + }) } // notices diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js index 0965cba38..0d3b43ae9 100644 --- a/ui/app/components/account-menu/index.js +++ b/ui/app/components/account-menu/index.js @@ -8,7 +8,7 @@ const actions = require('../../actions') const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu') const Identicon = require('../identicon') const { formatBalance } = require('../../util') -const { SETTINGS_ROUTE, INFO_ROUTE, IMPORT_ACCOUNT_ROUTE } = require('../../routes') +const { SETTINGS_ROUTE, INFO_ROUTE, IMPORT_ACCOUNT_ROUTE, DEFAULT_ROUTE } = require('../../routes') module.exports = compose( withRouter, @@ -40,22 +40,10 @@ function mapDispatchToProps (dispatch) { dispatch(actions.displayWarning(null)) dispatch(actions.toggleAccountMenu()) }, - showConfigPage: () => { - dispatch(actions.showConfigPage()) - dispatch(actions.toggleAccountMenu()) - }, showNewAccountModal: () => { dispatch(actions.showModal({ name: 'NEW_ACCOUNT' })) dispatch(actions.toggleAccountMenu()) }, - showImportPage: () => { - dispatch(actions.showImportPage()) - dispatch(actions.toggleAccountMenu()) - }, - showInfoPage: () => { - dispatch(actions.showInfoPage()) - dispatch(actions.toggleAccountMenu()) - }, } } @@ -64,10 +52,7 @@ AccountMenu.prototype.render = function () { isAccountMenuOpen, toggleAccountMenu, showNewAccountModal, - showImportPage, lockMetamask, - showConfigPage, - showInfoPage, history, } = this.props @@ -78,7 +63,10 @@ AccountMenu.prototype.render = function () { }, [ 'My Accounts', h('button.account-menu__logout-button', { - onClick: lockMetamask, + onClick: () => { + lockMetamask() + history.push(DEFAULT_ROUTE) + }, }, 'Log out'), ]), h(Divider), diff --git a/ui/app/components/pages/authenticated.js b/ui/app/components/pages/authenticated.js index 78f3a4225..89bd238d2 100644 --- a/ui/app/components/pages/authenticated.js +++ b/ui/app/components/pages/authenticated.js @@ -1,26 +1,26 @@ const { connect } = require('react-redux') const PropTypes = require('prop-types') -const { Redirect, Route } = require('react-router-dom') +const { Redirect } = require('react-router-dom') const h = require('react-hyperscript') -const { UNLOCK_ROUTE, INITIALIZE_MENU_ROUTE } = require('../../routes') +const MetamaskRoute = require('./metamask-route') +const { UNLOCK_ROUTE, INITIALIZE_ROUTE } = require('../../routes') const Authenticated = ({ component: Component, isUnlocked, isInitialized, ...props }) => { - - const render = props => { + const component = renderProps => { switch (true) { case isUnlocked: - return h(Component, { ...props }) + return h(Component, { ...renderProps }) case !isInitialized: - return h(Redirect, { to: { pathname: INITIALIZE_MENU_ROUTE } }) + return h(Redirect, { to: { pathname: INITIALIZE_ROUTE } }) default: return h(Redirect, { to: { pathname: UNLOCK_ROUTE } }) } } return ( - h(Route, { + h(MetamaskRoute, { ...props, - render, + component, }) ) } 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) { diff --git a/ui/app/components/pages/metamask-route.js b/ui/app/components/pages/metamask-route.js new file mode 100644 index 000000000..23c5b5199 --- /dev/null +++ b/ui/app/components/pages/metamask-route.js @@ -0,0 +1,28 @@ +const { connect } = require('react-redux') +const PropTypes = require('prop-types') +const { Route } = require('react-router-dom') +const h = require('react-hyperscript') + +const MetamaskRoute = ({ component, mascaraComponent, isMascara, ...props }) => { + return ( + h(Route, { + ...props, + component: isMascara && mascaraComponent ? mascaraComponent : component, + }) + ) +} + +MetamaskRoute.propTypes = { + component: PropTypes.func, + mascaraComponent: PropTypes.func, + isMascara: PropTypes.bool, +} + +const mapStateToProps = state => { + const { metamask: { isMascara } } = state + return { + isMascara, + } +} + +module.exports = connect(mapStateToProps)(MetamaskRoute) diff --git a/ui/app/components/pages/notice.js b/ui/app/components/pages/notice.js index 8e68cd52b..2329a9147 100644 --- a/ui/app/components/pages/notice.js +++ b/ui/app/components/pages/notice.js @@ -53,7 +53,6 @@ class Notice extends Component { render () { const { notice = {} } = this.props const { title, date, body } = notice - // const state = this.state || { disclaimerDisabled: true } const { disclaimerDisabled } = this.state return ( @@ -156,21 +155,6 @@ class Notice extends Component { const mapStateToProps = state => { const { metamask } = state const { noActiveNotices, lastUnreadNotice, lostAccounts } = metamask - // if (!props.noActiveNotices) { - // log.debug('rendering notice screen for unread notices.') - // return h(NoticeScreen, { - // notice: props.lastUnreadNotice, - // key: 'NoticeScreen', - // onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)), - // }) - // } else if (props.lostAccounts && props.lostAccounts.length > 0) { - // log.debug('rendering notice screen for lost accounts view.') - // return h(NoticeScreen, { - // notice: generateLostAccountsNotice(props.lostAccounts), - // key: 'LostAccountsNotice', - // onConfirm: () => props.dispatch(actions.markAccountsFound()), - // }) - // } return { noActiveNotices, diff --git a/ui/app/components/pages/unauthenticated/index.js b/ui/app/components/pages/unauthenticated/index.js new file mode 100644 index 000000000..f8b5fa172 --- /dev/null +++ b/ui/app/components/pages/unauthenticated/index.js @@ -0,0 +1,38 @@ +const { connect } = require('react-redux') +const PropTypes = require('prop-types') +const { Redirect } = require('react-router-dom') +const h = require('react-hyperscript') +const { INITIALIZE_ROUTE } = require('../../../routes') +const MetamaskRoute = require('../metamask-route') + +const Unauthenticated = ({ component: Component, isInitialized, ...props }) => { + const component = renderProps => { + return isInitialized + ? h(Component, { ...renderProps }) + : h(Redirect, { to: { pathname: INITIALIZE_ROUTE } }) + } + + return ( + h(MetamaskRoute, { + ...props, + component, + }) + ) +} + +Unauthenticated.propTypes = { + component: PropTypes.func, + isInitialized: PropTypes.bool, + isMascara: PropTypes.bool, + mascaraComponent: PropTypes.func, +} + +const mapStateToProps = state => { + const { metamask: { isInitialized, isMascara } } = state + return { + isInitialized, + isMascara, + } +} + +module.exports = connect(mapStateToProps)(Unauthenticated) diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js index 6832f5ddf..addcedc77 100644 --- a/ui/app/first-time/init-menu.js +++ b/ui/app/first-time/init-menu.js @@ -35,7 +35,7 @@ class InitializeMenuScreen extends Component { const { warning } = this.state return ( - h('.initialize-screen.flex-column.flex-center.flex-grow', [ + h('.initialize-screen.flex-column.flex-center', [ h(Mascot, { animationEventEmitter: this.animationEventEmitter, diff --git a/ui/app/routes.js b/ui/app/routes.js index 1305d6b1e..72be616a7 100644 --- a/ui/app/routes.js +++ b/ui/app/routes.js @@ -8,7 +8,7 @@ const ADD_TOKEN_ROUTE = '/add-token' const IMPORT_ACCOUNT_ROUTE = '/import-account' const SEND_ROUTE = '/send' const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction' -const INITIALIZE_MENU_ROUTE = '/initialize-menu' +const INITIALIZE_ROUTE = '/initialize' const NOTICE_ROUTE = '/notice' module.exports = { @@ -22,6 +22,6 @@ module.exports = { IMPORT_ACCOUNT_ROUTE, SEND_ROUTE, CONFIRM_TRANSACTION_ROUTE, - INITIALIZE_MENU_ROUTE, + INITIALIZE_ROUTE, NOTICE_ROUTE, }