mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-21 17:37:01 +01:00
Onboarding V2 Creation Successful view (#12248)
* add creation-successful onboarding view
This commit is contained in:
parent
b242d950a0
commit
a8ec9ada2a
@ -935,6 +935,9 @@
|
||||
"message": "File import not working? Click here!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"followUsOnTwitter": {
|
||||
"message": "Follow us on Twitter"
|
||||
},
|
||||
"forbiddenIpfsGateway": {
|
||||
"message": "Forbidden IPFS Gateway: Please specify a CID gateway"
|
||||
},
|
||||
@ -1793,6 +1796,9 @@
|
||||
"rejected": {
|
||||
"message": "Rejected"
|
||||
},
|
||||
"remember": {
|
||||
"message": "Remember:"
|
||||
},
|
||||
"remindMeLater": {
|
||||
"message": "Remind me later"
|
||||
},
|
||||
@ -2855,6 +2861,26 @@
|
||||
"walletConnectionGuide": {
|
||||
"message": "our hardware wallet connection guide"
|
||||
},
|
||||
"walletCreationSuccessDetail": {
|
||||
"message": "You’ve successfully protected your wallet. Keep your Secret Recovery Phrase safe and secret -- it’s your responsibility!"
|
||||
},
|
||||
"walletCreationSuccessReminder1": {
|
||||
"message": "MetaMask can’t recover your Secret Recovery Phrase."
|
||||
},
|
||||
"walletCreationSuccessReminder2": {
|
||||
"message": "MetaMask will never ask you for your Secret Recovery Phrase."
|
||||
},
|
||||
"walletCreationSuccessReminder3": {
|
||||
"message": "$1 with anyone or risk your funds being stolen",
|
||||
"description": "$1 is separated as walletCreationSuccessReminder3BoldSection so that we can bold it"
|
||||
},
|
||||
"walletCreationSuccessReminder3BoldSection": {
|
||||
"message": "Never share your Secret Recovery Phrase",
|
||||
"description": "This string is localized separately from walletCreationSuccessReminder3 so that we can bold it"
|
||||
},
|
||||
"walletCreationSuccessTitle": {
|
||||
"message": "Wallet creation successful"
|
||||
},
|
||||
"walletSeed": {
|
||||
"message": "Secret Recovery Phrase"
|
||||
},
|
||||
|
BIN
app/images/tada.png
Normal file
BIN
app/images/tada.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
BIN
app/images/twitter-icon.png
Normal file
BIN
app/images/twitter-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 584 B |
@ -1 +1 @@
|
||||
export { default } from './step-progress-bar';
|
||||
export { default, stages } from './step-progress-bar';
|
||||
|
@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import Box from '../../ui/box';
|
||||
|
||||
const stages = {
|
||||
export const stages = {
|
||||
PASSWORD_CREATE: 1,
|
||||
SEED_PHRASE_VIDEO: 2,
|
||||
SEED_PHRASE_REVIEW: 3,
|
||||
|
@ -15,7 +15,10 @@ import {
|
||||
|
||||
const ValidSize = PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
const ArrayOfValidSizes = PropTypes.arrayOf(ValidSize);
|
||||
const MultipleSizes = PropTypes.oneOfType([ValidSize, ArrayOfValidSizes]);
|
||||
export const MultipleSizes = PropTypes.oneOfType([
|
||||
ValidSize,
|
||||
ArrayOfValidSizes,
|
||||
]);
|
||||
|
||||
function generateSizeClasses(baseClass, type, main, top, right, bottom, left) {
|
||||
const arr = Array.isArray(main) ? main : [];
|
||||
|
@ -1 +1 @@
|
||||
export { default } from './box';
|
||||
export { default, MultipleSizes } from './box';
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
TEXT_ALIGN,
|
||||
TYPOGRAPHY,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import Box from '../box';
|
||||
import Box, { MultipleSizes } from '../box';
|
||||
|
||||
const { H6, H7, H8, H9 } = TYPOGRAPHY;
|
||||
|
||||
@ -22,6 +22,7 @@ export default function Typography({
|
||||
fontStyle = 'normal',
|
||||
align,
|
||||
boxProps = {},
|
||||
margin = [1, 0],
|
||||
}) {
|
||||
const computedClassName = classnames(
|
||||
'typography',
|
||||
@ -44,7 +45,7 @@ export default function Typography({
|
||||
}
|
||||
|
||||
return (
|
||||
<Box margin={[1, 0]} {...boxProps}>
|
||||
<Box margin={margin} {...boxProps}>
|
||||
{(boxClassName) => (
|
||||
<Tag className={classnames(boxClassName, computedClassName)}>
|
||||
{children}
|
||||
@ -63,6 +64,7 @@ Typography.propTypes = {
|
||||
boxProps: PropTypes.shape({
|
||||
...Box.propTypes,
|
||||
}),
|
||||
margin: MultipleSizes,
|
||||
fontWeight: PropTypes.oneOf(Object.values(FONT_WEIGHT)),
|
||||
fontStyle: PropTypes.oneOf(Object.values(FONT_STYLE)),
|
||||
tag: PropTypes.oneOf([
|
||||
|
@ -16,8 +16,11 @@ import { INITIALIZE_SEED_PHRASE_INTRO_ROUTE } from '../../../helpers/constants/r
|
||||
import FormField from '../../../components/ui/form-field';
|
||||
import Box from '../../../components/ui/box';
|
||||
import CheckBox from '../../../components/ui/check-box';
|
||||
import StepProgressBar, {
|
||||
stages,
|
||||
} from '../../../components/app/step-progress-bar';
|
||||
|
||||
export default function NewAccount({ onSubmit }) {
|
||||
export default function CreatePassword({ onSubmit }) {
|
||||
const t = useI18nContext();
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
@ -88,7 +91,8 @@ export default function NewAccount({ onSubmit }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="new-account__wrapper">
|
||||
<div className="create-password__wrapper">
|
||||
<StepProgressBar stage={stages.PASSWORD_CREATE} />
|
||||
<Typography variant={TYPOGRAPHY.H2} fontWeight={FONT_WEIGHT.BOLD}>
|
||||
{t('createPassword')}
|
||||
</Typography>
|
||||
@ -104,7 +108,7 @@ export default function NewAccount({ onSubmit }) {
|
||||
marginTop={3}
|
||||
padding={[0, 12]}
|
||||
>
|
||||
<form className="new-account__form" onSubmit={handleCreate}>
|
||||
<form className="create-password__form" onSubmit={handleCreate}>
|
||||
<FormField
|
||||
autoFocus
|
||||
error={passwordError}
|
||||
@ -114,7 +118,7 @@ export default function NewAccount({ onSubmit }) {
|
||||
value={password}
|
||||
titleDetail={
|
||||
<button
|
||||
className="new-account__form--password-button"
|
||||
className="create-password__form--password-button"
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@ -133,7 +137,7 @@ export default function NewAccount({ onSubmit }) {
|
||||
value={confirmPassword}
|
||||
titleDetail={
|
||||
isValid && (
|
||||
<div className="new-account__form--checkmark">
|
||||
<div className="create-password__form--checkmark">
|
||||
<i className="fas fa-check" />
|
||||
</div>
|
||||
)
|
||||
@ -152,12 +156,12 @@ export default function NewAccount({ onSubmit }) {
|
||||
{t('passwordTermsWarning', [
|
||||
<a
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
key="new-account__link-text"
|
||||
key="create-password__link-text"
|
||||
href="https://metamask.io/terms.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="new-account__link-text">
|
||||
<span className="create-password__link-text">
|
||||
{t('learnMore')}
|
||||
</span>
|
||||
</a>,
|
||||
@ -166,7 +170,7 @@ export default function NewAccount({ onSubmit }) {
|
||||
</Box>
|
||||
<Button
|
||||
type="primary"
|
||||
className="new-account__form--submit-button"
|
||||
className="create-password__form--submit-button"
|
||||
disabled={!isValid || !termsChecked}
|
||||
onClick={handleCreate}
|
||||
>
|
||||
@ -178,6 +182,6 @@ export default function NewAccount({ onSubmit }) {
|
||||
);
|
||||
}
|
||||
|
||||
NewAccount.propTypes = {
|
||||
CreatePassword.propTypes = {
|
||||
onSubmit: PropTypes.func,
|
||||
};
|
@ -1,9 +1,11 @@
|
||||
.new-account {
|
||||
.create-password {
|
||||
&__wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
max-width: 600px;
|
||||
max-height: 750px;
|
||||
}
|
||||
|
||||
&__link-text {
|
@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import Box from '../../../components/ui/box';
|
||||
import Typography from '../../../components/ui/typography';
|
||||
import Button from '../../../components/ui/button';
|
||||
import {
|
||||
FONT_WEIGHT,
|
||||
TEXT_ALIGN,
|
||||
TYPOGRAPHY,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import {
|
||||
ONBOARDING_PIN_EXTENSION_ROUTE,
|
||||
ONBOARDING_PRIVACY_SETTINGS_ROUTE,
|
||||
} from '../../../helpers/constants/routes';
|
||||
|
||||
export default function CreationSuccessful() {
|
||||
const history = useHistory();
|
||||
const t = useI18nContext();
|
||||
return (
|
||||
<div className="creation-successful">
|
||||
<Box textAlign={TEXT_ALIGN.CENTER} margin={6}>
|
||||
<img src="./images/tada.png" />
|
||||
<Typography
|
||||
variant={TYPOGRAPHY.H2}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
margin={6}
|
||||
>
|
||||
{t('walletCreationSuccessTitle')}
|
||||
</Typography>
|
||||
<Typography variant={TYPOGRAPHY.H4}>
|
||||
{t('walletCreationSuccessDetail')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant={TYPOGRAPHY.H4}>{t('remember')}</Typography>
|
||||
<ul>
|
||||
<li>
|
||||
<Typography variant={TYPOGRAPHY.H4}>
|
||||
{t('walletCreationSuccessReminder1')}
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant={TYPOGRAPHY.H4}>
|
||||
{t('walletCreationSuccessReminder2')}
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Typography variant={TYPOGRAPHY.H4}>
|
||||
{t('walletCreationSuccessReminder3', [
|
||||
<span
|
||||
key="creation-successful__bold"
|
||||
className="creation-successful__bold"
|
||||
>
|
||||
{t('walletCreationSuccessReminder3BoldSection')}
|
||||
</span>,
|
||||
])}
|
||||
</Typography>
|
||||
</li>
|
||||
<li>
|
||||
<Button
|
||||
href="https://community.metamask.io/t/what-is-a-secret-recovery-phrase-and-how-to-keep-your-crypto-wallet-secure/3440"
|
||||
target="_blank"
|
||||
type="link"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('learnMore')}
|
||||
</Button>
|
||||
</li>
|
||||
</ul>
|
||||
<Box marginTop={6} className="creation-successful__actions">
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => history.push(ONBOARDING_PRIVACY_SETTINGS_ROUTE)}
|
||||
>
|
||||
{t('setAdvancedPrivacySettings')}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
large
|
||||
rounded
|
||||
onClick={() => history.push(ONBOARDING_PIN_EXTENSION_ROUTE)}
|
||||
>
|
||||
{t('done')}
|
||||
</Button>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import CreationSuccessful from './creation-successful';
|
||||
|
||||
export default {
|
||||
title: 'Onboarding - Creation Successful',
|
||||
id: __filename,
|
||||
};
|
||||
|
||||
export const Base = () => {
|
||||
return (
|
||||
<div style={{ maxHeight: '2000px' }}>
|
||||
<CreationSuccessful />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import reactRouterDom from 'react-router-dom';
|
||||
import {
|
||||
ONBOARDING_PIN_EXTENSION_ROUTE,
|
||||
ONBOARDING_PRIVACY_SETTINGS_ROUTE,
|
||||
} from '../../../helpers/constants/routes';
|
||||
import { renderWithProvider } from '../../../../test/jest';
|
||||
import CreationSuccessful from './creation-successful';
|
||||
|
||||
describe('Creation Successful Onboarding View', () => {
|
||||
const pushMock = jest.fn();
|
||||
beforeAll(() => {
|
||||
jest
|
||||
.spyOn(reactRouterDom, 'useHistory')
|
||||
.mockImplementation()
|
||||
.mockReturnValue({ push: pushMock });
|
||||
});
|
||||
|
||||
it('should redirect to pin-extension view when "Done" button is clicked', () => {
|
||||
const { getByText } = renderWithProvider(<CreationSuccessful />);
|
||||
const doneButton = getByText('Done');
|
||||
fireEvent.click(doneButton);
|
||||
expect(pushMock).toHaveBeenCalledWith(ONBOARDING_PIN_EXTENSION_ROUTE);
|
||||
});
|
||||
|
||||
it('should redirect to privacy-settings view when "Set advanced privacy settings" button is clicked', () => {
|
||||
const { getByText } = renderWithProvider(<CreationSuccessful />);
|
||||
const privacySettingsButton = getByText('Set advanced privacy settings');
|
||||
fireEvent.click(privacySettingsButton);
|
||||
expect(pushMock).toHaveBeenCalledWith(ONBOARDING_PRIVACY_SETTINGS_ROUTE);
|
||||
});
|
||||
});
|
38
ui/pages/onboarding-flow/creation-successful/index.scss
Normal file
38
ui/pages/onboarding-flow/creation-successful/index.scss
Normal file
@ -0,0 +1,38 @@
|
||||
.creation-successful {
|
||||
img {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
max-width: 575px;
|
||||
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-left: 25px;
|
||||
|
||||
a {
|
||||
justify-content: flex-start;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
button {
|
||||
margin-top: 14px;
|
||||
max-width: 60%;
|
||||
padding: 18px 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
@import 'recovery-phrase/index';
|
||||
@import 'new-account/index';
|
||||
@import 'onboarding-app-header/index';
|
||||
@import 'secure-your-wallet/index';
|
||||
@import 'privacy-settings/index';
|
||||
@import 'create-password/index';
|
||||
@import 'creation-successful/index';
|
||||
|
||||
.onboarding-flow {
|
||||
width: 100%;
|
||||
@ -18,4 +19,13 @@
|
||||
border: 1px solid $Grey-100;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
&__twitter-button {
|
||||
margin-bottom: 40px;
|
||||
vertical-align: middle;
|
||||
|
||||
img {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Switch, Route, useHistory } from 'react-router-dom';
|
||||
import { Switch, Route, useHistory, useLocation } from 'react-router-dom';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Unlock from '../unlock-page';
|
||||
import {
|
||||
@ -10,6 +10,7 @@ import {
|
||||
DEFAULT_ROUTE,
|
||||
ONBOARDING_SECURE_YOUR_WALLET_ROUTE,
|
||||
ONBOARDING_PRIVACY_SETTINGS_ROUTE,
|
||||
ONBOARDING_COMPLETION_ROUTE,
|
||||
} from '../../helpers/constants/routes';
|
||||
import {
|
||||
getCompletedOnboarding,
|
||||
@ -22,17 +23,22 @@ import {
|
||||
unlockAndGetSeedPhrase,
|
||||
} from '../../store/actions';
|
||||
import { getFirstTimeFlowTypeRoute } from '../../selectors';
|
||||
import Button from '../../components/ui/button';
|
||||
import { useI18nContext } from '../../hooks/useI18nContext';
|
||||
import OnboardingFlowSwitch from './onboarding-flow-switch/onboarding-flow-switch';
|
||||
import NewAccount from './new-account/new-account';
|
||||
import CreatePassword from './create-password/create-password';
|
||||
import ReviewRecoveryPhrase from './recovery-phrase/review-recovery-phrase';
|
||||
import SecureYourWallet from './secure-your-wallet/secure-your-wallet';
|
||||
import ConfirmRecoveryPhrase from './recovery-phrase/confirm-recovery-phrase';
|
||||
import PrivacySettings from './privacy-settings/privacy-settings';
|
||||
import CreationSuccessful from './creation-successful/creation-successful';
|
||||
|
||||
export default function OnboardingFlow() {
|
||||
const [seedPhrase, setSeedPhrase] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
const currentLocation = useLocation();
|
||||
const history = useHistory();
|
||||
const t = useI18nContext();
|
||||
const isInitialized = useSelector(getIsInitialized);
|
||||
const isUnlocked = useSelector(getIsUnlocked);
|
||||
const completedOnboarding = useSelector(getCompletedOnboarding);
|
||||
@ -43,7 +49,7 @@ export default function OnboardingFlow() {
|
||||
// For ONBOARDING_V2 dev purposes,
|
||||
// Remove when ONBOARDING_V2 dev complete
|
||||
if (process.env.ONBOARDING_V2) {
|
||||
history.push(ONBOARDING_PRIVACY_SETTINGS_ROUTE);
|
||||
history.push(ONBOARDING_COMPLETION_ROUTE);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -85,7 +91,7 @@ export default function OnboardingFlow() {
|
||||
<Route
|
||||
path={ONBOARDING_CREATE_PASSWORD_ROUTE}
|
||||
render={(routeProps) => (
|
||||
<NewAccount
|
||||
<CreatePassword
|
||||
{...routeProps}
|
||||
createNewAccount={handleCreateNewAccount}
|
||||
/>
|
||||
@ -114,9 +120,24 @@ export default function OnboardingFlow() {
|
||||
path={ONBOARDING_PRIVACY_SETTINGS_ROUTE}
|
||||
component={PrivacySettings}
|
||||
/>
|
||||
<Route
|
||||
path={ONBOARDING_COMPLETION_ROUTE}
|
||||
component={CreationSuccessful}
|
||||
/>
|
||||
<Route exact path="*" component={OnboardingFlowSwitch} />
|
||||
</Switch>
|
||||
</div>
|
||||
{currentLocation?.pathname === ONBOARDING_COMPLETION_ROUTE && (
|
||||
<Button
|
||||
className="onboarding-flow__twitter-button"
|
||||
type="link"
|
||||
href="https://twitter.com/MetaMask"
|
||||
target="_blank"
|
||||
>
|
||||
<span>{t('followUsOnTwitter')}</span>
|
||||
<img src="images/twitter-icon.png" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user