diff --git a/development/build/scripts.js b/development/build/scripts.js
index da7e73e26..618485ad7 100644
--- a/development/build/scripts.js
+++ b/development/build/scripts.js
@@ -27,6 +27,7 @@ const bifyModuleGroups = require('bify-module-groups');
const metamaskrc = require('rc')('metamask', {
INFURA_PROJECT_ID: process.env.INFURA_PROJECT_ID,
+ ONBOARDING_V2: process.env.ONBOARDING_V2,
SEGMENT_HOST: process.env.SEGMENT_HOST,
SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,
SEGMENT_LEGACY_WRITE_KEY: process.env.SEGMENT_LEGACY_WRITE_KEY,
@@ -612,6 +613,7 @@ function getEnvironmentVariables({ buildType, devMode, testing }) {
? process.env.SEGMENT_PROD_LEGACY_WRITE_KEY
: metamaskrc.SEGMENT_LEGACY_WRITE_KEY,
SWAPS_USE_DEV_APIS: process.env.SWAPS_USE_DEV_APIS === '1',
+ ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1',
};
}
diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js
index b5867a1ff..e1314d5c2 100644
--- a/ui/ducks/metamask/metamask.js
+++ b/ui/ducks/metamask/metamask.js
@@ -325,3 +325,18 @@ export function getIsGasEstimatesLoading(state) {
return isGasEstimatesLoading;
}
+
+export function getCompletedOnboarding(state) {
+ return state.metamask.completedOnboarding;
+}
+export function getIsInitialized(state) {
+ return state.metamask.isInitialized;
+}
+
+export function getIsUnlocked(state) {
+ return state.metamask.isUnlocked;
+}
+
+export function getSeedPhraseBackedUp(state) {
+ return state.metamask.seedPhraseBackedUp;
+}
diff --git a/ui/helpers/constants/routes.js b/ui/helpers/constants/routes.js
index 800a4badb..1ae2ef494 100644
--- a/ui/helpers/constants/routes.js
+++ b/ui/helpers/constants/routes.js
@@ -52,6 +52,21 @@ const INITIALIZE_END_OF_FLOW_ROUTE = '/initialize/end-of-flow';
const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm';
const INITIALIZE_METAMETRICS_OPT_IN_ROUTE = '/initialize/metametrics-opt-in';
+const ONBOARDING_ROUTE = '/onboarding';
+const ONBOARDING_REVIEW_SRP_ROUTE = '/onboarding/review-srp';
+const ONBOARDING_CONFIRM_SRP_ROUTE = '/onboarding/confirm-srp';
+const ONBOARDING_CREATE_PASSWORD_ROUTE = '/onboarding/create-password';
+const ONBOARDING_COMPLETION_ROUTE = '/onboarding/completion';
+const ONBOARDING_UNLOCK_ROUTE = '/onboarding/unlock';
+const ONBOARDING_GET_STARTED_ROUTE = '/onboarding/get-started';
+const ONBOARDING_HELP_US_IMPROVE_ROUTE = '/onboarding/help-us-improve';
+const ONBOARDING_IMPORT_WITH_SRP_ROUTE =
+ '/onboarding/create-password/import-with-sre';
+const ONBOARDING_IMPORT_MOBILE_ROUTE = '/onboarding/create-password';
+const ONBOARDING_SECURE_YOUR_WALLET_ROUTE = '/onboarding/secure-your-wallet';
+const ONBOARDING_PRIVACY_SETTINGS_ROUTE = '/onboarding/privacy-settings';
+const ONBOARDING_PIN_EXTENSION_ROUTE = '/onboarding/pin-extension';
+
const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction';
const CONFIRM_SEND_ETHER_PATH = '/send-ether';
const CONFIRM_SEND_TOKEN_PATH = '/send-token';
@@ -199,4 +214,17 @@ export {
AWAITING_SIGNATURES_ROUTE,
SWAPS_ERROR_ROUTE,
SWAPS_MAINTENANCE_ROUTE,
+ ONBOARDING_ROUTE,
+ ONBOARDING_GET_STARTED_ROUTE,
+ ONBOARDING_HELP_US_IMPROVE_ROUTE,
+ ONBOARDING_CREATE_PASSWORD_ROUTE,
+ ONBOARDING_IMPORT_WITH_SRP_ROUTE,
+ ONBOARDING_IMPORT_MOBILE_ROUTE,
+ ONBOARDING_SECURE_YOUR_WALLET_ROUTE,
+ ONBOARDING_REVIEW_SRP_ROUTE,
+ ONBOARDING_CONFIRM_SRP_ROUTE,
+ ONBOARDING_PRIVACY_SETTINGS_ROUTE,
+ ONBOARDING_COMPLETION_ROUTE,
+ ONBOARDING_UNLOCK_ROUTE,
+ ONBOARDING_PIN_EXTENSION_ROUTE,
};
diff --git a/ui/helpers/higher-order-components/authenticated/authenticated.component.js b/ui/helpers/higher-order-components/authenticated/authenticated.component.js
index 2feebfdb0..1fe151047 100644
--- a/ui/helpers/higher-order-components/authenticated/authenticated.component.js
+++ b/ui/helpers/higher-order-components/authenticated/authenticated.component.js
@@ -1,16 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Redirect, Route } from 'react-router-dom';
-import { UNLOCK_ROUTE, INITIALIZE_ROUTE } from '../../constants/routes';
+import {
+ UNLOCK_ROUTE,
+ INITIALIZE_ROUTE,
+ ONBOARDING_ROUTE,
+} from '../../constants/routes';
export default function Authenticated(props) {
const { isUnlocked, completedOnboarding } = props;
-
switch (true) {
+ // For ONBOARDING_V2 dev purposes,
+ // Remove when ONBOARDING_V2 dev complete
+ case process.env.ONBOARDING_V2 === true:
+ return ;
+
case isUnlocked && completedOnboarding:
return ;
case !completedOnboarding:
- return ;
+ return (
+
+ );
default:
return ;
}
diff --git a/ui/helpers/higher-order-components/initialized/initialized.component.js b/ui/helpers/higher-order-components/initialized/initialized.component.js
index a953403fd..5773d605a 100644
--- a/ui/helpers/higher-order-components/initialized/initialized.component.js
+++ b/ui/helpers/higher-order-components/initialized/initialized.component.js
@@ -1,13 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Redirect, Route } from 'react-router-dom';
-import { INITIALIZE_ROUTE } from '../../constants/routes';
+import { INITIALIZE_ROUTE, ONBOARDING_ROUTE } from '../../constants/routes';
export default function Initialized(props) {
return props.completedOnboarding ? (
) : (
-
+
);
}
diff --git a/ui/pages/onboarding-flow/index.scss b/ui/pages/onboarding-flow/index.scss
new file mode 100644
index 000000000..aebae2fbd
--- /dev/null
+++ b/ui/pages/onboarding-flow/index.scss
@@ -0,0 +1,18 @@
+@import 'recovery-phrase/index';
+@import 'new-account/index';
+
+.onboarding-flow {
+ width: 100%;
+ background-color: $white;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ &__wrapper {
+ margin-top: 40px;
+ padding: 32px;
+ border: 1px solid $Grey-100;
+ border-radius: 20px;
+ }
+}
diff --git a/ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.js b/ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.js
new file mode 100644
index 000000000..3512f6f2f
--- /dev/null
+++ b/ui/pages/onboarding-flow/onboarding-flow-switch/onboarding-flow-switch.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { Redirect } from 'react-router-dom';
+import {
+ DEFAULT_ROUTE,
+ ONBOARDING_COMPLETION_ROUTE,
+ ONBOARDING_GET_STARTED_ROUTE,
+ ONBOARDING_UNLOCK_ROUTE,
+ LOCK_ROUTE,
+} from '../../../helpers/constants/routes';
+import {
+ getCompletedOnboarding,
+ getIsInitialized,
+ getIsUnlocked,
+ getSeedPhraseBackedUp,
+} from '../../../ducks/metamask/metamask';
+
+export default function OnboardingFlowSwitch() {
+ const completedOnboarding = useSelector(getCompletedOnboarding);
+ const isInitialized = useSelector(getIsInitialized);
+ const seedPhraseBackedUp = useSelector(getSeedPhraseBackedUp);
+ const isUnlocked = useSelector(getIsUnlocked);
+
+ if (completedOnboarding) {
+ return ;
+ }
+
+ if (seedPhraseBackedUp !== null) {
+ return ;
+ }
+
+ if (isUnlocked) {
+ return ;
+ }
+
+ if (!isInitialized) {
+ return ;
+ }
+
+ return ;
+}
diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js
new file mode 100644
index 000000000..fed13cbad
--- /dev/null
+++ b/ui/pages/onboarding-flow/onboarding-flow.js
@@ -0,0 +1,109 @@
+import React, { useEffect, useState } from 'react';
+import { Switch, Route, useHistory } from 'react-router-dom';
+import { useDispatch, useSelector } from 'react-redux';
+import Unlock from '../unlock-page';
+import {
+ ONBOARDING_CREATE_PASSWORD_ROUTE,
+ ONBOARDING_REVIEW_SRP_ROUTE,
+ ONBOARDING_CONFIRM_SRP_ROUTE,
+ ONBOARDING_UNLOCK_ROUTE,
+ DEFAULT_ROUTE,
+} from '../../helpers/constants/routes';
+import {
+ getCompletedOnboarding,
+ getIsInitialized,
+ getIsUnlocked,
+ getSeedPhraseBackedUp,
+} from '../../ducks/metamask/metamask';
+import {
+ createNewVaultAndGetSeedPhrase,
+ unlockAndGetSeedPhrase,
+} from '../../store/actions';
+import { getFirstTimeFlowTypeRoute } from '../../selectors';
+import OnboardingFlowSwitch from './onboarding-flow-switch/onboarding-flow-switch';
+import NewAccount from './new-account/new-account';
+import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase';
+import ConfirmRecoveryPhrase from './recovery-phrase/confirm-recovery-phrase';
+
+export default function OnboardingFlow() {
+ const [seedPhrase, setSeedPhrase] = useState('');
+ const dispatch = useDispatch();
+ const history = useHistory();
+ const isInitialized = useSelector(getIsInitialized);
+ const isUnlocked = useSelector(getIsUnlocked);
+ const completedOnboarding = useSelector(getCompletedOnboarding);
+ const seedPhraseBackedUp = useSelector(getSeedPhraseBackedUp);
+ const nextRoute = useSelector(getFirstTimeFlowTypeRoute);
+
+ useEffect(() => {
+ // For ONBOARDING_V2 dev purposes,
+ // Remove when ONBOARDING_V2 dev complete
+ if (process.env.ONBOARDING_V2) {
+ history.push(ONBOARDING_CREATE_PASSWORD_ROUTE);
+ return;
+ }
+
+ if (completedOnboarding && seedPhraseBackedUp) {
+ history.push(DEFAULT_ROUTE);
+ return;
+ }
+
+ if (isInitialized && !isUnlocked) {
+ history.push(ONBOARDING_UNLOCK_ROUTE);
+ }
+ }, [
+ history,
+ completedOnboarding,
+ isInitialized,
+ isUnlocked,
+ seedPhraseBackedUp,
+ ]);
+
+ const handleCreateNewAccount = async (password) => {
+ const newSeedPhrase = await dispatch(
+ createNewVaultAndGetSeedPhrase(password),
+ );
+ setSeedPhrase(newSeedPhrase);
+ };
+
+ const handleUnlock = async (password) => {
+ const retreivedSeedPhrase = await dispatch(
+ unlockAndGetSeedPhrase(password),
+ );
+ setSeedPhrase(retreivedSeedPhrase);
+ history.push(nextRoute);
+ };
+
+ return (
+
+
+
+ (
+
+ )}
+ />
+ }
+ />
+ }
+ />
+ (
+
+ )}
+ />
+
+
+
+
+ );
+}
diff --git a/ui/pages/pages.scss b/ui/pages/pages.scss
index 5cbc7932e..300bdcf4a 100644
--- a/ui/pages/pages.scss
+++ b/ui/pages/pages.scss
@@ -19,3 +19,4 @@
@import 'settings/index';
@import 'swaps/index';
@import 'unlock-page/index';
+@import 'onboarding-flow/index';
diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js
index 4b631c302..4c37ca5bb 100644
--- a/ui/pages/routes/routes.component.js
+++ b/ui/pages/routes/routes.component.js
@@ -53,6 +53,7 @@ import {
BUILD_QUOTE_ROUTE,
CONFIRMATION_V_NEXT_ROUTE,
CONFIRM_IMPORT_TOKEN_ROUTE,
+ ONBOARDING_ROUTE,
} from '../../helpers/constants/routes';
import {
@@ -62,6 +63,7 @@ import {
import { getEnvironmentType } from '../../../app/scripts/lib/util';
import { isBeta } from '../../helpers/utils/build-types';
import ConfirmationPage from '../confirmation';
+import OnboardingFlow from '../onboarding-flow/onboarding-flow';
export default class Routes extends Component {
static propTypes = {
@@ -114,9 +116,11 @@ export default class Routes extends Component {
renderRoutes() {
const { autoLockTimeLimit, setLastActiveTime } = this.props;
-
const routes = (
+ {process.env.ONBOARDING_V2 && (
+
+ )}
@@ -225,7 +229,7 @@ export default class Routes extends Component {
const isInitializing = Boolean(
matchPath(location.pathname, {
- path: INITIALIZE_ROUTE,
+ path: process.env.ONBOARDING_V2 ? ONBOARDING_ROUTE : INITIALIZE_ROUTE,
exact: false,
}),
);
diff --git a/ui/selectors/first-time-flow.js b/ui/selectors/first-time-flow.js
index b56bdc861..bbebfbd3a 100644
--- a/ui/selectors/first-time-flow.js
+++ b/ui/selectors/first-time-flow.js
@@ -2,6 +2,8 @@ import {
INITIALIZE_CREATE_PASSWORD_ROUTE,
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
DEFAULT_ROUTE,
+ ONBOARDING_CREATE_PASSWORD_ROUTE,
+ ONBOARDING_IMPORT_WITH_SRP_ROUTE,
} from '../helpers/constants/routes';
export function getFirstTimeFlowTypeRoute(state) {
@@ -9,9 +11,13 @@ export function getFirstTimeFlowTypeRoute(state) {
let nextRoute;
if (firstTimeFlowType === 'create') {
- nextRoute = INITIALIZE_CREATE_PASSWORD_ROUTE;
+ nextRoute = process.env.ONBOARDING_V2
+ ? ONBOARDING_CREATE_PASSWORD_ROUTE
+ : INITIALIZE_CREATE_PASSWORD_ROUTE;
} else if (firstTimeFlowType === 'import') {
- nextRoute = INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE;
+ nextRoute = process.env.ONBOARDING_V2
+ ? ONBOARDING_IMPORT_WITH_SRP_ROUTE
+ : INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE;
} else {
nextRoute = DEFAULT_ROUTE;
}