1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 11:22:43 +02:00

Implement Flask onboarding UI (#12745)

* Added Flask Experimental Area warning to OnboardingV2

* Added first time flow Flask Experimental Area warning

* Made both onboarding flows use one Experimental Area component

* Fix comments in React divs

* Fix unreachable code

* Fix build lint problems

* Changes after code review

* Added guards around route constants imports

* Code Review changes

* Update ui/components/app/flask/experimental-area/index.scss

Co-authored-by: George Marshall <george.marshall@consensys.net>

* Code review changes

* Fix lint

* Update ui/components/app/flask/experimental-area/index.scss

Co-authored-by: George Marshall <george.marshall@consensys.net>

* Update ui/components/app/flask/experimental-area/index.scss

Co-authored-by: George Marshall <george.marshall@consensys.net>

* Update ui/components/app/flask/experimental-area/index.scss

Co-authored-by: George Marshall <george.marshall@consensys.net>

* Update ui/components/app/flask/experimental-area/index.scss

Co-authored-by: George Marshall <george.marshall@consensys.net>

* Update ui/components/app/flask/experimental-area/index.scss

Co-authored-by: George Marshall <george.marshall@consensys.net>

* fix lint

Co-authored-by: George Marshall <george.marshall@consensys.net>
This commit is contained in:
Olaf Tomalka 2021-12-01 17:53:30 +01:00 committed by GitHub
parent 7a92e22111
commit 70386726f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 274 additions and 6 deletions

View File

@ -1056,6 +1056,21 @@
"message": "File import not working? Click here!",
"description": "Helps user import their account from a JSON file"
},
"flaskExperimentalText1": {
"message": "Using Flask can greatly increase your risk of fund loss:"
},
"flaskExperimentalText2": {
"message": "if you use it to install non-trustworthy Snaps"
},
"flaskExperimentalText3": {
"message": "if you do not review confirmations before approving changes"
},
"flaskExperimentalText4": {
"message": "if you interact with unfamiliar smart contracts"
},
"flaskExperimentalText5": {
"message": "Using Flask gives you much greater discretion in using the power of MetaMask, and that discretion is yours. Do you accept these risks as well as extra responsibility for your wallet's safety?"
},
"followUsOnTwitter": {
"message": "Follow us on Twitter"
},
@ -3217,6 +3232,9 @@
"usedByClients": {
"message": "Used by a variety of different clients"
},
"userAccepts": {
"message": "I accept"
},
"userName": {
"message": "Username"
},

View File

@ -29,7 +29,8 @@
},
"eslint-module-utils": {
"packages": {
"eslint-import-resolver-node": true
"eslint-import-resolver-node": true,
"@babel/eslint-parser": true
}
},
"node-sass": {

View File

@ -53,6 +53,7 @@
@import 'wallet-overview/index';
@import 'whats-new-popup/index';
@import 'loading-network-screen/index';
@import 'flask/experimental-area/index';
@import 'advanced-gas-fee-popover/index';
@import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/index';
@import 'advanced-gas-fee-popover/advanced-gas-fee-inputs/base-fee-input/index';

View File

@ -0,0 +1,97 @@
import React, { useContext } from 'react';
import { useHistory } from 'react-router-dom';
import PropTypes from 'prop-types';
import { I18nContext } from '../../../../contexts/i18n';
import Button from '../../../ui/button';
function lineBreaksToBr(source) {
return source.split('\n').map((value) => {
return (
<>
{value}
<br />
</>
);
});
}
const METAMASK_LOGO = lineBreaksToBr(`MMm*mmMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMmm*mMM
MM*./***mMMMMMMMMMMMMMMMMMMMMMMMMMMm***/.*MM
MM/...///*mMMMMMMMMMMMMMMMMMMMMMMm*///.../MM
Mm.....//../*mMMMMMMMMMMMMMMMMm*/..//.....mM
M*....../*....*mMMMMMMMMMMMMm*....*/......*M
M/........*.....*//////////*...../......../M
m..........*/...//........//.../*..........m
M/..........//.../......../...//........../M
M/.........../*/./.......//./*/.........../M
M*.............////......////.............*M
Mm...............**......**...............mM
Mm/...............*/..../*.............../mM
MM/............../*/..../*/............../MM
Mm..............//./...././/..............mM
MM*............*/../..../../*............*MM
MM/........../*..../..../....*/........../MM
MMm.........//...../..../.....//.........mMM
MMm......//**....../..../......**//......mMM
MMM/..////.*......./..../......././///../MMM
MMMm*//..../......./..../......./....//*mMMM
MMMm......*////////*....*////////*......mMMM
MMM*......*////////*....*////////*......*MMM
MMM/....../*......./..../.......*/....../MMM
MMm........**/./m*./..../.**/..**........mMM
MM*........//*mMMM///..///mMMm*//........*MM
MM/........././*mM*//..//*Mm*/./........./MM
Mm..........//.../**/../**/...//..........mM
M*...........*..../*/../*/..../...........*M
M*///////////*/.../m/../m/.../*///////////*M
M*.........../*/...m/../m.../*/...........*M
Mm.........../..//.*....*./*../...........mM
MM/........../...//******//.../........../MM
MM*........../....*MMMMMM*..../..........*MM
MMm........../....*MMMMMM*..../..........mMM
MMm/........//....*MMMMMM*....//......../mMM
MMM/....../*mm*...*mmmmmm*...*mm*/....../MMM
MMM*../*mmMMMMMm///......//*mMMMMMmm*/..*MMM
MMMm*mMMMMMMMMMMm**......**mMMMMMMMMMMm*mMMM
MMMMMMMMMMMMMMMMMm/....../mMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMmmmmmmmmMMMMMMMMMMMMMMMMMM`);
/* eslint-disable no-irregular-whitespace */
const EXPERIMENTAL_AREA = lineBreaksToBr(`█▄█ █▀█ █░█ ▀ █▀█ █▀▀   █▀▀ █▄░█ ▀█▀ █▀▀ █▀█ █ █▄░█ █▀▀   ▄▀█ █▄░█
`);
/* eslint-enable no-irregular-whitespace */
export default function ExperimentalArea({ redirectTo }) {
const t = useContext(I18nContext);
const history = useHistory();
const onClick = () => {
history.push(redirectTo);
};
return (
<div className="experimental-area">
<div className="logo">{METAMASK_LOGO}</div>
<div className="experimental-text">{EXPERIMENTAL_AREA}</div>
<div className="text">
{t('flaskExperimentalText1')}
<ul>
<li>{t('flaskExperimentalText2')}</li>
<li>{t('flaskExperimentalText3')}</li>
<li>{t('flaskExperimentalText4')}</li>
</ul>
{t('flaskExperimentalText5')}
</div>
<Button type="primary" onClick={onClick}>
{t('userAccepts')}
</Button>
</div>
);
}
ExperimentalArea.propTypes = {
redirectTo: PropTypes.string,
};

View File

@ -0,0 +1,13 @@
import React from 'react';
import ExperimentalArea from '.';
export default {
title: 'Components/App/Flask/ExperimentalArea',
id: __filename,
component: ExperimentalArea,
};
export const DefaultStory = () => <ExperimentalArea />;
DefaultStory.storyName = 'Default';

View File

@ -0,0 +1 @@
export { default } from './experimental-area';

View File

@ -0,0 +1,54 @@
.experimental-area {
color: $flask-purple;
margin: auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.logo {
padding: 16px 8px 0;
line-height: 0.625em;
font-family: monospace;
font-size: $font-size-h8;
margin-bottom: 32px;
}
.experimental-text {
padding: 16px 8px 0;
font-family: monospace;
font-size: $font-size-h5;
margin: auto;
line-height: 0.875em;
margin-bottom: 32px;
}
.text {
@include H5;
padding: 16px;
max-width: 670px;
}
ul {
padding: 16px 0;
}
li {
list-style-type: disc;
padding-left: 8px;
list-style-position: inside;
margin-bottom: 8px;
&:nth-last-child(1) {
margin-bottom: 0;
}
}
button {
background-color: $flask-purple !important;
border: 0 !important;
color: $ui-1;
width: 200px;
}
}

View File

@ -1,6 +1,5 @@
// These are the colors of the MetaMask design system
// Only design system colors should be added, no superfluous variables
// See https://bit.ly/32mnoja (link to figma design system)
$Blue-000: #eaf6ff;
$Blue-100: #a7d9fe;
$Blue-200: #75c4fd;
@ -112,6 +111,8 @@ $rinkeby: #f6c343;
$goerli: #3099f2;
$localhost: #29b6af;
$flask-purple: #8b45b6;
$color-map: (
'ui-1': $ui-1,
'ui-2': $ui-2,
@ -143,4 +144,5 @@ $color-map: (
'goerli': $goerli,
'localhost': $localhost,
'transparent': transparent,
'flask-purple': $flask-purple
);

View File

@ -70,6 +70,11 @@ const ONBOARDING_PIN_EXTENSION_ROUTE = '/onboarding/pin-extension';
const ONBOARDING_WELCOME_ROUTE = '/onboarding/welcome';
const ONBOARDING_METAMETRICS = '/onboarding/metametrics';
///: BEGIN:ONLY_INCLUDE_IN(flask)
const INITIALIZE_EXPERIMENTAL_AREA = '/initialize/experimental-area';
const ONBOARDING_EXPERIMENTAL_AREA = '/onboarding/experimental-area';
///: END:ONLY_INCLUDE_IN
const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction';
const CONFIRM_SEND_ETHER_PATH = '/send-ether';
const CONFIRM_SEND_TOKEN_PATH = '/send-token';
@ -234,4 +239,8 @@ export {
ONBOARDING_PIN_EXTENSION_ROUTE,
ONBOARDING_WELCOME_ROUTE,
ONBOARDING_METAMETRICS,
///: BEGIN:ONLY_INCLUDE_IN(flask)
INITIALIZE_EXPERIMENTAL_AREA,
ONBOARDING_EXPERIMENTAL_AREA,
///: END:ONLY_INCLUDE_IN
};

View File

@ -5,8 +5,13 @@ import {
DEFAULT_ROUTE,
LOCK_ROUTE,
INITIALIZE_END_OF_FLOW_ROUTE,
INITIALIZE_WELCOME_ROUTE,
INITIALIZE_UNLOCK_ROUTE,
///: BEGIN:ONLY_INCLUDE_IN(flask)
INITIALIZE_EXPERIMENTAL_AREA,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(main,beta)
INITIALIZE_WELCOME_ROUTE,
///: END:ONLY_INCLUDE_IN
} from '../../../helpers/constants/routes';
export default class FirstTimeFlowSwitch extends PureComponent {
@ -38,7 +43,16 @@ export default class FirstTimeFlowSwitch extends PureComponent {
}
if (!isInitialized) {
return <Redirect to={{ pathname: INITIALIZE_WELCOME_ROUTE }} />;
/* eslint-disable prefer-const */
let redirect;
///: BEGIN:ONLY_INCLUDE_IN(flask)
redirect = <Redirect to={{ pathname: INITIALIZE_EXPERIMENTAL_AREA }} />;
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(main,beta)
redirect = <Redirect to={{ pathname: INITIALIZE_WELCOME_ROUTE }} />;
///: END:ONLY_INCLUDE_IN
/* eslint-enable prefer-const */
return redirect;
}
return <Redirect to={{ pathname: INITIALIZE_UNLOCK_ROUTE }} />;

View File

@ -13,7 +13,13 @@ import {
INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
///: BEGIN:ONLY_INCLUDE_IN(flask)
INITIALIZE_EXPERIMENTAL_AREA,
///: END:ONLY_INCLUDE_IN
} from '../../helpers/constants/routes';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import ExperimentalArea from '../../components/app/flask/experimental-area';
///: END:ONLY_INCLUDE_IN
import FirstTimeFlowSwitch from './first-time-flow-switch';
import Welcome from './welcome';
import SelectAction from './select-action';
@ -162,6 +168,22 @@ export default class FirstTimeFlow extends PureComponent {
component={EndOfFlow}
/>
<Route exact path={INITIALIZE_WELCOME_ROUTE} component={Welcome} />
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
}
<Route
exact
path={INITIALIZE_EXPERIMENTAL_AREA}
render={(routeProps) => (
<ExperimentalArea
{...routeProps}
redirectTo={INITIALIZE_SELECT_ACTION_ROUTE}
/>
)}
/>
{
///: END:ONLY_INCLUDE_IN
}
<Route
exact
path={INITIALIZE_METAMETRICS_OPT_IN_ROUTE}

View File

@ -6,7 +6,12 @@ import {
ONBOARDING_COMPLETION_ROUTE,
ONBOARDING_UNLOCK_ROUTE,
LOCK_ROUTE,
ONBOARDING_WELCOME_ROUTE,
///: BEGIN:ONLY_INCLUDE_IN(flask)
ONBOARDING_EXPERIMENTAL_AREA, // eslint-disable-line no-unused-vars
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(main,beta)
ONBOARDING_WELCOME_ROUTE, // eslint-disable-line no-unused-vars
///: END:ONLY_INCLUDE_IN
} from '../../../helpers/constants/routes';
import {
getCompletedOnboarding,
@ -16,6 +21,7 @@ import {
} from '../../../ducks/metamask/metamask';
export default function OnboardingFlowSwitch() {
/* eslint-disable prefer-const */
const completedOnboarding = useSelector(getCompletedOnboarding);
const isInitialized = useSelector(getIsInitialized);
const seedPhraseBackedUp = useSelector(getSeedPhraseBackedUp);
@ -34,7 +40,16 @@ export default function OnboardingFlowSwitch() {
}
if (!isInitialized) {
return <Redirect to={{ pathname: ONBOARDING_WELCOME_ROUTE }} />;
let redirect;
/* eslint-disable prefer-const */
///: BEGIN:ONLY_INCLUDE_IN(flask)
redirect = <Redirect to={{ pathname: ONBOARDING_EXPERIMENTAL_AREA }} />;
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(main,beta)
redirect = <Redirect to={{ pathname: ONBOARDING_WELCOME_ROUTE }} />;
///: END:ONLY_INCLUDE_IN
/* eslint-enable prefer-const */
return redirect;
}
return <Redirect to={{ pathname: ONBOARDING_UNLOCK_ROUTE }} />;

View File

@ -3,6 +3,9 @@ import { Switch, Route, useHistory, useLocation } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import Unlock from '../unlock-page';
import {
///: BEGIN:ONLY_INCLUDE_IN(flask)
ONBOARDING_EXPERIMENTAL_AREA,
///: END:ONLY_INCLUDE_IN
ONBOARDING_CREATE_PASSWORD_ROUTE,
ONBOARDING_REVIEW_SRP_ROUTE,
ONBOARDING_CONFIRM_SRP_ROUTE,
@ -30,6 +33,9 @@ import {
import { getFirstTimeFlowTypeRoute } from '../../selectors';
import Button from '../../components/ui/button';
import { useI18nContext } from '../../hooks/useI18nContext';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import ExperimentalArea from '../../components/app/flask/experimental-area';
///: END:ONLY_INCLUDE_IN
import OnboardingFlowSwitch from './onboarding-flow-switch/onboarding-flow-switch';
import CreatePassword from './create-password/create-password';
import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase';
@ -161,6 +167,21 @@ export default function OnboardingFlow() {
path={ONBOARDING_METAMETRICS}
component={MetaMetricsComponent}
/>
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
}
<Route
path={ONBOARDING_EXPERIMENTAL_AREA}
render={(routeProps) => (
<ExperimentalArea
{...routeProps}
redirectTo={ONBOARDING_WELCOME_ROUTE}
/>
)}
/>
{
///: END:ONLY_INCLUDE_IN
}
<Route exact path="*" component={OnboardingFlowSwitch} />
</Switch>
</div>