aboutsummaryrefslogtreecommitdiffstats
path: root/mascara/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'mascara/src/app')
-rw-r--r--mascara/src/app/first-time/confirm-seed-screen.js151
-rw-r--r--mascara/src/app/first-time/create-password-screen.js279
-rw-r--r--mascara/src/app/first-time/import-seed-phrase-screen.js10
-rw-r--r--mascara/src/app/first-time/index.js187
-rw-r--r--mascara/src/app/first-time/notice-screen.js105
-rw-r--r--mascara/src/app/first-time/seed-screen.js (renamed from mascara/src/app/first-time/backup-phrase-screen.js)147
-rw-r--r--mascara/src/app/first-time/unique-image-screen.js18
7 files changed, 504 insertions, 393 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..438f383b1
--- /dev/null
+++ b/mascara/src/app/first-time/confirm-seed-screen.js
@@ -0,0 +1,151 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router-dom'
+import classnames from 'classnames'
+import shuffle from 'lodash.shuffle'
+import { compose } from 'recompose'
+import Identicon from '../../../../ui/app/components/identicon'
+import { confirmSeedWords, showModal } 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,
+ openBuyEtherModal: PropTypes.func,
+ };
+
+ 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)
+ }
+ }
+
+ handleClick () {
+ const { confirmSeedWords, history, openBuyEtherModal } = this.props
+
+ confirmSeedWords()
+ .then(() => {
+ history.push(DEFAULT_ROUTE)
+ openBuyEtherModal()
+ })
+ }
+
+ render () {
+ const { seedWords } = 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="first-view-main-wrapper">
+ <div className="first-view-main">
+ <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 && this.handleClick()}
+ disabled={!isValid}
+ >
+ Confirm
+ </button>
+ </div>
+ </div>
+ <Breadcrumbs total={3} currentIndex={1} />
+ </div>
+ </div>
+ </div>
+ )
+ }
+ </div>
+ )
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(
+ ({ metamask: { selectedAddress, seedWords }, appState: { isLoading } }) => ({
+ seedWords,
+ isLoading,
+ address: selectedAddress,
+ }),
+ dispatch => ({
+ confirmSeedWords: () => dispatch(confirmSeedWords()),
+ openBuyEtherModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER'})),
+ })
+ )
+)(ConfirmSeedScreen)
diff --git a/mascara/src/app/first-time/create-password-screen.js b/mascara/src/app/first-time/create-password-screen.js
index 192da3399..6ec05e11d 100644
--- a/mascara/src/app/first-time/create-password-screen.js
+++ b/mascara/src/app/first-time/create-password-screen.js
@@ -1,20 +1,26 @@
-import EventEmitter from 'events'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
-import classnames from 'classnames'
-import {createNewVaultAndKeychain} from '../../../../ui/app/actions'
-import LoadingScreen from './loading-screen'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
+import { createNewVaultAndKeychain } from '../../../../ui/app/actions'
import Breadcrumbs from './breadcrumbs'
+import EventEmitter from 'events'
import Mascot from '../../../../ui/app/components/mascot'
+import classnames from 'classnames'
+import {
+ INITIALIZE_UNIQUE_IMAGE_ROUTE,
+ INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
+ INITIALIZE_NOTICE_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,
+ isInitialized: PropTypes.bool,
+ isUnlocked: PropTypes.bool,
isMascara: PropTypes.bool.isRequired,
}
@@ -23,13 +29,21 @@ class CreatePasswordScreen extends Component {
confirmPassword: '',
}
- constructor () {
- super()
+ constructor (props) {
+ super(props)
this.animationEventEmitter = new EventEmitter()
}
+ componentWillMount () {
+ const { isInitialized, history } = this.props
+
+ if (isInitialized) {
+ history.push(INITIALIZE_NOTICE_ROUTE)
+ }
+ }
+
isValid () {
- const {password, confirmPassword} = this.state
+ const { password, confirmPassword } = this.state
if (!password || !confirmPassword) {
return false
@@ -47,93 +61,182 @@ class CreatePasswordScreen extends Component {
return
}
- const {password} = this.state
- const {createAccount, next} = this.props
+ const { password } = this.state
+ const { createAccount, history } = this.props
+ this.setState({ isLoading: true })
createAccount(password)
- .then(next)
+ .then(() => history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE))
+ }
+
+ renderFields () {
+ const { isMascara, history } = this.props
+
+ return (
+ <div className={classnames({ 'first-view-main-wrapper': !isMascara })}>
+ <div className={classnames({
+ 'first-view-main': !isMascara,
+ 'first-view-main__mascara': isMascara,
+ })}>
+ {isMascara && <div className="mascara-info first-view-phone-invisible">
+ <Mascot
+ animationEventEmitter={this.animationEventEmitter}
+ width="225"
+ height="225"
+ />
+ <div className="info">
+ MetaMask is a secure identity vault for Ethereum.
+ </div>
+ <div className="info">
+ It allows you to hold ether & tokens, and interact with decentralized applications.
+ </div>
+ </div>}
+ <div className="create-password">
+ <div className="create-password__title">
+ Create Password
+ </div>
+ <input
+ className="first-time-flow__input"
+ type="password"
+ placeholder="New Password (min 8 characters)"
+ onChange={e => this.setState({password: e.target.value})}
+ />
+ <input
+ className="first-time-flow__input create-password__confirm-input"
+ type="password"
+ placeholder="Confirm Password"
+ onChange={e => this.setState({confirmPassword: e.target.value})}
+ />
+ <button
+ className="first-time-flow__button"
+ disabled={!this.isValid()}
+ onClick={this.createAccount}
+ >
+ Create
+ </button>
+ <a
+ href=""
+ className="first-time-flow__link create-password__import-link"
+ onClick={e => {
+ e.preventDefault()
+ history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE)
+ }}
+ >
+ Import with seed phrase
+ </a>
+ { /* }
+ <a
+ href=""
+ className="first-time-flow__link create-password__import-link"
+ onClick={e => {
+ e.preventDefault()
+ history.push(INITIALIZE_IMPORT_ACCOUNT_ROUTE)
+ }}
+ >
+ Import an account
+ </a>
+ { */ }
+ <Breadcrumbs total={3} currentIndex={0} />
+ </div>
+ </div>
+ </div>
+ )
}
render () {
- const { isLoading, goToImportWithSeedPhrase, isMascara } = this.props
-
- return isLoading
- ? <LoadingScreen loadingMessage="Creating your new account" />
- : (
- <div className={classnames({ 'first-view-main-wrapper': !isMascara })}>
- <div className={classnames({
- 'first-view-main': !isMascara,
- 'first-view-main__mascara': isMascara,
- })}>
- {isMascara && <div className="mascara-info first-view-phone-invisible">
- <Mascot
- animationEventEmitter={this.animationEventEmitter}
- width="225"
- height="225"
- />
- <div className="info">
- MetaMask is a secure identity vault for Ethereum.
- </div>
- <div className="info">
- It allows you to hold ether & tokens, and interact with decentralized applications.
- </div>
- </div>}
- <div className="create-password">
- <div className="create-password__title">
- Create Password
- </div>
- <input
- className="first-time-flow__input"
- type="password"
- placeholder="New Password (min 8 characters)"
- onChange={e => this.setState({password: e.target.value})}
- />
- <input
- className="first-time-flow__input create-password__confirm-input"
- type="password"
- placeholder="Confirm Password"
- onChange={e => this.setState({confirmPassword: e.target.value})}
- />
- <button
- className="first-time-flow__button"
- disabled={!this.isValid()}
- onClick={this.createAccount}
- >
- Create
- </button>
- <a
- href=""
- className="first-time-flow__link create-password__import-link"
- onClick={e => {
- e.preventDefault()
- goToImportWithSeedPhrase()
- }}
- >
- Import with seed phrase
- </a>
- { /* }
- <a
- href=""
- className="first-time-flow__link create-password__import-link"
- onClick={e => {
- e.preventDefault()
- goToImportAccount()
- }}
- >
- Import an account
- </a>
- { */ }
- <Breadcrumbs total={3} currentIndex={0} />
+ const { history, isMascara } = this.props
+
+ return (
+ <div className={classnames({ 'first-view-main-wrapper': !isMascara })}>
+ <div className={classnames({
+ 'first-view-main': !isMascara,
+ 'first-view-main__mascara': isMascara,
+ })}>
+ {isMascara && <div className="mascara-info first-view-phone-invisible">
+ <Mascot
+ animationEventEmitter={this.animationEventEmitter}
+ width="225"
+ height="225"
+ />
+ <div className="info">
+ MetaMask is a secure identity vault for Ethereum.
+ </div>
+ <div className="info">
+ It allows you to hold ether & tokens, and interact with decentralized applications.
</div>
+ </div>}
+ <div className="create-password">
+ <div className="create-password__title">
+ Create Password
+ </div>
+ <input
+ className="first-time-flow__input"
+ type="password"
+ placeholder="New Password (min 8 characters)"
+ onChange={e => this.setState({password: e.target.value})}
+ />
+ <input
+ className="first-time-flow__input create-password__confirm-input"
+ type="password"
+ placeholder="Confirm Password"
+ onChange={e => this.setState({confirmPassword: e.target.value})}
+ />
+ <button
+ className="first-time-flow__button"
+ disabled={!this.isValid()}
+ onClick={this.createAccount}
+ >
+ Create
+ </button>
+ <a
+ href=""
+ className="first-time-flow__link create-password__import-link"
+ onClick={e => {
+ e.preventDefault()
+ history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE)
+ }}
+ >
+ Import with seed phrase
+ </a>
+ { /* }
+ <a
+ href=""
+ className="first-time-flow__link create-password__import-link"
+ onClick={e => {
+ e.preventDefault()
+ history.push(INITIALIZE_IMPORT_ACCOUNT_ROUTE)
+ }}
+ >
+ Import an account
+ </a>
+ { */ }
+ <Breadcrumbs total={3} currentIndex={0} />
</div>
</div>
- )
+ </div>
+ )
+ }
+}
+
+const mapStateToProps = ({ metamask, appState }) => {
+ const { isInitialized, isUnlocked, isMascara, noActiveNotices } = metamask
+ const { isLoading } = appState
+
+ return {
+ isLoading,
+ isInitialized,
+ isUnlocked,
+ isMascara,
+ noActiveNotices,
}
}
-export default connect(
- ({ appState: { isLoading }, metamask: { isMascara } }) => ({ isLoading, isMascara }),
- dispatch => ({
- createAccount: password => dispatch(createNewVaultAndKeychain(password)),
- })
+export default compose(
+ withRouter,
+ connect(
+ mapStateToProps,
+ dispatch => ({
+ createAccount: password => dispatch(createNewVaultAndKeychain(password)),
+ })
+ )
)(CreatePasswordScreen)
diff --git a/mascara/src/app/first-time/import-seed-phrase-screen.js b/mascara/src/app/first-time/import-seed-phrase-screen.js
index 86f02ceac..5c2b21b0a 100644
--- a/mascara/src/app/first-time/import-seed-phrase-screen.js
+++ b/mascara/src/app/first-time/import-seed-phrase-screen.js
@@ -8,16 +8,16 @@ import {
displayWarning,
unMarkPasswordForgotten,
} from '../../../../ui/app/actions'
+import { DEFAULT_ROUTE, INITIALIZE_NOTICE_ROUTE } from '../../../../ui/app/routes'
class ImportSeedPhraseScreen extends Component {
static propTypes = {
warning: PropTypes.string,
- back: PropTypes.func.isRequired,
- next: PropTypes.func.isRequired,
createNewVaultAndRestore: PropTypes.func.isRequired,
hideWarning: PropTypes.func.isRequired,
displayWarning: PropTypes.func,
leaveImportSeedScreenState: PropTypes.func,
+ history: PropTypes.object,
};
state = {
@@ -64,14 +64,14 @@ class ImportSeedPhraseScreen extends Component {
const { password, seedPhrase } = this.state
const {
createNewVaultAndRestore,
- next,
displayWarning,
leaveImportSeedScreenState,
+ history,
} = this.props
leaveImportSeedScreenState()
createNewVaultAndRestore(password, this.parseSeedPhrase(seedPhrase))
- .then(next)
+ .then(() => history.push(INITIALIZE_NOTICE_ROUTE))
}
render () {
@@ -86,7 +86,7 @@ class ImportSeedPhraseScreen extends Component {
className="import-account__back-button"
onClick={e => {
e.preventDefault()
- this.props.back()
+ this.props.history.goBack()
}}
href="#"
>
diff --git a/mascara/src/app/first-time/index.js b/mascara/src/app/first-time/index.js
index 0cc3b4b0e..01e5d67a6 100644
--- a/mascara/src/app/first-time/index.js
+++ b/mascara/src/app/first-time/index.js
@@ -1,17 +1,26 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
+import { withRouter, Switch, Route } from 'react-router-dom'
+import { compose } from 'recompose'
import CreatePasswordScreen from './create-password-screen'
import UniqueImageScreen from './unique-image-screen'
import NoticeScreen from './notice-screen'
-import BackupPhraseScreen from './backup-phrase-screen'
+import BackupPhraseScreen from './seed-screen'
import ImportAccountScreen from './import-account-screen'
import ImportSeedPhraseScreen from './import-seed-phrase-screen'
+import ConfirmSeed from './confirm-seed-screen'
import {
- onboardingBuyEthView,
- unMarkPasswordForgotten,
- showModal,
-} from '../../../../ui/app/actions'
+ INITIALIZE_ROUTE,
+ INITIALIZE_IMPORT_ACCOUNT_ROUTE,
+ INITIALIZE_UNIQUE_IMAGE_ROUTE,
+ INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
+ INITIALIZE_NOTICE_ROUTE,
+ INITIALIZE_BACKUP_PHRASE_ROUTE,
+ INITIALIZE_CONFIRM_SEED_ROUTE,
+ INITIALIZE_CREATE_PASSWORD_ROUTE,
+} from '../../../../ui/app/routes'
+import WelcomeScreen from '../../../../ui/app/welcome-screen'
class FirstTimeFlow extends Component {
@@ -20,6 +29,10 @@ class FirstTimeFlow extends Component {
seedWords: PropTypes.string,
address: PropTypes.string,
noActiveNotices: PropTypes.bool,
+ goToBuyEtherView: PropTypes.func,
+ isUnlocked: PropTypes.bool,
+ history: PropTypes.object,
+ welcomeScreenSeen: PropTypes.bool,
};
static defaultProps = {
@@ -28,145 +41,53 @@ class FirstTimeFlow extends Component {
noActiveNotices: false,
};
- static SCREEN_TYPE = {
- CREATE_PASSWORD: 'create_password',
- IMPORT_ACCOUNT: 'import_account',
- IMPORT_SEED_PHRASE: 'import_seed_phrase',
- UNIQUE_IMAGE: 'unique_image',
- NOTICE: 'notice',
- BACK_UP_PHRASE: 'back_up_phrase',
- CONFIRM_BACK_UP_PHRASE: 'confirm_back_up_phrase',
- LOADING: 'loading',
- };
-
- constructor (props) {
- super(props)
- this.state = {
- screenType: this.getScreenType(),
- }
- }
-
- setScreenType (screenType) {
- this.setState({ screenType })
- }
-
- getScreenType () {
- const {
- isInitialized,
- seedWords,
- noActiveNotices,
- forgottenPassword,
- } = this.props
- const {SCREEN_TYPE} = FirstTimeFlow
-
- // return SCREEN_TYPE.NOTICE
-
- if (forgottenPassword) {
- return SCREEN_TYPE.IMPORT_SEED_PHRASE
- }
- if (!isInitialized) {
- return SCREEN_TYPE.CREATE_PASSWORD
- }
-
- if (!noActiveNotices) {
- return SCREEN_TYPE.NOTICE
- }
-
- if (seedWords) {
- return SCREEN_TYPE.BACK_UP_PHRASE
- }
- };
-
- renderScreen () {
- const {SCREEN_TYPE} = FirstTimeFlow
- const {
- openBuyEtherModal,
- address,
- restoreCreatePasswordScreen,
- forgottenPassword,
- leaveImportSeedScreenState,
- } = this.props
-
- switch (this.state.screenType) {
- case SCREEN_TYPE.CREATE_PASSWORD:
- return (
- <CreatePasswordScreen
- next={() => this.setScreenType(SCREEN_TYPE.UNIQUE_IMAGE)}
- goToImportAccount={() => this.setScreenType(SCREEN_TYPE.IMPORT_ACCOUNT)}
- goToImportWithSeedPhrase={() => this.setScreenType(SCREEN_TYPE.IMPORT_SEED_PHRASE)}
- />
- )
- case SCREEN_TYPE.IMPORT_ACCOUNT:
- return (
- <ImportAccountScreen
- back={() => this.setScreenType(SCREEN_TYPE.CREATE_PASSWORD)}
- next={() => this.setScreenType(SCREEN_TYPE.NOTICE)}
- />
- )
- case SCREEN_TYPE.IMPORT_SEED_PHRASE:
- return (
- <ImportSeedPhraseScreen
- back={() => {
- leaveImportSeedScreenState()
- this.setScreenType(SCREEN_TYPE.CREATE_PASSWORD)
- }}
- next={() => {
- const newScreenType = forgottenPassword ? null : SCREEN_TYPE.NOTICE
- this.setScreenType(newScreenType)
- }}
- />
- )
- case SCREEN_TYPE.UNIQUE_IMAGE:
- return (
- <UniqueImageScreen
- next={() => this.setScreenType(SCREEN_TYPE.NOTICE)}
- />
- )
- case SCREEN_TYPE.NOTICE:
- return (
- <NoticeScreen
- next={() => this.setScreenType(SCREEN_TYPE.BACK_UP_PHRASE)}
- />
- )
- case SCREEN_TYPE.BACK_UP_PHRASE:
- return (
- <BackupPhraseScreen
- next={() => openBuyEtherModal()}
- />
- )
- default:
- return <noscript />
- }
- }
-
render () {
return (
<div className="first-time-flow">
- {this.renderScreen()}
+ <Switch>
+ <Route exact path={INITIALIZE_IMPORT_ACCOUNT_ROUTE} component={ImportAccountScreen} />
+ <Route
+ exact
+ path={INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE}
+ component={ImportSeedPhraseScreen}
+ />
+ <Route exact path={INITIALIZE_UNIQUE_IMAGE_ROUTE} component={UniqueImageScreen} />
+ <Route exact path={INITIALIZE_NOTICE_ROUTE} component={NoticeScreen} />
+ <Route exact path={INITIALIZE_BACKUP_PHRASE_ROUTE} component={BackupPhraseScreen} />
+ <Route exact path={INITIALIZE_CONFIRM_SEED_ROUTE} component={ConfirmSeed} />
+ <Route exact path={INITIALIZE_CREATE_PASSWORD_ROUTE} component={CreatePasswordScreen} />
+ <Route exact path={INITIALIZE_ROUTE} component={WelcomeScreen} />
+ </Switch>
</div>
)
}
-
}
-export default connect(
- ({
- metamask: {
- isInitialized,
- seedWords,
- noActiveNotices,
- selectedAddress,
- forgottenPassword,
- }
- }) => ({
+const mapStateToProps = ({ metamask }) => {
+ const {
+ isInitialized,
+ seedWords,
+ noActiveNotices,
+ selectedAddress,
+ forgottenPassword,
+ isMascara,
+ isUnlocked,
+ welcomeScreenSeen,
+ } = metamask
+
+ return {
+ isMascara,
isInitialized,
seedWords,
noActiveNotices,
address: selectedAddress,
forgottenPassword,
- }),
- dispatch => ({
- leaveImportSeedScreenState: () => dispatch(unMarkPasswordForgotten()),
- openBuyEtherModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER'})),
- })
+ isUnlocked,
+ welcomeScreenSeen,
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(mapStateToProps)
)(FirstTimeFlow)
diff --git a/mascara/src/app/first-time/notice-screen.js b/mascara/src/app/first-time/notice-screen.js
index cbd8f9f5b..a449ccfa9 100644
--- a/mascara/src/app/first-time/notice-screen.js
+++ b/mascara/src/app/first-time/notice-screen.js
@@ -1,11 +1,14 @@
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 { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
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 { INITIALIZE_BACKUP_PHRASE_ROUTE } from '../../../../ui/app/routes'
import LoadingScreen from './loading-screen'
class NoticeScreen extends Component {
@@ -16,8 +19,15 @@ class NoticeScreen extends Component {
date: 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,
+ isLoading: PropTypes.bool,
+ noActiveNotices: PropTypes.bool,
};
static defaultProps = {
@@ -29,17 +39,24 @@ class NoticeScreen extends Component {
}
componentDidMount () {
+ if (this.props.noActiveNotices) {
+ this.props.history.push(INITIALIZE_BACKUP_PHRASE_ROUTE)
+ }
+
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(hasActiveNotices => {
+ if (!hasActiveNotices) {
+ history.push(INITIALIZE_BACKUP_PHRASE_ROUTE)
+ } else {
+ this.setState({ atBottom: false })
+ this.onScroll()
+ }
+ })
}
onScroll = debounce(() => {
@@ -64,27 +81,29 @@ class NoticeScreen extends Component {
isLoading
? <LoadingScreen />
: (
- <div className="first-view-main-wrapper">
- <div className="first-view-main">
- <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="first-view-main-wrapper">
+ <div className="first-view-main">
+ <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>
</div>
</div>
@@ -93,12 +112,24 @@ class NoticeScreen extends Component {
}
}
-export default connect(
- ({ metamask: { selectedAddress, lastUnreadNotice }, appState: { isLoading } }) => ({
- lastUnreadNotice,
+const mapStateToProps = ({ metamask, appState }) => {
+ const { selectedAddress, lastUnreadNotice, noActiveNotices } = metamask
+ const { isLoading } = appState
+
+ return {
address: selectedAddress,
- }),
- dispatch => ({
- markNoticeRead: notice => dispatch(markNoticeRead(notice)),
- })
+ lastUnreadNotice,
+ noActiveNotices,
+ isLoading,
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(
+ mapStateToProps,
+ dispatch => ({
+ 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 6819abcf3..d004be77b 100644
--- a/mascara/src/app/first-time/backup-phrase-screen.js
+++ b/mascara/src/app/first-time/seed-screen.js
@@ -1,13 +1,13 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
+import { connect } from 'react-redux'
import classnames from 'classnames'
-import shuffle from 'lodash.shuffle'
-import {compose, onlyUpdateForPropTypes} from 'recompose'
+import { withRouter } from 'react-router-dom'
+import { compose } 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, INITIALIZE_CONFIRM_SEED_ROUTE } from '../../../../ui/app/routes'
const LockIcon = props => (
<svg
@@ -36,34 +36,32 @@ 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)
}
}
@@ -73,7 +71,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>
@@ -96,6 +94,7 @@ class BackupPhraseScreen extends Component {
renderSecretScreen () {
const { isShowingSecret } = this.state
+ const { history } = this.props
return (
<div className="backup-phrase__content-wrapper">
@@ -124,10 +123,7 @@ class BackupPhraseScreen extends Component {
<div className="backup-phrase__next-button">
<button
className="first-time-flow__button"
- onClick={() => isShowingSecret && this.setState({
- isShowingSecret: false,
- page: BackupPhraseScreen.PAGE.CONFIRM,
- })}
+ onClick={() => isShowingSecret && history.push(INITIALIZE_CONFIRM_SEED_ROUTE)}
disabled={!isShowingSecret}
>
Next
@@ -138,99 +134,6 @@ class BackupPhraseScreen extends Component {
)
}
- renderConfirmationScreen() {
- const { seedWords, confirmSeedWords, next } = this.props;
- const { selectedSeeds, shuffledSeeds } = this.state;
- const isValid = seedWords === selectedSeeds.map(([_, seed]) => seed).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">
- {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>
- )
- }
-
- 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" />
@@ -238,9 +141,8 @@ class BackupPhraseScreen extends Component {
<div className="first-view-main-wrapper">
<div className="first-view-main">
<div className="backup-phrase">
- {this.renderBack()}
<Identicon address={this.props.address} diameter={70} />
- {this.renderContent()}
+ {this.renderSecretScreen()}
</div>
</div>
</div>
@@ -249,15 +151,12 @@ class BackupPhraseScreen extends Component {
}
export default compose(
- onlyUpdateForPropTypes,
+ withRouter,
connect(
({ metamask: { selectedAddress, seedWords }, appState: { isLoading } }) => ({
seedWords,
isLoading,
address: selectedAddress,
- }),
- dispatch => ({
- confirmSeedWords: () => dispatch(confirmSeedWords()),
})
)
)(BackupPhraseScreen)
diff --git a/mascara/src/app/first-time/unique-image-screen.js b/mascara/src/app/first-time/unique-image-screen.js
index ede17ee3d..9555e5318 100644
--- a/mascara/src/app/first-time/unique-image-screen.js
+++ b/mascara/src/app/first-time/unique-image-screen.js
@@ -1,13 +1,16 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
import {connect} from 'react-redux'
import Identicon from '../../../../ui/app/components/identicon'
import Breadcrumbs from './breadcrumbs'
+import { INITIALIZE_NOTICE_ROUTE } from '../../../../ui/app/routes'
class UniqueImageScreen extends Component {
static propTypes = {
address: PropTypes.string,
- next: PropTypes.func.isRequired,
+ history: PropTypes.object,
}
render () {
@@ -25,7 +28,7 @@ class UniqueImageScreen extends Component {
</div>
<button
className="first-time-flow__button"
- onClick={this.props.next}
+ onClick={() => this.props.history.push(INITIALIZE_NOTICE_ROUTE)}
>
Next
</button>
@@ -37,8 +40,11 @@ class UniqueImageScreen extends Component {
}
}
-export default connect(
- ({ metamask: { selectedAddress } }) => ({
- address: selectedAddress,
- })
+export default compose(
+ withRouter,
+ connect(
+ ({ metamask: { selectedAddress } }) => ({
+ address: selectedAddress,
+ })
+ )
)(UniqueImageScreen)