aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components/pages/first-time-flow
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components/pages/first-time-flow')
-rw-r--r--ui/app/components/pages/first-time-flow/create-password/create-password.component.js17
-rw-r--r--ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js37
-rw-r--r--ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js39
-rw-r--r--ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js19
-rw-r--r--ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js37
-rw-r--r--ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js16
-rw-r--r--ui/app/components/pages/first-time-flow/end-of-flow/index.scss14
-rw-r--r--ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js9
-rw-r--r--ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js2
-rw-r--r--ui/app/components/pages/first-time-flow/first-time-flow.component.js12
-rw-r--r--ui/app/components/pages/first-time-flow/first-time-flow.container.js2
-rw-r--r--ui/app/components/pages/first-time-flow/first-time-flow.selectors.js26
-rw-r--r--ui/app/components/pages/first-time-flow/index.scss7
-rw-r--r--ui/app/components/pages/first-time-flow/metametrics-opt-in/index.js1
-rw-r--r--ui/app/components/pages/first-time-flow/metametrics-opt-in/index.scss136
-rw-r--r--ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js169
-rw-r--r--ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.container.js27
-rw-r--r--ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js13
-rw-r--r--ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js20
-rw-r--r--ui/app/components/pages/first-time-flow/select-action/index.js2
-rw-r--r--ui/app/components/pages/first-time-flow/select-action/index.scss3
-rw-r--r--ui/app/components/pages/first-time-flow/select-action/select-action.component.js22
-rw-r--r--ui/app/components/pages/first-time-flow/select-action/select-action.container.js23
-rw-r--r--ui/app/components/pages/first-time-flow/welcome/welcome.component.js12
-rw-r--r--ui/app/components/pages/first-time-flow/welcome/welcome.container.js3
25 files changed, 609 insertions, 59 deletions
diff --git a/ui/app/components/pages/first-time-flow/create-password/create-password.component.js b/ui/app/components/pages/first-time-flow/create-password/create-password.component.js
index 7cca82ca6..3faaa3764 100644
--- a/ui/app/components/pages/first-time-flow/create-password/create-password.component.js
+++ b/ui/app/components/pages/first-time-flow/create-password/create-password.component.js
@@ -3,18 +3,16 @@ import PropTypes from 'prop-types'
import { Switch, Route } from 'react-router-dom'
import NewAccount from './new-account'
import ImportWithSeedPhrase from './import-with-seed-phrase'
-import UniqueImage from './unique-image'
import {
INITIALIZE_CREATE_PASSWORD_ROUTE,
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
- INITIALIZE_UNIQUE_IMAGE_ROUTE,
+ INITIALIZE_END_OF_FLOW_ROUTE,
} from '../../../../routes'
export default class CreatePassword extends PureComponent {
static propTypes = {
history: PropTypes.object,
isInitialized: PropTypes.bool,
- isImportedKeyring: PropTypes.bool,
onCreateNewAccount: PropTypes.func,
onCreateNewAccountFromSeed: PropTypes.func,
}
@@ -23,12 +21,12 @@ export default class CreatePassword extends PureComponent {
const { isInitialized, history } = this.props
if (isInitialized) {
- history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
+ history.push(INITIALIZE_END_OF_FLOW_ROUTE)
}
}
render () {
- const { onCreateNewAccount, onCreateNewAccountFromSeed, isImportedKeyring } = this.props
+ const { onCreateNewAccount, onCreateNewAccountFromSeed } = this.props
return (
<div className="first-time-flow__wrapper">
@@ -46,15 +44,6 @@ export default class CreatePassword extends PureComponent {
/>
</div>
<Switch>
- <Route exact
- path={INITIALIZE_UNIQUE_IMAGE_ROUTE}
- render={props => (
- <UniqueImage
- { ...props }
- isImportedKeyring={isImportedKeyring}
- />
- )}
- />
<Route
exact
path={INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE}
diff --git a/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js b/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js
index 2e99147bb..6b3c03bb3 100644
--- a/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js
+++ b/ui/app/components/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js
@@ -5,12 +5,13 @@ import TextField from '../../../../text-field'
import Button from '../../../../button'
import {
INITIALIZE_SELECT_ACTION_ROUTE,
- INITIALIZE_UNIQUE_IMAGE_ROUTE,
+ INITIALIZE_END_OF_FLOW_ROUTE,
} from '../../../../../routes'
export default class ImportWithSeedPhrase extends PureComponent {
static contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
static propTypes = {
@@ -104,7 +105,14 @@ export default class ImportWithSeedPhrase extends PureComponent {
try {
await onSubmit(password, seedPhrase)
- history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Import Seed Phrase',
+ name: 'Import Complete',
+ },
+ })
+ history.push(INITIALIZE_END_OF_FLOW_ROUTE)
} catch (error) {
this.setState({ seedPhraseError: error.message })
}
@@ -132,6 +140,14 @@ export default class ImportWithSeedPhrase extends PureComponent {
}
toggleTermsCheck = () => {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Import Seed Phrase',
+ name: 'Check ToS',
+ },
+ })
+
this.setState((prevState) => ({
termsChecked: !prevState.termsChecked,
}))
@@ -150,6 +166,13 @@ export default class ImportWithSeedPhrase extends PureComponent {
<a
onClick={e => {
e.preventDefault()
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Import Seed Phrase',
+ name: 'Go Back from Onboarding Import',
+ },
+ })
this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE)
}}
href="#"
@@ -208,7 +231,15 @@ export default class ImportWithSeedPhrase extends PureComponent {
{termsChecked ? <i className="fa fa-check fa-2x" /> : null}
</div>
<span className="first-time-flow__checkbox-label">
- { t('agreeTermsOfService') }
+ I have read and agree to the <a
+ href="https://metamask.io/terms.html"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <span className="first-time-flow__link-text">
+ { 'Terms of Use' }
+ </span>
+ </a>
</span>
</div>
<Button
diff --git a/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js b/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js
index b82cba0c5..11d10e2d9 100644
--- a/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js
+++ b/ui/app/components/pages/first-time-flow/create-password/new-account/new-account.component.js
@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Button from '../../../../button'
import {
- INITIALIZE_UNIQUE_IMAGE_ROUTE,
+ INITIALIZE_SEED_PHRASE_ROUTE,
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
INITIALIZE_SELECT_ACTION_ROUTE,
} from '../../../../../routes'
@@ -10,6 +10,7 @@ import TextField from '../../../../text-field'
export default class NewAccount extends PureComponent {
static contextTypes = {
+ metricsEvent: PropTypes.func,
t: PropTypes.func,
}
@@ -99,7 +100,16 @@ export default class NewAccount extends PureComponent {
try {
await onSubmit(password)
- history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
+
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Create Password',
+ name: 'Submit Password',
+ },
+ })
+
+ history.push(INITIALIZE_SEED_PHRASE_ROUTE)
} catch (error) {
this.setState({ passwordError: error.message })
}
@@ -113,6 +123,14 @@ export default class NewAccount extends PureComponent {
}
toggleTermsCheck = () => {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Create Password',
+ name: 'Check ToS',
+ },
+ })
+
this.setState((prevState) => ({
termsChecked: !prevState.termsChecked,
}))
@@ -128,6 +146,13 @@ export default class NewAccount extends PureComponent {
<a
onClick={e => {
e.preventDefault()
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Create Password',
+ name: 'Go Back from Onboarding Create',
+ },
+ })
this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE)
}}
href="#"
@@ -174,7 +199,15 @@ export default class NewAccount extends PureComponent {
{termsChecked ? <i className="fa fa-check fa-2x" /> : null}
</div>
<span className="first-time-flow__checkbox-label">
- I agree to the Terms Of Service
+ I have read and agree to the <a
+ href="https://metamask.io/terms.html"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <span className="first-time-flow__link-text">
+ { 'Terms of Use' }
+ </span>
+ </a>
</span>
</div>
<Button
diff --git a/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js b/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js
index fa76074f5..cbc85c0e4 100644
--- a/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js
+++ b/ui/app/components/pages/first-time-flow/create-password/unique-image/unique-image.component.js
@@ -1,21 +1,21 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Button from '../../../../button'
-import { INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_END_OF_FLOW_ROUTE } from '../../../../../routes'
+import { INITIALIZE_END_OF_FLOW_ROUTE } from '../../../../../routes'
export default class UniqueImageScreen extends PureComponent {
static contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
static propTypes = {
history: PropTypes.object,
- isImportedKeyring: PropTypes.bool,
}
render () {
const { t } = this.context
- const { history, isImportedKeyring } = this.props
+ const { history } = this.props
return (
<div>
@@ -37,11 +37,14 @@ export default class UniqueImageScreen extends PureComponent {
type="confirm"
className="first-time-flow__button"
onClick={() => {
- if (isImportedKeyring) {
- history.push(INITIALIZE_END_OF_FLOW_ROUTE)
- } else {
- history.push(INITIALIZE_SEED_PHRASE_ROUTE)
- }
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Agree to Phishing Warning',
+ name: 'Agree to Phishing Warning',
+ },
+ })
+ history.push(INITIALIZE_END_OF_FLOW_ROUTE)
}}
>
{ t('next') }
diff --git a/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js
index 2ca5fd8ec..c0e2f59d9 100644
--- a/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js
+++ b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.component.js
@@ -6,16 +6,18 @@ import { DEFAULT_ROUTE } from '../../../../routes'
export default class EndOfFlowScreen extends PureComponent {
static contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
static propTypes = {
history: PropTypes.object,
completeOnboarding: PropTypes.func,
+ completionMetaMetricsName: PropTypes.string,
}
render () {
const { t } = this.context
- const { history, completeOnboarding } = this.props
+ const { history, completeOnboarding, completionMetaMetricsName } = this.props
return (
<div className="end-of-flow">
@@ -42,23 +44,44 @@ export default class EndOfFlowScreen extends PureComponent {
<div className="first-time-flow__text-block end-of-flow__text-2">
{ t('endOfFlowMessage2') }
</div>
- <div className="first-time-flow__text-block end-of-flow__text-3">
+ <div className="end-of-flow__text-3">
{ '• ' + t('endOfFlowMessage3') }
</div>
- <div className="first-time-flow__text-block end-of-flow__text-4">
+ <div className="end-of-flow__text-3">
{ '• ' + t('endOfFlowMessage4') }
</div>
- <div className="first-time-flow__text-block end-of-flow__text-3">
- { t('endOfFlowMessage5') }
+ <div className="end-of-flow__text-3">
+ { '• ' + t('endOfFlowMessage5') }
+ </div>
+ <div className="end-of-flow__text-3">
+ { '• ' + t('endOfFlowMessage6') }
</div>
- <div className="first-time-flow__text-block end-of-flow__text-3">
- { '*' + t('endOfFlowMessage6') }
+ <div className="end-of-flow__text-3">
+ { '• ' + t('endOfFlowMessage7') }
+ </div>
+ <div className="first-time-flow__text-block end-of-flow__text-4">
+ *MetaMask cannot recover your seedphrase. <a
+ href="https://metamask.zendesk.com/hc/en-us/articles/360015489591-Basic-Safety-Tips"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <span className="first-time-flow__link-text">
+ Learn More
+ </span>
+ </a>.
</div>
<Button
type="confirm"
className="first-time-flow__button"
onClick={async () => {
await completeOnboarding()
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Onboarding Complete',
+ name: completionMetaMetricsName,
+ },
+ })
history.push(DEFAULT_ROUTE)
}}
>
diff --git a/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js
index ffe2c0efb..91ae5a941 100644
--- a/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js
+++ b/ui/app/components/pages/first-time-flow/end-of-flow/end-of-flow.container.js
@@ -2,10 +2,24 @@ import { connect } from 'react-redux'
import EndOfFlow from './end-of-flow.component'
import { setCompletedOnboarding } from '../../../../actions'
+const firstTimeFlowTypeNameMap = {
+ create: 'New Wallet Created',
+ 'import': 'New Wallet Imported',
+}
+
+const mapStateToProps = ({ metamask }) => {
+ const { firstTimeFlowType } = metamask
+
+ return {
+ completionMetaMetricsName: firstTimeFlowTypeNameMap[firstTimeFlowType],
+ }
+}
+
+
const mapDispatchToProps = dispatch => {
return {
completeOnboarding: () => dispatch(setCompletedOnboarding()),
}
}
-export default connect(null, mapDispatchToProps)(EndOfFlow)
+export default connect(mapStateToProps, mapDispatchToProps)(EndOfFlow)
diff --git a/ui/app/components/pages/first-time-flow/end-of-flow/index.scss b/ui/app/components/pages/first-time-flow/end-of-flow/index.scss
index 5f5cc5991..d7eb4513b 100644
--- a/ui/app/components/pages/first-time-flow/end-of-flow/index.scss
+++ b/ui/app/components/pages/first-time-flow/end-of-flow/index.scss
@@ -24,12 +24,18 @@
margin-top: 26px;
}
- &__text-3 {
- margin-top: 26px;
+ &__text-3 {
+ margin-top: 2px;
+ margin-bottom: 2px;
+
+ @media screen and (max-width: $break-small) {
+ margin-bottom: 16px;
+ font-size: .875rem;
+ }
}
- &__text-3 {
- margin-top: 2px;
+ &__text-4 {
+ margin-top: 26px;
}
button {
diff --git a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js
index 43f792e06..5c2294393 100644
--- a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js
+++ b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.component.js
@@ -7,6 +7,7 @@ import {
INITIALIZE_WELCOME_ROUTE,
INITIALIZE_UNLOCK_ROUTE,
INITIALIZE_SEED_PHRASE_ROUTE,
+ INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
} from '../../../../routes'
export default class FirstTimeFlowSwitch extends PureComponent {
@@ -15,6 +16,7 @@ export default class FirstTimeFlowSwitch extends PureComponent {
isInitialized: PropTypes.bool,
isUnlocked: PropTypes.bool,
seedPhrase: PropTypes.string,
+ optInMetaMetrics: PropTypes.bool,
}
render () {
@@ -23,6 +25,7 @@ export default class FirstTimeFlowSwitch extends PureComponent {
isInitialized,
isUnlocked,
seedPhrase,
+ optInMetaMetrics,
} = this.props
if (completedOnboarding) {
@@ -45,6 +48,10 @@ export default class FirstTimeFlowSwitch extends PureComponent {
return <Redirect to={{ pathname: INITIALIZE_SEED_PHRASE_ROUTE }} />
}
- return <Redirect to={{ pathname: INITIALIZE_WELCOME_ROUTE }} />
+ if (optInMetaMetrics === null) {
+ return <Redirect to={{ pathname: INITIALIZE_WELCOME_ROUTE }} />
+ }
+
+ return <Redirect to={{ pathname: INITIALIZE_METAMETRICS_OPT_IN_ROUTE }} />
}
}
diff --git a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js
index e44c216c0..d68f7a153 100644
--- a/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js
+++ b/ui/app/components/pages/first-time-flow/first-time-flow-switch/first-time-flow-switch.container.js
@@ -6,12 +6,14 @@ const mapStateToProps = ({ metamask }) => {
completedOnboarding,
isInitialized,
isUnlocked,
+ participateInMetaMetrics: optInMetaMetrics,
} = metamask
return {
completedOnboarding,
isInitialized,
isUnlocked,
+ optInMetaMetrics,
}
}
diff --git a/ui/app/components/pages/first-time-flow/first-time-flow.component.js b/ui/app/components/pages/first-time-flow/first-time-flow.component.js
index 82308dda2..a1f629431 100644
--- a/ui/app/components/pages/first-time-flow/first-time-flow.component.js
+++ b/ui/app/components/pages/first-time-flow/first-time-flow.component.js
@@ -8,6 +8,7 @@ import EndOfFlow from './end-of-flow'
import Unlock from '../unlock-page'
import CreatePassword from './create-password'
import SeedPhrase from './seed-phrase'
+import MetaMetricsOptInScreen from './metametrics-opt-in'
import {
DEFAULT_ROUTE,
INITIALIZE_WELCOME_ROUTE,
@@ -16,6 +17,7 @@ import {
INITIALIZE_UNLOCK_ROUTE,
INITIALIZE_SELECT_ACTION_ROUTE,
INITIALIZE_END_OF_FLOW_ROUTE,
+ INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
} from '../../../routes'
export default class FirstTimeFlow extends PureComponent {
@@ -27,6 +29,7 @@ export default class FirstTimeFlow extends PureComponent {
isInitialized: PropTypes.bool,
isUnlocked: PropTypes.bool,
unlockAccount: PropTypes.func,
+ nextRoute: PropTypes.func,
}
state = {
@@ -71,12 +74,12 @@ export default class FirstTimeFlow extends PureComponent {
}
handleUnlock = async password => {
- const { unlockAccount, history } = this.props
+ const { unlockAccount, history, nextRoute } = this.props
try {
const seedPhrase = await unlockAccount(password)
this.setState({ seedPhrase }, () => {
- history.push(INITIALIZE_SEED_PHRASE_ROUTE)
+ history.push(nextRoute)
})
} catch (error) {
throw new Error(error.message)
@@ -134,6 +137,11 @@ export default class FirstTimeFlow extends PureComponent {
/>
<Route
exact
+ path={INITIALIZE_METAMETRICS_OPT_IN_ROUTE}
+ component={MetaMetricsOptInScreen}
+ />
+ <Route
+ exact
path="*"
component={FirstTimeFlowSwitch}
/>
diff --git a/ui/app/components/pages/first-time-flow/first-time-flow.container.js b/ui/app/components/pages/first-time-flow/first-time-flow.container.js
index 1419dd59f..293f94c47 100644
--- a/ui/app/components/pages/first-time-flow/first-time-flow.container.js
+++ b/ui/app/components/pages/first-time-flow/first-time-flow.container.js
@@ -1,5 +1,6 @@
import { connect } from 'react-redux'
import FirstTimeFlow from './first-time-flow.component'
+import { getFirstTimeFlowTypeRoute } from './first-time-flow.selectors'
import {
createNewVaultAndGetSeedPhrase,
createNewVaultAndRestore,
@@ -13,6 +14,7 @@ const mapStateToProps = state => {
completedOnboarding,
isInitialized,
isUnlocked,
+ nextRoute: getFirstTimeFlowTypeRoute(state),
}
}
diff --git a/ui/app/components/pages/first-time-flow/first-time-flow.selectors.js b/ui/app/components/pages/first-time-flow/first-time-flow.selectors.js
new file mode 100644
index 000000000..1286afed9
--- /dev/null
+++ b/ui/app/components/pages/first-time-flow/first-time-flow.selectors.js
@@ -0,0 +1,26 @@
+import {
+ INITIALIZE_CREATE_PASSWORD_ROUTE,
+ INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
+ DEFAULT_ROUTE,
+} from '../../../routes'
+
+const selectors = {
+ getFirstTimeFlowTypeRoute,
+}
+
+module.exports = selectors
+
+function getFirstTimeFlowTypeRoute (state) {
+ const { firstTimeFlowType } = state.metamask
+
+ let nextRoute
+ if (firstTimeFlowType === 'create') {
+ nextRoute = INITIALIZE_CREATE_PASSWORD_ROUTE
+ } else if (firstTimeFlowType === 'import') {
+ nextRoute = INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE
+ } else {
+ nextRoute = DEFAULT_ROUTE
+ }
+
+ return nextRoute
+}
diff --git a/ui/app/components/pages/first-time-flow/index.scss b/ui/app/components/pages/first-time-flow/index.scss
index e14d57f58..d41748575 100644
--- a/ui/app/components/pages/first-time-flow/index.scss
+++ b/ui/app/components/pages/first-time-flow/index.scss
@@ -6,6 +6,9 @@
@import './end-of-flow/index';
+@import './metametrics-opt-in/index';
+
+
.first-time-flow {
width: 100%;
background-color: $white;
@@ -149,4 +152,8 @@
color: #939090;
margin-left: 18px;
}
+
+ &__link-text {
+ color: $curious-blue;
+ }
}
diff --git a/ui/app/components/pages/first-time-flow/metametrics-opt-in/index.js b/ui/app/components/pages/first-time-flow/metametrics-opt-in/index.js
new file mode 100644
index 000000000..4bc2fc3a7
--- /dev/null
+++ b/ui/app/components/pages/first-time-flow/metametrics-opt-in/index.js
@@ -0,0 +1 @@
+export { default } from './metametrics-opt-in.container'
diff --git a/ui/app/components/pages/first-time-flow/metametrics-opt-in/index.scss b/ui/app/components/pages/first-time-flow/metametrics-opt-in/index.scss
new file mode 100644
index 000000000..6c2e37785
--- /dev/null
+++ b/ui/app/components/pages/first-time-flow/metametrics-opt-in/index.scss
@@ -0,0 +1,136 @@
+.metametrics-opt-in {
+ position: relative;
+ width: 100%;
+
+ a {
+ color: #2f9ae0bf;
+ }
+
+ &__main {
+ display: flex;
+ flex-direction: column;
+ margin-left: 26.26%;
+ margin-right: 28%;
+ color: black;
+
+ @media screen and (max-width: 575px) {
+ justify-content: center;
+ margin-left: 2%;
+ margin-right: 0%;
+ }
+
+ .app-header__logo-container {
+ margin-top: 3%;
+ }
+ }
+
+ &__title {
+ position: relative;
+ margin-top: 20px;
+
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ line-height: normal;
+ font-size: 42px;
+ }
+
+ &__body-graphic {
+ margin-top: 25px;
+
+ .fa-bar-chart {
+ color: #C4C4C4;
+ }
+ }
+
+ &__description {
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: normal;
+ line-height: 21px;
+ font-size: 16px;
+ margin-top: 12px;
+ }
+
+ &__committments {
+ display: flex;
+ flex-direction: column;
+ }
+
+ &__content {
+ overflow-y: scroll;
+ flex: 1;
+ }
+
+ &__row {
+ display: flex;
+ margin-top: 8px;
+
+ .fa-check {
+ margin-right: 12px;
+ color: #1ACC56;
+ }
+
+ .fa-times {
+ margin-right: 12px;
+ color: #D0021B;
+ }
+ }
+
+ &__bold {
+ font-weight: bold;
+ }
+
+ &__break-row {
+ margin-top: 30px;
+ }
+
+ &__body {
+ position: relative;
+ display: flex;
+ max-width: 730px;
+ flex-direction: column;
+ }
+
+ &__body-text {
+ max-width: 548px;
+ margin-left: 16px;
+ margin-right: 16px;
+ }
+
+ &__bottom-text {
+ margin-top: 10px;
+ color: #9a9a9a;
+ }
+
+ &__content {
+ overflow-y: auto;
+ }
+
+ &__footer {
+ margin-top: 26px;
+
+ @media screen and (max-width: 575px) {
+ margin-top: 10px;
+ justify-content: center;
+ margin-left: 2%;
+ max-height: 520px;
+ }
+
+ .page-container__footer {
+ border-top: none;
+ max-width: 535px;
+ margin-bottom: 15px;
+
+ button {
+ height: 44px;
+ min-height: 44px;
+ margin-right: 16px;
+ }
+
+ header {
+ padding: 0px;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js b/ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js
new file mode 100644
index 000000000..834516f5f
--- /dev/null
+++ b/ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js
@@ -0,0 +1,169 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import PageContainerFooter from '../../../page-container/page-container-footer'
+
+export default class MetaMetricsOptIn extends Component {
+ static propTypes = {
+ history: PropTypes.object,
+ setParticipateInMetaMetrics: PropTypes.func,
+ nextRoute: PropTypes.string,
+ firstTimeSelectionMetaMetricsName: PropTypes.string,
+ participateInMetaMetrics: PropTypes.bool,
+ }
+
+ static contextTypes = {
+ metricsEvent: PropTypes.func,
+ }
+
+ render () {
+ const { metricsEvent } = this.context
+ const {
+ nextRoute,
+ history,
+ setParticipateInMetaMetrics,
+ firstTimeSelectionMetaMetricsName,
+ participateInMetaMetrics,
+ } = this.props
+
+ return (
+ <div className="metametrics-opt-in">
+ <div className="metametrics-opt-in__main">
+ <div className="app-header__logo-container">
+ <img
+ className="app-header__metafox-logo app-header__metafox-logo--horizontal"
+ src="/images/logo/metamask-logo-horizontal.svg"
+ height={30}
+ />
+ <img
+ className="app-header__metafox-logo app-header__metafox-logo--icon"
+ src="/images/logo/metamask-fox.svg"
+ height={42}
+ width={42}
+ />
+ </div>
+ <div className="metametrics-opt-in__body-graphic">
+ <img src="images/metrics-chart.svg" />
+ </div>
+ <div className="metametrics-opt-in__title">Help Us Improve MetaMask</div>
+ <div className="metametrics-opt-in__body">
+ <div className="metametrics-opt-in__description">
+ MetaMask would like to gather usage data to better understand how our users interact with the extension. This data
+ will be used to continually improve the usability and user experience of our product and the Etheruem ecosystem.
+ </div>
+ <div className="metametrics-opt-in__description">
+ MetaMask will..
+ </div>
+
+ <div className="metametrics-opt-in__committments">
+ <div className="metametrics-opt-in__row">
+ <i className="fa fa-check" />
+ <div className="metametrics-opt-in__row-description">
+ Always allow you to opt-out via Settings
+ </div>
+ </div>
+ <div className="metametrics-opt-in__row">
+ <i className="fa fa-check" />
+ <div className="metametrics-opt-in__row-description">
+ Send anonymized click & pageview events
+ </div>
+ </div>
+ <div className="metametrics-opt-in__row">
+ <i className="fa fa-check" />
+ <div className="metametrics-opt-in__row-description">
+ Maintain a public aggregate dashboard to educate the community
+ </div>
+ </div>
+ <div className="metametrics-opt-in__row metametrics-opt-in__break-row">
+ <i className="fa fa-times" />
+ <div className="metametrics-opt-in__row-description">
+ <span className="metametrics-opt-in__bold">Never</span> collect keys, addresses, transactions, balances, hashes, or any personal information
+ </div>
+ </div>
+ <div className="metametrics-opt-in__row">
+ <i className="fa fa-times" />
+ <div className="metametrics-opt-in__row-description">
+ <span className="metametrics-opt-in__bold">Never</span> collect your full IP address
+ </div>
+ </div>
+ <div className="metametrics-opt-in__row">
+ <i className="fa fa-times" />
+ <div className="metametrics-opt-in__row-description">
+ <span className="metametrics-opt-in__bold">Never</span> sell data for profit. Ever!
+ </div>
+ </div>
+ </div>
+ </div>
+ <div className="metametrics-opt-in__footer">
+ <PageContainerFooter
+ onCancel={() => {
+ setParticipateInMetaMetrics(false)
+ .then(() => {
+ if (participateInMetaMetrics === null) {
+ return metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Metrics Option',
+ name: 'Metrics Opt Out',
+ },
+ isOptIn: true,
+ }, {
+ excludeMetaMetricsId: true,
+ })
+ .then(() => {
+ history.push(nextRoute)
+ })
+ }
+ })
+ }}
+ cancelText={'No Thanks'}
+ hideCancel={false}
+ onSubmit={() => {
+ setParticipateInMetaMetrics(true)
+ .then(([participateStatus, metaMetricsId]) => {
+ const promise = participateInMetaMetrics === null
+ ? metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Metrics Option',
+ name: 'Metrics Opt In',
+ },
+ isOptIn: true,
+ })
+ : Promise.resolve()
+
+ promise
+ .then(() => {
+ return metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Import or Create',
+ name: firstTimeSelectionMetaMetricsName,
+ },
+ isOptIn: true,
+ metaMetricsId,
+ })
+ })
+ .then(() => {
+ history.push(nextRoute)
+ })
+ })
+ }}
+ submitText={'I agree'}
+ submitButtonType={'confirm'}
+ disabled={false}
+ />
+ <div className="metametrics-opt-in__bottom-text">
+ This data is aggregated and is therefore anonymous for the purposes of General Data Protection Regulation (EU) 2016/679. For more information in relation to our privacy practices, please see our <a
+ href="https://metamask.io/privacy.html"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ Privacy Policy here
+ </a>.
+ </div>
+ </div>
+ </div>
+ </div>
+ )
+ }
+}
diff --git a/ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.container.js b/ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.container.js
new file mode 100644
index 000000000..b13af8bf6
--- /dev/null
+++ b/ui/app/components/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.container.js
@@ -0,0 +1,27 @@
+import { connect } from 'react-redux'
+import MetaMetricsOptIn from './metametrics-opt-in.component'
+import { setParticipateInMetaMetrics } from '../../../../actions'
+import { getFirstTimeFlowTypeRoute } from '../first-time-flow.selectors'
+
+const firstTimeFlowTypeNameMap = {
+ create: 'Selected Create New Wallet',
+ 'import': 'Selected Import Wallet',
+}
+
+const mapStateToProps = (state) => {
+ const { firstTimeFlowType, participateInMetaMetrics } = state.metamask
+
+ return {
+ nextRoute: getFirstTimeFlowTypeRoute(state),
+ firstTimeSelectionMetaMetricsName: firstTimeFlowTypeNameMap[firstTimeFlowType],
+ participateInMetaMetrics,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)),
+ }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(MetaMetricsOptIn)
diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
index b5c4bf463..bd5ab8a84 100644
--- a/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
+++ b/ui/app/components/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js
@@ -3,12 +3,16 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import shuffle from 'lodash.shuffle'
import Button from '../../../../button'
-import { INITIALIZE_END_OF_FLOW_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE } from '../../../../../routes'
+import {
+ INITIALIZE_END_OF_FLOW_ROUTE,
+ INITIALIZE_SEED_PHRASE_ROUTE,
+} from '../../../../../routes'
import { exportAsFile } from '../../../../../../app/util'
import { selectSeedWord, deselectSeedWord } from './confirm-seed-phrase.state'
export default class ConfirmSeedPhrase extends PureComponent {
static contextTypes = {
+ metricsEvent: PropTypes.func,
t: PropTypes.func,
}
@@ -47,6 +51,13 @@ export default class ConfirmSeedPhrase extends PureComponent {
}
try {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Seed Phrase Setup',
+ name: 'Verify Complete',
+ },
+ })
history.push(INITIALIZE_END_OF_FLOW_ROUTE)
} catch (error) {
console.error(error.message)
diff --git a/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js b/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js
index 732ce14af..cb8a01322 100644
--- a/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js
+++ b/ui/app/components/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js
@@ -9,6 +9,7 @@ import { exportAsFile } from '../../../../../../app/util'
export default class RevealSeedPhrase extends PureComponent {
static contextTypes = {
t: PropTypes.func,
+ metricsEvent: PropTypes.func,
}
static propTypes = {
@@ -29,6 +30,14 @@ export default class RevealSeedPhrase extends PureComponent {
const { isShowingSeedPhrase } = this.state
const { history } = this.props
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Seed Phrase Setup',
+ name: 'Advance to Verify',
+ },
+ })
+
if (!isShowingSeedPhrase) {
return
}
@@ -53,7 +62,16 @@ export default class RevealSeedPhrase extends PureComponent {
!isShowingSeedPhrase && (
<div
className="reveal-seed-phrase__secret-blocker"
- onClick={() => this.setState({ isShowingSeedPhrase: true })}
+ onClick={() => {
+ this.context.metricsEvent({
+ eventOpts: {
+ category: 'Onboarding',
+ action: 'Seed Phrase Setup',
+ name: 'Revealed Words',
+ },
+ })
+ this.setState({ isShowingSeedPhrase: true })
+ }}
>
<LockIcon
width="28px"
diff --git a/ui/app/components/pages/first-time-flow/select-action/index.js b/ui/app/components/pages/first-time-flow/select-action/index.js
index 3aa968834..4fbe1823b 100644
--- a/ui/app/components/pages/first-time-flow/select-action/index.js
+++ b/ui/app/components/pages/first-time-flow/select-action/index.js
@@ -1 +1 @@
-export { default } from './select-action.component'
+export { default } from './select-action.container'
diff --git a/ui/app/components/pages/first-time-flow/select-action/index.scss b/ui/app/components/pages/first-time-flow/select-action/index.scss
index b9585eb3b..e1b22d05b 100644
--- a/ui/app/components/pages/first-time-flow/select-action/index.scss
+++ b/ui/app/components/pages/first-time-flow/select-action/index.scss
@@ -32,7 +32,7 @@
flex-direction: column;
align-items: center;
justify-content: space-evenly;
- width: 269px;
+ width: 388px;
height: 278px;
border: 1px solid #D8D8D8;
@@ -78,6 +78,7 @@
font-size: 14px;
color: #7A7A7B;
margin-top: 10px;
+ text-align: center;
}
button {
diff --git a/ui/app/components/pages/first-time-flow/select-action/select-action.component.js b/ui/app/components/pages/first-time-flow/select-action/select-action.component.js
index 385efe02a..b6a6942c3 100644
--- a/ui/app/components/pages/first-time-flow/select-action/select-action.component.js
+++ b/ui/app/components/pages/first-time-flow/select-action/select-action.component.js
@@ -2,15 +2,15 @@ import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Button from '../../../button'
import {
- INITIALIZE_CREATE_PASSWORD_ROUTE,
- INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
- INITIALIZE_UNIQUE_IMAGE_ROUTE,
+ INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
} from '../../../../routes'
export default class SelectAction extends PureComponent {
static propTypes = {
history: PropTypes.object,
isInitialized: PropTypes.bool,
+ setFirstTimeFlowType: PropTypes.func,
+ nextRoute: PropTypes.string,
}
static contextTypes = {
@@ -18,19 +18,21 @@ export default class SelectAction extends PureComponent {
}
componentDidMount () {
- const { history, isInitialized } = this.props
+ const { history, isInitialized, nextRoute } = this.props
if (isInitialized) {
- history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
+ history.push(nextRoute)
}
}
handleCreate = () => {
- this.props.history.push(INITIALIZE_CREATE_PASSWORD_ROUTE)
+ this.props.setFirstTimeFlowType('create')
+ this.props.history.push(INITIALIZE_METAMETRICS_OPT_IN_ROUTE)
}
handleImport = () => {
- this.props.history.push(INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE)
+ this.props.setFirstTimeFlowType('import')
+ this.props.history.push(INITIALIZE_METAMETRICS_OPT_IN_ROUTE)
}
render () {
@@ -68,6 +70,9 @@ export default class SelectAction extends PureComponent {
<div className="select-action__button-text-big">
{ t('noAlreadyHaveSeed') }
</div>
+ <div className="select-action__button-text-small">
+ { t('importYourExisting') }
+ </div>
</div>
<Button
type="primary"
@@ -85,6 +90,9 @@ export default class SelectAction extends PureComponent {
<div className="select-action__button-text-big">
{ t('letsGoSetUp') }
</div>
+ <div className="select-action__button-text-small">
+ { t('thisWillCreate') }
+ </div>
</div>
<Button
type="confirm"
diff --git a/ui/app/components/pages/first-time-flow/select-action/select-action.container.js b/ui/app/components/pages/first-time-flow/select-action/select-action.container.js
index e69de29bb..42fac7af2 100644
--- a/ui/app/components/pages/first-time-flow/select-action/select-action.container.js
+++ b/ui/app/components/pages/first-time-flow/select-action/select-action.container.js
@@ -0,0 +1,23 @@
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
+import { setFirstTimeFlowType } from '../../../../actions'
+import { getFirstTimeFlowTypeRoute } from '../first-time-flow.selectors'
+import Welcome from './select-action.component'
+
+const mapStateToProps = (state) => {
+ return {
+ nextRoute: getFirstTimeFlowTypeRoute(state),
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ setFirstTimeFlowType: type => dispatch(setFirstTimeFlowType(type)),
+ }
+}
+
+export default compose(
+ withRouter,
+ connect(mapStateToProps, mapDispatchToProps)
+)(Welcome)
diff --git a/ui/app/components/pages/first-time-flow/welcome/welcome.component.js b/ui/app/components/pages/first-time-flow/welcome/welcome.component.js
index 08eb86939..88cdb936c 100644
--- a/ui/app/components/pages/first-time-flow/welcome/welcome.component.js
+++ b/ui/app/components/pages/first-time-flow/welcome/welcome.component.js
@@ -3,12 +3,14 @@ import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Mascot from '../../../mascot'
import Button from '../../../button'
-import { INITIALIZE_SELECT_ACTION_ROUTE, INITIALIZE_UNIQUE_IMAGE_ROUTE } from '../../../../routes'
+import { INITIALIZE_CREATE_PASSWORD_ROUTE, INITIALIZE_SELECT_ACTION_ROUTE } from '../../../../routes'
export default class Welcome extends PureComponent {
static propTypes = {
history: PropTypes.object,
isInitialized: PropTypes.bool,
+ participateInMetaMetrics: PropTypes.bool,
+ welcomeScreenSeen: PropTypes.bool,
}
static contextTypes = {
@@ -22,10 +24,12 @@ export default class Welcome extends PureComponent {
}
componentDidMount () {
- const { history, isInitialized } = this.props
+ const { history, participateInMetaMetrics, welcomeScreenSeen } = this.props
- if (isInitialized) {
- history.push(INITIALIZE_UNIQUE_IMAGE_ROUTE)
+ if (welcomeScreenSeen && participateInMetaMetrics !== null) {
+ history.push(INITIALIZE_CREATE_PASSWORD_ROUTE)
+ } else if (welcomeScreenSeen) {
+ history.push(INITIALIZE_SELECT_ACTION_ROUTE)
}
}
diff --git a/ui/app/components/pages/first-time-flow/welcome/welcome.container.js b/ui/app/components/pages/first-time-flow/welcome/welcome.container.js
index 4362d89cb..47753e16f 100644
--- a/ui/app/components/pages/first-time-flow/welcome/welcome.container.js
+++ b/ui/app/components/pages/first-time-flow/welcome/welcome.container.js
@@ -5,11 +5,12 @@ import { closeWelcomeScreen } from '../../../../actions'
import Welcome from './welcome.component'
const mapStateToProps = ({ metamask }) => {
- const { welcomeScreenSeen, isInitialized } = metamask
+ const { welcomeScreenSeen, isInitialized, participateInMetaMetrics } = metamask
return {
welcomeScreenSeen,
isInitialized,
+ participateInMetaMetrics,
}
}