diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index a35c8a6a8..4cdb5c47b 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1357,6 +1357,9 @@ "metametricsCommitmentsAllowOptOut": { "message": "Always allow you to opt-out via Settings" }, + "metametricsCommitmentsAllowOptOut2": { + "message": "Always be able to opt-out via Settings" + }, "metametricsCommitmentsBoldNever": { "message": "Never", "description": "This string is localized separately from some of the commitments so that we can bold it" @@ -1364,6 +1367,9 @@ "metametricsCommitmentsIntro": { "message": "MetaMask will.." }, + "metametricsCommitmentsNeverCollect": { + "message": "Never collect keys, addresses, transactions, balances, hashes, or any personal information" + }, "metametricsCommitmentsNeverCollectIP": { "message": "$1 collect your full IP address", "description": "The $1 is the bolded word 'Never', from 'metametricsCommitmentsBoldNever'" @@ -1372,6 +1378,12 @@ "message": "$1 collect keys, addresses, transactions, balances, hashes, or any personal information", "description": "The $1 is the bolded word 'Never', from 'metametricsCommitmentsBoldNever'" }, + "metametricsCommitmentsNeverIP": { + "message": "Never collect your full IP address" + }, + "metametricsCommitmentsNeverSell": { + "message": "Never sell data for profit. Ever!" + }, "metametricsCommitmentsNeverSellDataForProfit": { "message": "$1 sell data for profit. Ever!", "description": "The $1 is the bolded word 'Never', from 'metametricsCommitmentsBoldNever'" @@ -1380,11 +1392,17 @@ "message": "Send anonymized click & pageview events" }, "metametricsHelpImproveMetaMask": { - "message": "Help Us Improve MetaMask" + "message": "Help us improve MetaMask" }, "metametricsOptInDescription": { "message": "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 Ethereum ecosystem." }, + "metametricsOptInDescription2": { + "message": "We would like to gather basic usage data to improve the usability of our product. These metrics will..." + }, + "metametricsTitle": { + "message": "Join 6M+ users to improve MetaMask" + }, "mismatchedChain": { "message": "The network details for this chain ID do not match our records. We recommend that you $1 before proceeding.", "description": "$1 is a clickable link with text defined by the 'mismatchedChainLinkText' key" diff --git a/ui/helpers/constants/routes.js b/ui/helpers/constants/routes.js index 48e7200b9..8b10ea77a 100644 --- a/ui/helpers/constants/routes.js +++ b/ui/helpers/constants/routes.js @@ -66,6 +66,7 @@ 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 ONBOARDING_WELCOME_ROUTE = '/onboarding/welcome'; +const ONBOARDING_METAMETRICS = '/onboarding/metametrics'; const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction'; const CONFIRM_SEND_ETHER_PATH = '/send-ether'; @@ -227,4 +228,5 @@ export { ONBOARDING_UNLOCK_ROUTE, ONBOARDING_PIN_EXTENSION_ROUTE, ONBOARDING_WELCOME_ROUTE, + ONBOARDING_METAMETRICS, }; diff --git a/ui/pages/onboarding-flow/index.scss b/ui/pages/onboarding-flow/index.scss index e3fbe0030..b6c9e762c 100644 --- a/ui/pages/onboarding-flow/index.scss +++ b/ui/pages/onboarding-flow/index.scss @@ -7,6 +7,7 @@ @import 'welcome/index'; @import 'import-srp/index'; @import 'pin-extension/index'; +@import 'metametrics/index'; .onboarding-flow { width: 100%; diff --git a/ui/pages/onboarding-flow/metametrics/index.scss b/ui/pages/onboarding-flow/metametrics/index.scss new file mode 100644 index 000000000..c4c539963 --- /dev/null +++ b/ui/pages/onboarding-flow/metametrics/index.scss @@ -0,0 +1,41 @@ +.onboarding-metametrics { + width: 600px; + + ul { + margin: 24px 0 0 0; + + li { + padding-bottom: 20px; + display: flex; + } + } + + .fa { + width: 16px; + } + + .fa-check { + margin-inline-end: 12px; + color: #1acc56; + } + + .fa-times { + margin-inline-end: 12px; + color: #d0021b; + } + + &__terms a { + color: $Blue-500; + } + + &__buttons { + margin: 24px auto 0 auto; + justify-content: space-between; + display: flex; + + button { + margin-bottom: 24px; + width: 200px; + } + } +} diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.js b/ui/pages/onboarding-flow/metametrics/metametrics.js new file mode 100644 index 000000000..51c1a553e --- /dev/null +++ b/ui/pages/onboarding-flow/metametrics/metametrics.js @@ -0,0 +1,152 @@ +import React, { useContext } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import Typography from '../../../components/ui/typography/typography'; +import { + TYPOGRAPHY, + FONT_WEIGHT, + TEXT_ALIGN, + COLORS, +} from '../../../helpers/constants/design-system'; +import Button from '../../../components/ui/button'; +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { setParticipateInMetaMetrics } from '../../../store/actions'; +import { + getFirstTimeFlowTypeRoute, + getFirstTimeFlowType, + getParticipateInMetaMetrics, +} from '../../../selectors'; + +import { MetaMetricsContext } from '../../../contexts/metametrics'; + +const firstTimeFlowTypeNameMap = { + create: 'Selected Create New Wallet', + import: 'Selected Import Wallet', +}; + +export default function OnboardingMetametrics() { + const t = useI18nContext(); + const dispatch = useDispatch(); + const history = useHistory(); + + const nextRoute = useSelector(getFirstTimeFlowTypeRoute); + const firstTimeFlowType = useSelector(getFirstTimeFlowType); + + const participateInMetaMetrics = useSelector(getParticipateInMetaMetrics); + const firstTimeSelectionMetaMetricsName = + firstTimeFlowTypeNameMap[firstTimeFlowType]; + + const metricsEvent = useContext(MetaMetricsContext); + + const onConfirm = async () => { + const [, metaMetricsId] = await dispatch(setParticipateInMetaMetrics(true)); + + try { + if (!participateInMetaMetrics) { + metricsEvent({ + eventOpts: { + category: 'Onboarding', + action: 'Metrics Option', + name: 'Metrics Opt In', + }, + isOptIn: true, + flushImmediately: true, + }); + } + metricsEvent({ + eventOpts: { + category: 'Onboarding', + action: 'Import or Create', + name: firstTimeSelectionMetaMetricsName, + }, + isOptIn: true, + metaMetricsId, + flushImmediately: true, + }); + } finally { + history.push(nextRoute); + } + }; + + const onCancel = async () => { + await dispatch(setParticipateInMetaMetrics(false)); + + try { + if (!participateInMetaMetrics) { + metricsEvent({ + eventOpts: { + category: 'Onboarding', + action: 'Metrics Option', + name: 'Metrics Opt Out', + }, + isOptIn: true, + flushImmediately: true, + }); + } + } finally { + history.push(nextRoute); + } + }; + + return ( +
+ + {t('metametricsTitle')} + + + {t('metametricsOptInDescription2')} + + + + {t('gdprMessage', [ + + {t('gdprMessagePrivacyPolicy')} + , + ])} + +
+ + +
+
+ ); +} diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.stories.js b/ui/pages/onboarding-flow/metametrics/metametrics.stories.js new file mode 100644 index 000000000..da93b91ec --- /dev/null +++ b/ui/pages/onboarding-flow/metametrics/metametrics.stories.js @@ -0,0 +1,9 @@ +import React from 'react'; +import OnboardingMetametrics from './metametrics'; + +export default { + title: 'Onboarding', + id: __filename, +}; + +export const OnboardingComponent = () => ; diff --git a/ui/pages/onboarding-flow/onboarding-flow.js b/ui/pages/onboarding-flow/onboarding-flow.js index 87af96ba6..6f6c78117 100644 --- a/ui/pages/onboarding-flow/onboarding-flow.js +++ b/ui/pages/onboarding-flow/onboarding-flow.js @@ -14,6 +14,7 @@ import { ONBOARDING_COMPLETION_ROUTE, ONBOARDING_IMPORT_WITH_SRP_ROUTE, ONBOARDING_PIN_EXTENSION_ROUTE, + ONBOARDING_METAMETRICS, } from '../../helpers/constants/routes'; import { getCompletedOnboarding, @@ -39,6 +40,7 @@ import CreationSuccessful from './creation-successful/creation-successful'; import OnboardingWelcome from './welcome/welcome'; import ImportSRP from './import-srp/import-srp'; import OnboardingPinExtension from './pin-extension/pin-extension'; +import MetaMetricsComponent from './metametrics/metametrics'; export default function OnboardingFlow() { const [secretRecoveryPhrase, setSecretRecoveryPhrase] = useState(''); @@ -56,7 +58,7 @@ export default function OnboardingFlow() { // For ONBOARDING_V2 dev purposes, // Remove when ONBOARDING_V2 dev complete if (process.env.ONBOARDING_V2) { - history.push(ONBOARDING_PIN_EXTENSION_ROUTE); + history.push(ONBOARDING_METAMETRICS); return; } @@ -162,6 +164,10 @@ export default function OnboardingFlow() { path={ONBOARDING_PIN_EXTENSION_ROUTE} component={OnboardingPinExtension} /> + diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 226287024..b283e3a61 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -89,6 +89,10 @@ export function getCurrentKeyring(state) { return keyring; } +export function getParticipateInMetaMetrics(state) { + return Boolean(state.metamask.participateInMetaMetrics); +} + export function isEIP1559Account(state) { // Trezor does not support 1559 at this time const currentKeyring = getCurrentKeyring(state);