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

Revert "Revert "Adding recovery phrase video to onboarding process (#10717)""

This reverts commit 68c5defcdbb7cf3745c14c0023a1d3c37e657992.
This commit is contained in:
ryanml 2021-05-05 12:26:03 -07:00
parent 4c7b9cc92a
commit 24ad486d77
17 changed files with 379 additions and 7 deletions

View File

@ -1524,6 +1524,42 @@
"securitySettingsDescription": { "securitySettingsDescription": {
"message": "Privacy settings and wallet seed phrase" "message": "Privacy settings and wallet seed phrase"
}, },
"seedPhraseIntroSidebarBulletFour": {
"message": "Write down and store in multiple secret places."
},
"seedPhraseIntroSidebarBulletOne": {
"message": "Save in a password manager"
},
"seedPhraseIntroSidebarBulletThree": {
"message": "Store in a safe-deposit box."
},
"seedPhraseIntroSidebarBulletTwo": {
"message": "Store in a bank vault."
},
"seedPhraseIntroSidebarCopyOne": {
"message": "Your recovery phrase is the “master key” to your wallet and funds."
},
"seedPhraseIntroSidebarCopyThree": {
"message": "If someone asks for your recovery phrase, they are most likely trying to scam you."
},
"seedPhraseIntroSidebarCopyTwo": {
"message": "Never, ever share your recovery phrase, even with MetaMask!"
},
"seedPhraseIntroSidebarTitleOne": {
"message": "What is a recovery phrase?"
},
"seedPhraseIntroSidebarTitleThree": {
"message": "Should I share my recovery phrase?"
},
"seedPhraseIntroSidebarTitleTwo": {
"message": "How do I save my recovery phrase?"
},
"seedPhraseIntroTitle": {
"message": "Secure your wallet"
},
"seedPhraseIntroTitleCopy": {
"message": "Before getting started, watch this short video to learn about your recovery phrase and how to keep your wallet safe."
},
"seedPhrasePlaceholder": { "seedPhrasePlaceholder": {
"message": "Separate each word with a single space" "message": "Separate each word with a single space"
}, },

View File

@ -0,0 +1,116 @@
WEBVTT
1
00:00:00.780 --> 00:00:04.580
MetaMask is a new way to connect
to sites and applications.
2
00:00:04.580 --> 00:00:08.860
On traditional websites, a central database
or bank is responsible for controlling and
3
00:00:08.860 --> 00:00:10.179
recovering your accounts.
4
00:00:10.179 --> 00:00:15.050
But on MetaMask, all of the power belongs
to the holder of a master key.
5
00:00:15.050 --> 00:00:18.460
Whoever holds the key, controls the accounts.
6
00:00:18.460 --> 00:00:21.110
Your secret recovery phrase
is your "master key".
7
00:00:21.110 --> 00:00:26.070
It's a series of 12 words that are generated
when you first set up MetaMask, which allow
8
00:00:26.070 --> 00:00:30.120
you to recover your wallet and funds if you
ever lose access.
9
00:00:30.120 --> 00:00:33.451
It's important that you secure
your wallet by keeping your
10
00:00:33.451 --> 00:00:37.510
secret recovery phrase
very safe, and very secret.
11
00:00:37.510 --> 00:00:41.429
If anyone gets access to it, they will have
the "master key" to your wallet and can
12
00:00:41.429 --> 00:00:45.190
freely access and take all of your funds.
13
00:00:45.190 --> 00:00:50.109
To secure your MetaMask wallet you'll want
to safely save your secret recovery phrase.
14
00:00:50.109 --> 00:00:54.930
You can write it down, hide it somewhere,
put it in a safe deposit box
15
00:00:54.930 --> 00:00:57.729
or use a secure password manager.
16
00:00:57.729 --> 00:01:01.050
Some users even engrave their
phrase onto a metal plate!
17
00:01:01.050 --> 00:01:04.440
Nobody, not even the team
at MetaMask, can help you
18
00:01:04.440 --> 00:01:07.820
recover your wallet if you lose
your secret recovery phrase.
19
00:01:07.820 --> 00:01:12.072
If you haven't written down your secret recovery
phrase and stored it somewhere safe,
20
00:01:12.072 --> 00:01:15.492
do it now. We'll wait.
21
00:01:15.500 --> 00:01:20.780
And remember, never share your secret recovery
phrase with anyone: not even us.
22
00:01:20.780 --> 00:01:24.910
If anyone ever asks you for it,
they're trying to scam you.
23
00:01:24.910 --> 00:01:26.250
That's it!
24
00:01:26.250 --> 00:01:31.020
Now you know what a secret recovery phrase
is and how to keep your wallet safe and secure.

Binary file not shown.

View File

@ -83,6 +83,11 @@ describe('MetaMask', function () {
let seedPhrase; let seedPhrase;
it('renders the seed phrase intro screen', async function () {
await driver.clickElement('.seed-phrase-intro__left button');
await driver.delay(regularDelayMs);
});
it('reveals the seed phrase', async function () { it('reveals the seed phrase', async function () {
const byRevealButton = const byRevealButton =
'.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button'; '.reveal-seed-phrase__secret-blocker .reveal-seed-phrase__reveal-button';

View File

@ -9,6 +9,7 @@ import {
DISPLAY, DISPLAY,
JUSTIFY_CONTENT, JUSTIFY_CONTENT,
SIZES, SIZES,
TEXT_ALIGN,
} from '../../../helpers/constants/design-system'; } from '../../../helpers/constants/design-system';
const ValidSize = PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); const ValidSize = PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
@ -70,6 +71,7 @@ export default function Box({
borderStyle, borderStyle,
alignItems, alignItems,
justifyContent, justifyContent,
textAlign,
display, display,
width, width,
height, height,
@ -113,6 +115,8 @@ export default function Box({
!display && (Boolean(justifyContent) || Boolean(alignItems)), !display && (Boolean(justifyContent) || Boolean(alignItems)),
[`box--justify-content-${justifyContent}`]: Boolean(justifyContent), [`box--justify-content-${justifyContent}`]: Boolean(justifyContent),
[`box--align-items-${alignItems}`]: Boolean(alignItems), [`box--align-items-${alignItems}`]: Boolean(alignItems),
// text align
[`box--text-align-${textAlign}`]: Boolean(textAlign),
// display // display
[`box--display-${display}`]: Boolean(display), [`box--display-${display}`]: Boolean(display),
// width & height // width & height
@ -144,6 +148,7 @@ Box.propTypes = {
borderStyle: PropTypes.oneOf(Object.values(BORDER_STYLE)), borderStyle: PropTypes.oneOf(Object.values(BORDER_STYLE)),
alignItems: PropTypes.oneOf(Object.values(ALIGN_ITEMS)), alignItems: PropTypes.oneOf(Object.values(ALIGN_ITEMS)),
justifyContent: PropTypes.oneOf(Object.values(JUSTIFY_CONTENT)), justifyContent: PropTypes.oneOf(Object.values(JUSTIFY_CONTENT)),
textAlign: PropTypes.oneOf(Object.values(TEXT_ALIGN)),
display: PropTypes.oneOf(Object.values(DISPLAY)), display: PropTypes.oneOf(Object.values(DISPLAY)),
width: PropTypes.oneOf(Object.values(BLOCK_SIZES)), width: PropTypes.oneOf(Object.values(BLOCK_SIZES)),
height: PropTypes.oneOf(Object.values(BLOCK_SIZES)), height: PropTypes.oneOf(Object.values(BLOCK_SIZES)),

View File

@ -129,6 +129,8 @@ $attributes: padding, margin;
// text // text
@each $alignment in design-system.$text-align { @each $alignment in design-system.$text-align {
&--text-align-#{$alignment} {
text-align: $alignment; text-align: $alignment;
} }
} }
}

View File

@ -7,6 +7,7 @@ import {
COLORS, COLORS,
DISPLAY, DISPLAY,
JUSTIFY_CONTENT, JUSTIFY_CONTENT,
TEXT_ALIGN,
} from '../../../helpers/constants/design-system'; } from '../../../helpers/constants/design-system';
import Box from './box'; import Box from './box';
@ -38,6 +39,7 @@ export const box = () => {
undefined, undefined,
'display', 'display',
)} )}
textAlign={select('textAlign', TEXT_ALIGN, undefined, 'left')}
alignItems={select('alignItems', ALIGN_ITEMS, undefined, 'display')} alignItems={select('alignItems', ALIGN_ITEMS, undefined, 'display')}
margin={select('margin', sizeKnobOptions, undefined, 'margin')} margin={select('margin', sizeKnobOptions, undefined, 'margin')}
marginTop={select('marginTop', sizeKnobOptions, undefined, 'margin')} marginTop={select('marginTop', sizeKnobOptions, undefined, 'margin')}

View File

@ -45,6 +45,7 @@ const INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE =
const INITIALIZE_SELECT_ACTION_ROUTE = '/initialize/select-action'; const INITIALIZE_SELECT_ACTION_ROUTE = '/initialize/select-action';
const INITIALIZE_SEED_PHRASE_ROUTE = '/initialize/seed-phrase'; const INITIALIZE_SEED_PHRASE_ROUTE = '/initialize/seed-phrase';
const INITIALIZE_BACKUP_SEED_PHRASE_ROUTE = '/initialize/backup-seed-phrase'; const INITIALIZE_BACKUP_SEED_PHRASE_ROUTE = '/initialize/backup-seed-phrase';
const INITIALIZE_SEED_PHRASE_INTRO_ROUTE = '/initialize/seed-phrase-intro';
const INITIALIZE_END_OF_FLOW_ROUTE = '/initialize/end-of-flow'; const INITIALIZE_END_OF_FLOW_ROUTE = '/initialize/end-of-flow';
const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm'; const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm';
const INITIALIZE_METAMETRICS_OPT_IN_ROUTE = '/initialize/metametrics-opt-in'; const INITIALIZE_METAMETRICS_OPT_IN_ROUTE = '/initialize/metametrics-opt-in';
@ -118,6 +119,7 @@ const PATH_NAME_MAP = {
[INITIALIZE_SEED_PHRASE_ROUTE]: 'Initialization Seed Phrase Page', [INITIALIZE_SEED_PHRASE_ROUTE]: 'Initialization Seed Phrase Page',
[INITIALIZE_BACKUP_SEED_PHRASE_ROUTE]: [INITIALIZE_BACKUP_SEED_PHRASE_ROUTE]:
'Initialization Backup Seed Phrase Page', 'Initialization Backup Seed Phrase Page',
[INITIALIZE_SEED_PHRASE_INTRO_ROUTE]: 'Initialization Seed Phrase Intro Page',
[INITIALIZE_END_OF_FLOW_ROUTE]: 'End of Initialization Page', [INITIALIZE_END_OF_FLOW_ROUTE]: 'End of Initialization Page',
[INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE]: [INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE]:
'Initialization Confirm Seed Phrase Page', 'Initialization Confirm Seed Phrase Page',
@ -178,6 +180,7 @@ export {
NETWORKS_ROUTE, NETWORKS_ROUTE,
NETWORKS_FORM_ROUTE, NETWORKS_FORM_ROUTE,
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE, INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
CONNECT_ROUTE, CONNECT_ROUTE,
CONNECT_CONFIRM_PERMISSIONS_ROUTE, CONNECT_CONFIRM_PERMISSIONS_ROUTE,
CONNECTED_ROUTE, CONNECTED_ROUTE,

View File

@ -5,7 +5,7 @@ import MetaFoxLogo from '../../../components/ui/metafox-logo';
import { import {
INITIALIZE_CREATE_PASSWORD_ROUTE, INITIALIZE_CREATE_PASSWORD_ROUTE,
INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
} from '../../../helpers/constants/routes'; } from '../../../helpers/constants/routes';
import NewAccount from './new-account'; import NewAccount from './new-account';
import ImportWithSeedPhrase from './import-with-seed-phrase'; import ImportWithSeedPhrase from './import-with-seed-phrase';
@ -22,7 +22,7 @@ export default class CreatePassword extends PureComponent {
const { isInitialized, history } = this.props; const { isInitialized, history } = this.props;
if (isInitialized) { if (isInitialized) {
history.push(INITIALIZE_SEED_PHRASE_ROUTE); history.push(INITIALIZE_SEED_PHRASE_INTRO_ROUTE);
} }
} }

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Button from '../../../../components/ui/button'; import Button from '../../../../components/ui/button';
import { import {
INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
INITIALIZE_SELECT_ACTION_ROUTE, INITIALIZE_SELECT_ACTION_ROUTE,
} from '../../../../helpers/constants/routes'; } from '../../../../helpers/constants/routes';
import TextField from '../../../../components/ui/text-field'; import TextField from '../../../../components/ui/text-field';
@ -108,7 +108,7 @@ export default class NewAccount extends PureComponent {
}, },
}); });
history.push(INITIALIZE_SEED_PHRASE_ROUTE); history.push(INITIALIZE_SEED_PHRASE_INTRO_ROUTE);
} catch (error) { } catch (error) {
this.setState({ passwordError: error.message }); this.setState({ passwordError: error.message });
} }

View File

@ -12,6 +12,7 @@ import {
INITIALIZE_END_OF_FLOW_ROUTE, INITIALIZE_END_OF_FLOW_ROUTE,
INITIALIZE_METAMETRICS_OPT_IN_ROUTE, INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE, INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
} from '../../helpers/constants/routes'; } from '../../helpers/constants/routes';
import FirstTimeFlowSwitch from './first-time-flow-switch'; import FirstTimeFlowSwitch from './first-time-flow-switch';
import Welcome from './welcome'; import Welcome from './welcome';
@ -125,6 +126,16 @@ export default class FirstTimeFlow extends PureComponent {
/> />
)} )}
/> />
<Route
path={INITIALIZE_SEED_PHRASE_INTRO_ROUTE}
render={(routeProps) => (
<SeedPhrase
{...routeProps}
seedPhrase={seedPhrase}
verifySeedPhrase={verifySeedPhrase}
/>
)}
/>
<Route <Route
path={INITIALIZE_CREATE_PASSWORD_ROUTE} path={INITIALIZE_CREATE_PASSWORD_ROUTE}
render={(routeProps) => ( render={(routeProps) => (

View File

@ -143,3 +143,13 @@
color: $primary-blue; color: $primary-blue;
} }
} }
.first-time-flow__wrapper.intro {
@media screen and (min-width: $break-large) {
max-width: 1010px;
}
@media screen and (max-width: 1010px) {
padding: 0 20px;
}
}

View File

@ -1,5 +1,6 @@
@import 'confirm-seed-phrase/index'; @import 'confirm-seed-phrase/index';
@import 'reveal-seed-phrase/index'; @import 'reveal-seed-phrase/index';
@import 'seed-phrase-intro/index';
.seed-phrase { .seed-phrase {
&__sections { &__sections {

View File

@ -0,0 +1 @@
export { default } from './seed-phrase-intro.component';

View File

@ -0,0 +1,44 @@
.seed-phrase-intro {
&__sections {
display: flex;
@media screen and (min-width: $break-large) {
flex-direction: row;
}
@media screen and (max-width: 970px) {
flex-direction: column;
}
}
&__left {
flex: 3;
min-width: 0;
}
&__right {
flex: 1;
min-width: 0;
@media screen and (max-width: 970px) {
margin-top: 24px;
}
}
video {
border-radius: 8px;
@media screen and (max-width: 970px) {
width: 95%;
}
}
&__copy {
max-width: 696px;
}
&__sidebar_list {
list-style: disc;
padding-left: 20px;
}
}

View File

@ -0,0 +1,121 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import { useI18nContext } from '../../../../hooks/useI18nContext';
// Components
import Box from '../../../../components/ui/box';
import Button from '../../../../components/ui/button';
import Typography from '../../../../components/ui/typography';
import {
BLOCK_SIZES,
COLORS,
TYPOGRAPHY,
FONT_WEIGHT,
SIZES,
BORDER_STYLE,
} from '../../../../helpers/constants/design-system';
// Routes
import { INITIALIZE_SEED_PHRASE_ROUTE } from '../../../../helpers/constants/routes';
export default function SeedPhraseIntro() {
const t = useI18nContext();
const history = useHistory();
const handleNextStep = () => {
history.push(INITIALIZE_SEED_PHRASE_ROUTE);
};
return (
<div className="seed-phrase-intro">
<div className="seed-phrase-intro__sections">
<div className="seed-phrase-intro__left">
<Typography
color={COLORS.BLACK}
variant={TYPOGRAPHY.H1}
boxProps={{ marginTop: 0, marginBottom: 4 }}
>
{t('seedPhraseIntroTitle')}
</Typography>
<Typography
color={COLORS.BLACK}
boxProps={{ marginBottom: 4 }}
variant={TYPOGRAPHY.Paragraph}
className="seed-phrase-intro__copy"
>
{t('seedPhraseIntroTitleCopy')}
</Typography>
<Box marginBottom={4}>
<video controls>
<source
type="video/webm"
src="./images/videos/recovery-onboarding/video.webm"
/>
<track
default
srcLang="en"
label="English"
kind="subtitles"
src="./images/videos/recovery-onboarding/subtitles-en.vtt"
/>
</video>
</Box>
<Box width={BLOCK_SIZES.ONE_THIRD}>
<Button type="primary" onClick={handleNextStep}>
{t('next')}
</Button>
</Box>
</div>
<div className="seed-phrase-intro__right">
<Box
padding={4}
borderWidth={1}
borderRadius={SIZES.MD}
borderColor={COLORS.UI2}
borderStyle={BORDER_STYLE.SOLID}
>
<Box marginBottom={4}>
<Typography
tag="span"
color={COLORS.BLACK}
fontWeight={FONT_WEIGHT.BOLD}
boxProps={{ display: 'block' }}
>
{t('seedPhraseIntroSidebarTitleOne')}
</Typography>
<span>{t('seedPhraseIntroSidebarCopyOne')}</span>
</Box>
<Box marginBottom={4}>
<Typography
tag="span"
color={COLORS.BLACK}
fontWeight={FONT_WEIGHT.BOLD}
boxProps={{ display: 'block' }}
>
{t('seedPhraseIntroSidebarTitleTwo')}
</Typography>
<ul className="seed-phrase-intro__sidebar_list">
<li>{t('seedPhraseIntroSidebarBulletOne')}</li>
<li>{t('seedPhraseIntroSidebarBulletTwo')}</li>
<li>{t('seedPhraseIntroSidebarBulletThree')}</li>
<li>{t('seedPhraseIntroSidebarBulletFour')}</li>
</ul>
</Box>
<Box marginBottom={4}>
<Typography
tag="span"
color={COLORS.BLACK}
fontWeight={FONT_WEIGHT.BOLD}
boxProps={{ display: 'block' }}
>
{t('seedPhraseIntroSidebarTitleThree')}
</Typography>
<span>{t('seedPhraseIntroSidebarCopyTwo')}</span>
</Box>
<Box marginBottom={4}>
<span>{t('seedPhraseIntroSidebarCopyThree')}</span>
</Box>
</Box>
</div>
</div>
</div>
);
}

View File

@ -7,11 +7,13 @@ import {
INITIALIZE_SEED_PHRASE_ROUTE, INITIALIZE_SEED_PHRASE_ROUTE,
INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE, INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE,
INITIALIZE_BACKUP_SEED_PHRASE_ROUTE, INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
DEFAULT_ROUTE, DEFAULT_ROUTE,
} from '../../../helpers/constants/routes'; } from '../../../helpers/constants/routes';
import MetaFoxLogo from '../../../components/ui/metafox-logo'; import MetaFoxLogo from '../../../components/ui/metafox-logo';
import ConfirmSeedPhrase from './confirm-seed-phrase'; import ConfirmSeedPhrase from './confirm-seed-phrase';
import RevealSeedPhrase from './reveal-seed-phrase'; import RevealSeedPhrase from './reveal-seed-phrase';
import SeedPhraseIntro from './seed-phrase-intro';
export default class SeedPhrase extends PureComponent { export default class SeedPhrase extends PureComponent {
static propTypes = { static propTypes = {
@ -39,12 +41,15 @@ export default class SeedPhrase extends PureComponent {
} }
render() { render() {
const { seedPhrase } = this.props; const { seedPhrase, history } = this.props;
const { verifiedSeedPhrase } = this.state; const { verifiedSeedPhrase } = this.state;
const pathname = history?.location?.pathname;
const introClass =
pathname === INITIALIZE_SEED_PHRASE_INTRO_ROUTE ? 'intro' : '';
return ( return (
<DragDropContextProvider backend={HTML5Backend}> <DragDropContextProvider backend={HTML5Backend}>
<div className="first-time-flow__wrapper"> <div className={`first-time-flow__wrapper ${introClass}`}>
<MetaFoxLogo /> <MetaFoxLogo />
<Switch> <Switch>
<Route <Route
@ -77,6 +82,16 @@ export default class SeedPhrase extends PureComponent {
/> />
)} )}
/> />
<Route
exact
path={INITIALIZE_SEED_PHRASE_INTRO_ROUTE}
render={(routeProps) => (
<SeedPhraseIntro
{...routeProps}
seedPhrase={seedPhrase || verifiedSeedPhrase}
/>
)}
/>
</Switch> </Switch>
</div> </div>
</DragDropContextProvider> </DragDropContextProvider>