aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorDan J Miller <danjm.com@gmail.com>2019-08-02 11:57:26 +0800
committerGitHub <noreply@github.com>2019-08-02 11:57:26 +0800
commit3eff4787757ac22d0a469c91599cdcfd97a2a98c (patch)
treed0ba3b8b5fbbb9f6d68ba227fa3fd8410b766912 /ui
parent189e126f6184b4351a9f11d8dc063d7abd5e9bbe (diff)
downloadtangerine-wallet-browser-3eff4787757ac22d0a469c91599cdcfd97a2a98c.tar.gz
tangerine-wallet-browser-3eff4787757ac22d0a469c91599cdcfd97a2a98c.tar.zst
tangerine-wallet-browser-3eff4787757ac22d0a469c91599cdcfd97a2a98c.zip
I5849 incremental account security (#6874)
* Implements ability to defer seed phrase backup to later * Adds incremental-security.spec.js, including test dapp that sends signed tx with stand alone localhost provider * Update metamask-responsive-ui for incremental account security changes * Update backup-notification style and fix responsiveness of seed phrase screen * Remove uneeded files from send-eth-with-private-key-test/ * Apply linguist flags in .gitattributes for send-eth-with-private-key-test/ethereumjs-tx.js * Improve docs in controllers/onboarding.js * Clean up metamask-extension/test/e2e/send-eth-with-private-key-test/index.html * Remove unnecessary newlines in a couple first-time-flow/ files * Fix import of backup-notification in home.component * Fix git attrs file
Diffstat (limited to 'ui')
-rw-r--r--ui/app/components/app/backup-notification/backup-notification.component.js50
-rw-r--r--ui/app/components/app/backup-notification/backup-notification.container.js16
-rw-r--r--ui/app/components/app/backup-notification/index.js1
-rw-r--r--ui/app/components/app/backup-notification/index.scss75
-rw-r--r--ui/app/components/app/index.scss2
-rw-r--r--ui/app/ducks/app/app.js11
-rw-r--r--ui/app/helpers/higher-order-components/authenticated/authenticated.container.js1
-rw-r--r--ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js8
-rw-r--r--ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.container.js13
-rw-r--r--ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/index.js2
-rw-r--r--ui/app/pages/first-time-flow/first-time-flow.component.js16
-rw-r--r--ui/app/pages/first-time-flow/first-time-flow.container.js6
-rw-r--r--ui/app/pages/first-time-flow/index.scss4
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js13
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js23
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js2
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.js2
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss16
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js47
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.container.js15
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/seed-phrase.component.js21
-rw-r--r--ui/app/pages/first-time-flow/seed-phrase/tests/confirm-seed-phrase-component.test.js6
-rw-r--r--ui/app/pages/home/home.component.js9
-rw-r--r--ui/app/pages/home/home.container.js4
-rw-r--r--ui/app/pages/settings/networks-tab/networks-tab.component.js1
-rw-r--r--ui/app/store/actions.js36
26 files changed, 375 insertions, 25 deletions
diff --git a/ui/app/components/app/backup-notification/backup-notification.component.js b/ui/app/components/app/backup-notification/backup-notification.component.js
new file mode 100644
index 000000000..dba79186c
--- /dev/null
+++ b/ui/app/components/app/backup-notification/backup-notification.component.js
@@ -0,0 +1,50 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import Button from '../../ui/button'
+import {
+ INITIALIZE_SEED_PHRASE_ROUTE,
+} from '../../../helpers/constants/routes'
+
+export default class BackupNotification extends PureComponent {
+ static propTypes = {
+ history: PropTypes.object,
+ showSeedPhraseBackupAfterOnboarding: PropTypes.func,
+ }
+
+ static contextTypes = {
+ t: PropTypes.func,
+ metricsEvent: PropTypes.func,
+ }
+
+ handleSubmit = () => {
+ const { history, showSeedPhraseBackupAfterOnboarding } = this.props
+ showSeedPhraseBackupAfterOnboarding()
+ history.push(INITIALIZE_SEED_PHRASE_ROUTE)
+ }
+
+ render () {
+ const { t } = this.context
+
+ return (
+ <div className="backup-notification">
+ <div className="backup-notification__header">
+ <img
+ className="backup-notification__icon"
+ src="images/meta-shield.svg"
+ />
+ <div className="backup-notification__text">Backup your Secret Recovery code to keep your wallet and funds secure.</div>
+ <i className="fa fa-info-circle"></i>
+ </div>
+ <div className="backup-notification__buttons">
+ <Button
+ type="primary"
+ className="backup-notification__submit-button"
+ onClick={this.handleSubmit}
+ >
+ { t('backupNow') }
+ </Button>
+ </div>
+ </div>
+ )
+ }
+}
diff --git a/ui/app/components/app/backup-notification/backup-notification.container.js b/ui/app/components/app/backup-notification/backup-notification.container.js
new file mode 100644
index 000000000..6996770bc
--- /dev/null
+++ b/ui/app/components/app/backup-notification/backup-notification.container.js
@@ -0,0 +1,16 @@
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
+import BackupNotification from './backup-notification.component'
+import { showSeedPhraseBackupAfterOnboarding } from '../../../store/actions'
+
+const mapDispatchToProps = dispatch => {
+ return {
+ showSeedPhraseBackupAfterOnboarding: () => dispatch(showSeedPhraseBackupAfterOnboarding()),
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(null, mapDispatchToProps)
+)(BackupNotification)
diff --git a/ui/app/components/app/backup-notification/index.js b/ui/app/components/app/backup-notification/index.js
new file mode 100644
index 000000000..a1cbfe75a
--- /dev/null
+++ b/ui/app/components/app/backup-notification/index.js
@@ -0,0 +1 @@
+export { default } from './backup-notification.container'
diff --git a/ui/app/components/app/backup-notification/index.scss b/ui/app/components/app/backup-notification/index.scss
new file mode 100644
index 000000000..2d76c6ce4
--- /dev/null
+++ b/ui/app/components/app/backup-notification/index.scss
@@ -0,0 +1,75 @@
+.backup-notification {
+ background: rgba(36, 41, 46, 0.9);
+ box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.12);
+ border-radius: 8px;
+ height: 116px;
+ padding: 16px;
+ margin: 8px;
+
+ display: flex;
+ flex-flow: column;
+ justify-content: space-between;
+
+ position: absolute;
+ right: 0;
+ bottom: 0;
+
+ &__header {
+ display: flex;
+ }
+
+ &__text {
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ font-size: 12px;
+ color: #FFFFFF;
+ margin-left: 10px;
+ margin-right: 8px;
+ }
+
+ .fa-info-circle {
+ color: #6A737D;
+ }
+
+ &__ignore-button {
+ border: 2px solid #6A737D;
+ box-sizing: border-box;
+ border-radius: 6px;
+ color: $white;
+ background-color: rgba(36, 41, 46, 0.9);
+ height: 34px;
+ width: 155px;
+ padding: 0;
+
+ &:hover {
+ border-color: #6A737D;
+ background-color: #6A737D;
+ }
+ }
+
+ &__submit-button {
+ border: 2px solid #6A737D;
+ box-sizing: border-box;
+ border-radius: 6px;
+ color: $white;
+ background-color: rgba(36, 41, 46, 0.9);
+ height: 34px;
+ width: 155px;
+ padding: 0;
+
+ &:hover {
+ background-color: #3b4046;
+ }
+
+ &:active {
+ background-color:#141618;
+ }
+ }
+
+ &__buttons {
+ display: flex;
+ width: 130px;
+ align-self: flex-end;
+ }
+} \ No newline at end of file
diff --git a/ui/app/components/app/index.scss b/ui/app/components/app/index.scss
index 9b7da8c2e..1f70ba974 100644
--- a/ui/app/components/app/index.scss
+++ b/ui/app/components/app/index.scss
@@ -81,3 +81,5 @@
@import '../ui/toggle-button/index';
@import 'home-notification/index';
+
+@import 'backup-notification/index';
diff --git a/ui/app/ducks/app/app.js b/ui/app/ducks/app/app.js
index 10ed7d155..6fe2a3a9a 100644
--- a/ui/app/ducks/app/app.js
+++ b/ui/app/ducks/app/app.js
@@ -73,6 +73,7 @@ function reduceApp (state, action) {
networksTabSelectedRpcUrl: '',
networksTabIsInAddMode: false,
loadingMethodData: false,
+ showingSeedPhraseBackupAfterOnboarding: false,
}, state.appState)
switch (action.type) {
@@ -756,6 +757,16 @@ function reduceApp (state, action) {
loadingMethodData: false,
})
+ case actions.SHOW_SEED_PHRASE_BACKUP_AFTER_ONBOARDING:
+ return extend(appState, {
+ showingSeedPhraseBackupAfterOnboarding: true,
+ })
+
+ case actions.HIDE_SEED_PHRASE_BACKUP_AFTER_ONBOARDING:
+ return extend(appState, {
+ showingSeedPhraseBackupAfterOnboarding: false,
+ })
+
default:
return appState
diff --git a/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js b/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js
index 6124b0fcd..8fc637332 100644
--- a/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js
+++ b/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js
@@ -3,6 +3,7 @@ import Authenticated from './authenticated.component'
const mapStateToProps = state => {
const { metamask: { isUnlocked, completedOnboarding } } = state
+
return {
isUnlocked,
completedOnboarding,
diff --git a/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js
index a5cf0f752..48eff96cb 100644
--- a/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js
+++ b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js
@@ -17,6 +17,7 @@ export default class ImportWithSeedPhrase extends PureComponent {
static propTypes = {
history: PropTypes.object,
onSubmit: PropTypes.func.isRequired,
+ setSeedPhraseBackedUp: PropTypes.func,
}
state = {
@@ -126,7 +127,7 @@ export default class ImportWithSeedPhrase extends PureComponent {
}
const { password, seedPhrase } = this.state
- const { history, onSubmit } = this.props
+ const { history, onSubmit, setSeedPhraseBackedUp } = this.props
try {
await onSubmit(password, this.parseSeedPhrase(seedPhrase))
@@ -137,7 +138,10 @@ export default class ImportWithSeedPhrase extends PureComponent {
name: 'Import Complete',
},
})
- history.push(INITIALIZE_END_OF_FLOW_ROUTE)
+
+ setSeedPhraseBackedUp(true).then(() => {
+ history.push(INITIALIZE_END_OF_FLOW_ROUTE)
+ })
} catch (error) {
this.setState({ seedPhraseError: error.message })
}
diff --git a/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.container.js b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.container.js
new file mode 100644
index 000000000..0cfeee1f4
--- /dev/null
+++ b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.container.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import ImportWithSeedPhrase from './import-with-seed-phrase.component'
+import {
+ setSeedPhraseBackedUp,
+} from '../../../../store/actions'
+
+const mapDispatchToProps = dispatch => {
+ return {
+ setSeedPhraseBackedUp: (seedPhraseBackupState) => dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)),
+ }
+}
+
+export default connect(null, mapDispatchToProps)(ImportWithSeedPhrase)
diff --git a/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/index.js b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/index.js
index e5ff1fde5..9d4ad7d0f 100644
--- a/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/index.js
+++ b/ui/app/pages/first-time-flow/create-password/import-with-seed-phrase/index.js
@@ -1 +1 @@
-export { default } from './import-with-seed-phrase.component'
+export { default } from './import-with-seed-phrase.container'
diff --git a/ui/app/pages/first-time-flow/first-time-flow.component.js b/ui/app/pages/first-time-flow/first-time-flow.component.js
index 0d206bf42..df9631e15 100644
--- a/ui/app/pages/first-time-flow/first-time-flow.component.js
+++ b/ui/app/pages/first-time-flow/first-time-flow.component.js
@@ -30,6 +30,9 @@ export default class FirstTimeFlow extends PureComponent {
isUnlocked: PropTypes.bool,
unlockAccount: PropTypes.func,
nextRoute: PropTypes.string,
+ showingSeedPhraseBackupAfterOnboarding: PropTypes.bool,
+ seedPhraseBackedUp: PropTypes.bool,
+ verifySeedPhrase: PropTypes.func,
}
state = {
@@ -38,9 +41,16 @@ export default class FirstTimeFlow extends PureComponent {
}
componentDidMount () {
- const { completedOnboarding, history, isInitialized, isUnlocked } = this.props
+ const {
+ completedOnboarding,
+ history,
+ isInitialized,
+ isUnlocked,
+ showingSeedPhraseBackupAfterOnboarding,
+ seedPhraseBackedUp,
+ } = this.props
- if (completedOnboarding) {
+ if (completedOnboarding && (!showingSeedPhraseBackupAfterOnboarding || seedPhraseBackedUp)) {
history.push(DEFAULT_ROUTE)
return
}
@@ -88,6 +98,7 @@ export default class FirstTimeFlow extends PureComponent {
render () {
const { seedPhrase, isImportedKeyring } = this.state
+ const { verifySeedPhrase } = this.props
return (
<div className="first-time-flow">
@@ -98,6 +109,7 @@ export default class FirstTimeFlow extends PureComponent {
<SeedPhrase
{ ...props }
seedPhrase={seedPhrase}
+ verifySeedPhrase={verifySeedPhrase}
/>
)}
/>
diff --git a/ui/app/pages/first-time-flow/first-time-flow.container.js b/ui/app/pages/first-time-flow/first-time-flow.container.js
index 16025a489..76fd12bcd 100644
--- a/ui/app/pages/first-time-flow/first-time-flow.container.js
+++ b/ui/app/pages/first-time-flow/first-time-flow.container.js
@@ -5,16 +5,19 @@ import {
createNewVaultAndGetSeedPhrase,
createNewVaultAndRestore,
unlockAndGetSeedPhrase,
+ verifySeedPhrase,
} from '../../store/actions'
const mapStateToProps = state => {
- const { metamask: { completedOnboarding, isInitialized, isUnlocked } } = state
+ const { metamask: { completedOnboarding, isInitialized, isUnlocked, seedPhraseBackedUp }, appState: { showingSeedPhraseBackupAfterOnboarding } } = state
return {
completedOnboarding,
isInitialized,
isUnlocked,
nextRoute: getFirstTimeFlowTypeRoute(state),
+ showingSeedPhraseBackupAfterOnboarding,
+ seedPhraseBackedUp,
}
}
@@ -25,6 +28,7 @@ const mapDispatchToProps = dispatch => {
return dispatch(createNewVaultAndRestore(password, seedPhrase))
},
unlockAccount: password => dispatch(unlockAndGetSeedPhrase(password)),
+ verifySeedPhrase: () => verifySeedPhrase(),
}
}
diff --git a/ui/app/pages/first-time-flow/index.scss b/ui/app/pages/first-time-flow/index.scss
index 6c65cfdae..dec80cb60 100644
--- a/ui/app/pages/first-time-flow/index.scss
+++ b/ui/app/pages/first-time-flow/index.scss
@@ -26,6 +26,10 @@
.app-header__metafox-logo {
margin-bottom: 40px;
+
+ @media screen and (max-width: $break-small) {
+ margin-bottom: 0px;
+ }
}
}
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
index 4cfc38fdf..9256c3d8d 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
@@ -6,6 +6,7 @@ import Button from '../../../../components/ui/button'
import {
INITIALIZE_END_OF_FLOW_ROUTE,
INITIALIZE_SEED_PHRASE_ROUTE,
+ DEFAULT_ROUTE,
} from '../../../../helpers/constants/routes'
import { exportAsFile } from '../../../../helpers/utils/util'
import DraggableSeed from './draggable-seed.component'
@@ -88,7 +89,7 @@ export default class ConfirmSeedPhrase extends PureComponent {
}
handleSubmit = async () => {
- const { history } = this.props
+ const { history, setSeedPhraseBackedUp, showingSeedPhraseBackupAfterOnboarding, hideSeedPhraseBackupAfterOnboarding } = this.props
if (!this.isValid()) {
return
@@ -102,7 +103,15 @@ export default class ConfirmSeedPhrase extends PureComponent {
name: 'Verify Complete',
},
})
- history.push(INITIALIZE_END_OF_FLOW_ROUTE)
+
+ setSeedPhraseBackedUp(true).then(() => {
+ if (showingSeedPhraseBackupAfterOnboarding) {
+ hideSeedPhraseBackupAfterOnboarding()
+ history.push(DEFAULT_ROUTE)
+ } else {
+ history.push(INITIALIZE_END_OF_FLOW_ROUTE)
+ }
+ })
} catch (error) {
console.error(error.message)
}
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js
new file mode 100644
index 000000000..ac5a26979
--- /dev/null
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.js
@@ -0,0 +1,23 @@
+import { connect } from 'react-redux'
+import ConfirmSeedPhrase from './confirm-seed-phrase.component'
+import {
+ setSeedPhraseBackedUp,
+ hideSeedPhraseBackupAfterOnboarding,
+} from '../../../../store/actions'
+
+const mapStateToProps = state => {
+ const { appState: { showingSeedPhraseBackupAfterOnboarding } } = state
+
+ return {
+ showingSeedPhraseBackupAfterOnboarding,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ setSeedPhraseBackedUp: (seedPhraseBackupState) => dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)),
+ hideSeedPhraseBackupAfterOnboarding: () => dispatch(hideSeedPhraseBackupAfterOnboarding()),
+ }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ConfirmSeedPhrase)
diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js
index c7b511503..beb53b383 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js
+++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js
@@ -1 +1 @@
-export { default } from './confirm-seed-phrase.component'
+export { default } from './confirm-seed-phrase.container'
diff --git a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.js b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.js
index 4a1b191b5..a528f95a2 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.js
+++ b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.js
@@ -1 +1 @@
-export { default } from './reveal-seed-phrase.component'
+export { default } from './reveal-seed-phrase.container'
diff --git a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss
index 8a47447ed..dfe9868cf 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss
+++ b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss
@@ -1,4 +1,12 @@
.reveal-seed-phrase {
+ @media screen and (max-width: 576px) {
+ display: flex;
+ flex-direction: column;
+ width: 96%;
+ margin-left: 2%;
+ margin-right: 2%;
+ }
+
&__secret {
position: relative;
display: flex;
@@ -54,4 +62,12 @@
button {
margin-top: 0xp;
}
+
+ &__buttons {
+ display: flex;
+
+ .first-time-flow__button:last-of-type {
+ margin-left: 20px;
+ }
+ }
}
diff --git a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js
index 4e9948a0e..78981bae8 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js
+++ b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import LockIcon from '../../../../components/ui/lock-icon'
import Button from '../../../../components/ui/button'
-import { INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE } from '../../../../helpers/constants/routes'
+import { INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE, DEFAULT_ROUTE } from '../../../../helpers/constants/routes'
import { exportAsFile } from '../../../../helpers/utils/util'
export default class RevealSeedPhrase extends PureComponent {
@@ -15,6 +15,8 @@ export default class RevealSeedPhrase extends PureComponent {
static propTypes = {
history: PropTypes.object,
seedPhrase: PropTypes.string,
+ setSeedPhraseBackedUp: PropTypes.func,
+ setCompletedOnboarding: PropTypes.func,
}
state = {
@@ -45,6 +47,24 @@ export default class RevealSeedPhrase extends PureComponent {
history.push(INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE)
}
+ handleSkip = event => {
+ event.preventDefault()
+ const { history, setSeedPhraseBackedUp, setCompletedOnboarding } = this.props
+
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Seed Phrase Setup',
+ name: 'Remind me later',
+ },
+ })
+
+ Promise.all([setCompletedOnboarding(), setSeedPhraseBackedUp(false)])
+ .then(() => {
+ history.push(DEFAULT_ROUTE)
+ })
+ }
+
renderSecretWordsContainer () {
const { t } = this.context
const { seedPhrase } = this.props
@@ -129,14 +149,23 @@ export default class RevealSeedPhrase extends PureComponent {
</div>
</div>
</div>
- <Button
- type="primary"
- className="first-time-flow__button"
- onClick={this.handleNext}
- disabled={!isShowingSeedPhrase}
- >
- { t('next') }
- </Button>
+ <div className="reveal-seed-phrase__buttons">
+ <Button
+ type="secondary"
+ className="first-time-flow__button"
+ onClick={this.handleSkip}
+ >
+ { t('remindMeLater') }
+ </Button>
+ <Button
+ type="primary"
+ className="first-time-flow__button"
+ onClick={this.handleNext}
+ disabled={!isShowingSeedPhrase}
+ >
+ { t('next') }
+ </Button>
+ </div>
</div>
)
}
diff --git a/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.container.js b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.container.js
new file mode 100644
index 000000000..7ada36afc
--- /dev/null
+++ b/ui/app/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.container.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux'
+import RevealSeedPhrase from './reveal-seed-phrase.component'
+import {
+ setCompletedOnboarding,
+ setSeedPhraseBackedUp,
+} from '../../../../store/actions'
+
+const mapDispatchToProps = dispatch => {
+ return {
+ setSeedPhraseBackedUp: (seedPhraseBackupState) => dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)),
+ setCompletedOnboarding: () => dispatch(setCompletedOnboarding()),
+ }
+}
+
+export default connect(null, mapDispatchToProps)(RevealSeedPhrase)
diff --git a/ui/app/pages/first-time-flow/seed-phrase/seed-phrase.component.js b/ui/app/pages/first-time-flow/seed-phrase/seed-phrase.component.js
index f4557115a..79cb27c52 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/seed-phrase.component.js
+++ b/ui/app/pages/first-time-flow/seed-phrase/seed-phrase.component.js
@@ -17,18 +17,31 @@ export default class SeedPhrase extends PureComponent {
address: PropTypes.string,
history: PropTypes.object,
seedPhrase: PropTypes.string,
+ verifySeedPhrase: PropTypes.func,
+ }
+
+ state = {
+ verifiedSeedPhrase: '',
}
componentDidMount () {
- const { seedPhrase, history } = this.props
+ const { seedPhrase, history, verifySeedPhrase } = this.props
if (!seedPhrase) {
- history.push(DEFAULT_ROUTE)
+ verifySeedPhrase()
+ .then(verifiedSeedPhrase => {
+ if (!verifiedSeedPhrase) {
+ history.push(DEFAULT_ROUTE)
+ } else {
+ this.setState({ verifiedSeedPhrase })
+ }
+ })
}
}
render () {
const { seedPhrase } = this.props
+ const { verifiedSeedPhrase } = this.state
return (
<DragDropContextProvider backend={HTML5Backend}>
@@ -41,7 +54,7 @@ export default class SeedPhrase extends PureComponent {
render={props => (
<ConfirmSeedPhrase
{ ...props }
- seedPhrase={seedPhrase}
+ seedPhrase={seedPhrase || verifiedSeedPhrase}
/>
)}
/>
@@ -51,7 +64,7 @@ export default class SeedPhrase extends PureComponent {
render={props => (
<RevealSeedPhrase
{ ...props }
- seedPhrase={seedPhrase}
+ seedPhrase={seedPhrase || verifiedSeedPhrase}
/>
)}
/>
diff --git a/ui/app/pages/first-time-flow/seed-phrase/tests/confirm-seed-phrase-component.test.js b/ui/app/pages/first-time-flow/seed-phrase/tests/confirm-seed-phrase-component.test.js
index 8339a6f6f..3d5f7f066 100644
--- a/ui/app/pages/first-time-flow/seed-phrase/tests/confirm-seed-phrase-component.test.js
+++ b/ui/app/pages/first-time-flow/seed-phrase/tests/confirm-seed-phrase-component.test.js
@@ -131,7 +131,7 @@ describe('ConfirmSeedPhrase Component', () => {
assert.deepEqual(root.state().pendingSeedIndices, [2, 0, 1])
})
- it('should submit correctly', () => {
+ it('should submit correctly', async () => {
const originalSeed = ['鼠', '牛', '虎', '兔', '龍', '蛇', '馬', '羊', '猴', '雞', '狗', '豬']
const metricsEventSpy = sinon.spy()
const pushSpy = sinon.spy()
@@ -139,6 +139,7 @@ describe('ConfirmSeedPhrase Component', () => {
{
seedPhrase: '鼠 牛 虎 兔 龍 蛇 馬 羊 猴 雞 狗 豬',
history: { push: pushSpy },
+ setSeedPhraseBackedUp: () => Promise.resolve(),
},
{
metricsEvent: metricsEventSpy,
@@ -157,6 +158,9 @@ describe('ConfirmSeedPhrase Component', () => {
root.update()
root.find('.first-time-flow__button').simulate('click')
+
+ await (new Promise(resolve => setTimeout(resolve, 100)))
+
assert.deepEqual(metricsEventSpy.args[0][0], {
eventOpts: {
category: 'Onboarding',
diff --git a/ui/app/pages/home/home.component.js b/ui/app/pages/home/home.component.js
index 1fd12a359..e59106537 100644
--- a/ui/app/pages/home/home.component.js
+++ b/ui/app/pages/home/home.component.js
@@ -6,6 +6,7 @@ import HomeNotification from '../../components/app/home-notification'
import WalletView from '../../components/app/wallet-view'
import TransactionView from '../../components/app/transaction-view'
import ProviderApproval from '../provider-approval'
+import BackupNotification from '../../components/app/backup-notification'
import {
RESTORE_VAULT_ROUTE,
@@ -38,6 +39,7 @@ export default class Home extends PureComponent {
unsetMigratedPrivacyMode: PropTypes.func,
viewingUnconnectedDapp: PropTypes.bool.isRequired,
forceApproveProviderRequestByOrigin: PropTypes.func,
+ shouldShowSeedPhraseReminder: PropTypes.bool,
}
componentWillMount () {
@@ -74,6 +76,7 @@ export default class Home extends PureComponent {
unsetMigratedPrivacyMode,
viewingUnconnectedDapp,
forceApproveProviderRequestByOrigin,
+ shouldShowSeedPhraseReminder,
} = this.props
if (forgottenPassword) {
@@ -85,7 +88,6 @@ export default class Home extends PureComponent {
<ProviderApproval providerRequest={providerRequests[0]} />
)
}
-
return (
<div className="main-container">
<div className="account-and-transaction-details">
@@ -124,6 +126,11 @@ export default class Home extends PureComponent {
)
: null
}
+ {
+ shouldShowSeedPhraseReminder
+ ? (<BackupNotification />)
+ : null
+ }
</TransactionView>
)
: null }
diff --git a/ui/app/pages/home/home.container.js b/ui/app/pages/home/home.container.js
index 81a3946c5..4195fbe79 100644
--- a/ui/app/pages/home/home.container.js
+++ b/ui/app/pages/home/home.container.js
@@ -3,6 +3,7 @@ import { compose } from 'recompose'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { unconfirmedTransactionsCountSelector } from '../../selectors/confirm-transaction'
+import { getCurrentEthBalance } from '../../selectors/selectors'
import {
forceApproveProviderRequestByOrigin,
unsetMigratedPrivacyMode,
@@ -21,7 +22,9 @@ const mapStateToProps = state => {
featureFlags: {
privacyMode,
} = {},
+ seedPhraseBackedUp,
} = metamask
+ const accountBalance = getCurrentEthBalance(state)
const { forgottenPassword } = appState
const isUnconnected = Boolean(activeTab && privacyMode && !approvedOrigins[activeTab.origin])
@@ -36,6 +39,7 @@ const mapStateToProps = state => {
showPrivacyModeNotification: migratedPrivacyMode,
activeTab,
viewingUnconnectedDapp: isUnconnected && isPopup,
+ shouldShowSeedPhraseReminder: parseInt(accountBalance, 16) > 0 && !seedPhraseBackedUp,
}
}
diff --git a/ui/app/pages/settings/networks-tab/networks-tab.component.js b/ui/app/pages/settings/networks-tab/networks-tab.component.js
index a44872843..40e1a902f 100644
--- a/ui/app/pages/settings/networks-tab/networks-tab.component.js
+++ b/ui/app/pages/settings/networks-tab/networks-tab.component.js
@@ -126,6 +126,7 @@ export default class NetworksTab extends PureComponent {
renderNetworksList () {
const { networksToRender, selectedNetwork, networkIsSelected, networksTabIsInAddMode, networkDefaultedToProvider } = this.props
+
return (
<div
className={classnames('networks-tab__networks-list', {
diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js
index 726deb59d..9d8c7c6b2 100644
--- a/ui/app/store/actions.js
+++ b/ui/app/store/actions.js
@@ -375,6 +375,15 @@ var actions = {
LOADING_TOKEN_PARAMS_STARTED: 'LOADING_TOKEN_PARAMS_STARTED',
loadingTokenParamsFinished,
LOADING_TOKEN_PARAMS_FINISHED: 'LOADING_TOKEN_PARAMS_FINISHED',
+
+ setSeedPhraseBackedUp,
+ showSeedPhraseBackupAfterOnboarding,
+ SHOW_SEED_PHRASE_BACKUP_AFTER_ONBOARDING: 'SHOW_SEED_PHRASE_BACKUP_AFTER_ONBOARDING',
+ hideSeedPhraseBackupAfterOnboarding,
+ HIDE_SEED_PHRASE_BACKUP_AFTER_ONBOARDING: 'HIDE_SEED_PHRASE_BACKUP_AFTER_ONBOARDING',
+
+ verifySeedPhrase,
+ SET_SEED_PHRASE_BACKED_UP_TO_TRUE: 'SET_SEED_PHRASE_BACKED_UP_TO_TRUE',
}
module.exports = actions
@@ -2772,3 +2781,30 @@ function unsetMigratedPrivacyMode () {
background.unsetMigratedPrivacyMode()
}
}
+
+function setSeedPhraseBackedUp (seedPhraseBackupState) {
+ return (dispatch) => {
+ log.debug(`background.setSeedPhraseBackedUp`)
+ return new Promise((resolve, reject) => {
+ background.setSeedPhraseBackedUp(seedPhraseBackupState, (err) => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
+ return forceUpdateMetamaskState(dispatch).then(() => resolve())
+ })
+ })
+ }
+}
+
+function showSeedPhraseBackupAfterOnboarding () {
+ return {
+ type: actions.SHOW_SEED_PHRASE_BACKUP_AFTER_ONBOARDING,
+ }
+}
+
+function hideSeedPhraseBackupAfterOnboarding () {
+ return {
+ type: actions.HIDE_SEED_PHRASE_BACKUP_AFTER_ONBOARDING,
+ }
+}