From 86cde15a46d2012227ff1f01dfa98e3a38917961 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 16 May 2024 16:17:48 -0700 Subject: [PATCH 01/36] Add metametrics toast when onboarded --- app/_locales/en/messages.json | 27 ++++++++++ ui/components/ui/popover/index.scss | 6 ++- ui/components/ui/popover/popover.component.js | 6 +++ ui/pages/home/home.component.js | 50 ++++++++++++++++++- ui/pages/home/index.scss | 10 +++- 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 6dd725445187..e3b44cc87ff9 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3217,6 +3217,33 @@ "on": { "message": "On" }, + "onboardedMetametricsAccept": { + "message": "I agree" + }, + "onboardedMetametricsDisagree": { + "message": "No thanks" + }, + "onboardedMetametricsKey1": { + "message": "Latest developments" + }, + "onboardedMetametricsKey2": { + "message": "Product features" + }, + "onboardedMetametricsKey3": { + "message": "Other relevant promotional materials" + }, + "onboardedMetametricsParagraph1": { + "message": "In addition to MetaMetrics, we'd like to use data to understand how you interact with marketing communications." + }, + "onboardedMetametricsParagraph2": { + "message": "This helps us personalize what we share with you, like:" + }, + "onboardedMetametricsParagraph3": { + "message": "Remember, we never sell the data you provide and you can opt out any time." + }, + "onboardedMetametricsTitle": { + "message": "Help us enhance your experience" + }, "onboarding": { "message": "Onboarding" }, diff --git a/ui/components/ui/popover/index.scss b/ui/components/ui/popover/index.scss index c657ef2f3764..ec1f89943960 100644 --- a/ui/components/ui/popover/index.scss +++ b/ui/components/ui/popover/index.scss @@ -28,6 +28,10 @@ &__title--center { flex: 1; } + + &__title-wrap { + white-space: normal; + } } &-bg { @@ -95,4 +99,4 @@ &-container .page-container { width: auto; } -} +} \ No newline at end of file diff --git a/ui/components/ui/popover/popover.component.js b/ui/components/ui/popover/popover.component.js index 261883c5666c..385f2896bb15 100644 --- a/ui/components/ui/popover/popover.component.js +++ b/ui/components/ui/popover/popover.component.js @@ -75,6 +75,7 @@ const Popover = ({ showScrollDown, onScrollDownButtonClick, centerTitle, + wrapTitle, headerProps = defaultHeaderProps, contentProps = defaultContentProps, footerProps = defaultFooterProps, @@ -106,6 +107,7 @@ const Popover = ({ ) : null} { + const { t } = this.context; + + return ( + {}} + title={t('onboardedMetametricsTitle')} + footer={ + + + + + } + > + + {t('onboardedMetametricsParagraph1')} + {t('onboardedMetametricsParagraph2')} +
    +
  • {t('onboardedMetametricsKey1')}
  • +
  • {t('onboardedMetametricsKey2')}
  • +
  • {t('onboardedMetametricsKey3')}
  • +
+ {t('onboardedMetametricsParagraph3')} +
+
+ ); + }; + renderPopover = () => { const { setConnectedStatusPopoverHasBeenShown } = this.props; const { t } = this.context; @@ -905,6 +952,7 @@ export default class Home extends PureComponent { {isPopup && !connectedStatusPopoverHasBeenShown ? this.renderPopover() : null} + {this.renderOnboardingPopover()} { ///: END:ONLY_INCLUDE_IF } diff --git a/ui/pages/home/index.scss b/ui/pages/home/index.scss index c5d6db36ad36..ea4c1d1629b0 100644 --- a/ui/pages/home/index.scss +++ b/ui/pages/home/index.scss @@ -1,6 +1,14 @@ @use "design-system"; .home { + &__onboarding_list { + list-style: initial; + margin-left: 20px; + display: flex; + flex-direction: column; + gap: 10px; + } + &__container { display: flex; min-height: 100%; @@ -277,4 +285,4 @@ line-height: 140.62%; } } -} +} \ No newline at end of file From 288b56c29a02af64912869a02828c0bc399724d7 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 16 May 2024 16:25:22 -0700 Subject: [PATCH 02/36] Add link --- app/_locales/en/messages.json | 5 ++++- ui/pages/home/home.component.js | 13 ++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index e3b44cc87ff9..51d035234e4f 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3232,8 +3232,11 @@ "onboardedMetametricsKey3": { "message": "Other relevant promotional materials" }, + "onboardedMetametricsLink": { + "message": "MetaMetrics" + }, "onboardedMetametricsParagraph1": { - "message": "In addition to MetaMetrics, we'd like to use data to understand how you interact with marketing communications." + "message": "In addition to $1, we'd like to use data to understand how you interact with marketing communications." }, "onboardedMetametricsParagraph2": { "message": "This helps us personalize what we share with you, like:" diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 892cc3218c54..ae10e6ba0a68 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -790,7 +790,18 @@ export default class Home extends PureComponent { gap={2} margin={4} > - {t('onboardedMetametricsParagraph1')} + + {t('onboardedMetametricsParagraph1', [ + + {t('onboardedMetametricsLink')} + , + ])} + {t('onboardedMetametricsParagraph2')}
  • {t('onboardedMetametricsKey1')}
  • From 581a2ce673202e0835a57e7f1dc893fb5c38fb6c Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 16 May 2024 17:11:22 -0700 Subject: [PATCH 03/36] Add toggle --- app/_locales/en/messages.json | 6 ++++ .../security-tab/security-tab.component.js | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 20a1dbf06c86..d2856327e617 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1231,6 +1231,12 @@ "dataBackupSeemsCorrupt": { "message": "Can not restore your data. The file appears to be corrupt." }, + "dataCollectionForMarketing": { + "message": "Data collection for marketing" + }, + "dataCollectionForMarketingDescription": { + "message": "We'll use MetaMetrics to learn how you interact with our marketing communications. We may share relevant news (like product features and other materials)." + }, "dataHex": { "message": "Hex" }, diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index 8e9fd077b28d..f0799cd6d217 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -361,6 +361,40 @@ export default class SecurityTab extends PureComponent { ); } + renderDataCollectionForMarketing() { + const { t } = this.context; + + return ( + +
    + {t('dataCollectionForMarketing')} +
    + {t('dataCollectionForMarketingDescription')} +
    +
    + +
    + {}} + offLabel={t('off')} + onLabel={t('on')} + /> +
    +
    + ); + } + renderChooseYourNetworkButton() { const { t } = this.context; @@ -1109,6 +1143,7 @@ export default class SecurityTab extends PureComponent {
    {this.renderMetaMetricsOptIn()} + {this.renderDataCollectionForMarketing()}
    ); From a91a2562ed01119d7bab8c935104a912911e7f01 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 16 May 2024 17:32:10 -0700 Subject: [PATCH 04/36] Add popover --- app/_locales/en/messages.json | 6 +++ .../security-tab/security-tab.component.js | 43 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index d2856327e617..bee40d1f1018 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1237,6 +1237,12 @@ "dataCollectionForMarketingDescription": { "message": "We'll use MetaMetrics to learn how you interact with our marketing communications. We may share relevant news (like product features and other materials)." }, + "dataCollectionWarningPopoverButton": { + "message": "Okay" + }, + "dataCollectionWarningPopoverDescription": { + "message": "You turned off data collection for our marketing purposes. This only applies to this device. If you use MetaMask on other devices, make sure to opt out there as well." + }, "dataHex": { "message": "Hex" }, diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index f0799cd6d217..e51d453f64cb 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -25,17 +25,24 @@ import SRPQuiz from '../../../components/app/srp-quiz-modal/SRPQuiz'; import { Button, BUTTON_SIZES, + Icon, + IconSize, + IconName, Box, Text, } from '../../../components/component-library'; import TextField from '../../../components/ui/text-field'; import ToggleButton from '../../../components/ui/toggle-button'; +import Popover from '../../../components/ui/popover'; +import Typography from '../../../components/ui/typography'; import { Display, + BlockSize, FlexDirection, JustifyContent, TextColor, TextVariant, + IconColor, } from '../../../helpers/constants/design-system'; import { ADD_POPULAR_CUSTOM_NETWORK } from '../../../helpers/constants/routes'; import { @@ -1059,12 +1066,48 @@ export default class SecurityTab extends PureComponent { ); } + renderDataCollectionWarning = () => { + const { t } = this.context; + + return ( + {}} + title={ + + } + footer={ + + } + > + + + {t('dataCollectionWarningPopoverDescription')} + + + + ); + }; + render() { const { warning, petnamesEnabled } = this.props; return (
    {this.renderUseExternalServices()} + {this.renderDataCollectionWarning()} {warning &&
    {warning}
    } From 1413cb8d35ae4d346648ad7294f12462ae4bcf10 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Thu, 16 May 2024 17:37:17 +0200 Subject: [PATCH 05/36] feat: add consent checkbox to onboarding metametrics page --- app/_locales/en/messages.json | 3 + .../__snapshots__/metametrics.test.js.snap | 142 ++++++++++++++++++ .../metametrics/metametrics.js | 13 +- .../metametrics/metametrics.test.js | 12 ++ 4 files changed, 169 insertions(+), 1 deletion(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 20a1dbf06c86..9a0d8b9000df 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3334,6 +3334,9 @@ "onboardingMetametricsTitle": { "message": "Help us improve MetaMask" }, + "onboardingMetametricsUseDataCheckbox": { + "message": "We’ll use this data to learn how you interact with our marketing communications. We may share relevant news (like product features)." + }, "onboardingPinExtensionBillboardAccess": { "message": "Full access" }, diff --git a/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap b/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap index 8425fda35aa4..bcf1d6371571 100644 --- a/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap +++ b/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap @@ -150,3 +150,145 @@ exports[`Onboarding Metametrics Component should match snapshot 1`] = `
    `; + +exports[`Onboarding Metametrics Component should match snapshot after new policy date 1`] = ` +
    +
    +

    + Help us improve MetaMask +

    +

    + We’d like to gather basic usage and diagnostics data to improve MetaMask. Know that we never sell the data you provide here. +

    +

    + When we gather metrics, it will always be... +

    +
      +
    • +
      + + + + + + Private: + + clicks and views on the app are stored, but other details (like your public address) are not. + + +
      +
    • +
    • +
      + + + + + + General: + + we temporarily use your IP address to detect a general location (like your country or region), but it's never stored. + + +
      +
    • +
    • +
      + + + + + + Optional: + + you decide if you want to share or delete your usage data via settings any time. + + +
      + +
    • +
    + +
    + + + We’ll let you know if we decide to use this data for other purposes. You can review our + + Privacy Policy + + for more information. Remember, you can go to settings and opt out at any time. + + +
    +
    + + +
    +
    +
    +`; diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.js b/ui/pages/onboarding-flow/metametrics/metametrics.js index 60cc82975ae2..a0e36a5d3c19 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.js @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useContext, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import Typography from '../../../components/ui/typography/typography'; @@ -25,6 +25,7 @@ import { import { MetaMetricsContext } from '../../../contexts/metametrics'; import { + Checkbox, Icon, IconName, IconSize, @@ -47,6 +48,9 @@ export default function OnboardingMetametrics() { const trackEvent = useContext(MetaMetricsContext); + // TODO: maybe this can be a simple ref + const [hasAgreedToDataUse, setHasAgreedToDataUse] = useState(false); + const onConfirm = async () => { const [, metaMetricsId] = await dispatch(setParticipateInMetaMetrics(true)); try { @@ -319,6 +323,13 @@ export default function OnboardingMetametrics() { {' '}
+ setHasAgreedToDataUse((prevValue) => !prevValue)} + label={t('onboardingMetametricsUseDataCheckbox')} + paddingBottom={3} + /> { expect(container).toMatchSnapshot(); }); + it('should match snapshot after new policy date', () => { + // TODO: merge this with the previous test once this date is reached + jest.useFakeTimers().setSystemTime(new Date('2024-06-05')); + + const { container } = renderWithProvider( + , + mockStore, + ); + + expect(container).toMatchSnapshot(); + }); + it('should set setParticipateInMetaMetrics to true when clicking agree', async () => { const { queryByText } = renderWithProvider( , From 23229210d9a11a370445e6e438629483de202733 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Fri, 17 May 2024 13:14:22 +0200 Subject: [PATCH 06/36] test: add cleanup to timer dependent test --- ui/pages/onboarding-flow/metametrics/metametrics.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.test.js b/ui/pages/onboarding-flow/metametrics/metametrics.test.js index ab51ba5d7e01..6b9fb41abda4 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.test.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.test.js @@ -68,6 +68,8 @@ describe('Onboarding Metametrics Component', () => { ); expect(container).toMatchSnapshot(); + + jest.useRealTimers(); }); it('should set setParticipateInMetaMetrics to true when clicking agree', async () => { From a5b933348ceabdde215301f7148f8b83ba88dbd9 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Mon, 20 May 2024 13:38:49 +0100 Subject: [PATCH 07/36] Tie all to state --- app/scripts/controllers/metametrics.js | 7 ++++ app/scripts/metamask-controller.js | 4 ++ ui/ducks/metamask/metamask.js | 7 ++++ ui/pages/home/home.component.js | 20 ++++++++-- ui/pages/home/home.container.js | 5 +++ .../metametrics/metametrics.js | 18 +++++---- .../security-tab/security-tab.component.js | 12 ++++-- .../security-tab/security-tab.container.js | 5 +++ ui/selectors/metametrics.js | 3 ++ ui/store/actionConstants.ts | 2 + ui/store/actions.ts | 39 +++++++++++++++++++ 11 files changed, 107 insertions(+), 15 deletions(-) diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index 73e9fd9609df..7178df191dac 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -471,6 +471,13 @@ export default class MetaMetricsController { return metaMetricsId; } + async setDataCollectionForMarketing(dataCollectionForMarketing) { + let { metaMetricsId } = this.state; + console.log({ dataCollectionForMarketing }); + this.store.updateState({ dataCollectionForMarketing }); + return metaMetricsId; + } + get state() { return this.store.getState(); } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 50a9a6e4c36b..caacd277a3c9 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -2948,6 +2948,10 @@ export default class MetamaskController extends EventEmitter { metaMetricsController.setParticipateInMetaMetrics.bind( metaMetricsController, ), + setDataCollectionForMarketing: + metaMetricsController.setDataCollectionForMarketing.bind( + metaMetricsController, + ), setCurrentLocale: preferencesController.setCurrentLocale.bind( preferencesController, ), diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index ef45e19e7694..4b5b0c761b12 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -56,6 +56,7 @@ const initialState = { knownMethodData: {}, use4ByteResolution: true, participateInMetaMetrics: null, + dataCollectionForMarketing: null, nextNonce: null, currencyRates: { ETH: { @@ -162,6 +163,12 @@ export default function reduceMetamask(state = initialState, action) { participateInMetaMetrics: action.value, }; + case actionConstants.SET_DATA_COLLECTION_FOR_MARKETING: + return { + ...metamaskState, + dataCollectionForMarketing: action.value, + }; + case actionConstants.CLOSE_WELCOME_SCREEN: return { ...metamaskState, diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index ae10e6ba0a68..6d01aec87b89 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -168,6 +168,8 @@ export default class Home extends PureComponent { onTabClick: PropTypes.func.isRequired, haveSwapsQuotes: PropTypes.bool.isRequired, showAwaitingSwapScreen: PropTypes.bool.isRequired, + setDataCollectionForMarketing: PropTypes.func.isRequired, + dataCollectionForMarketing: PropTypes.bool.isRequired, swapsFetchParams: PropTypes.object, location: PropTypes.object, shouldShowWeb3ShimUsageNotification: PropTypes.bool.isRequired, @@ -761,12 +763,13 @@ export default class Home extends PureComponent { renderOnboardingPopover = () => { const { t } = this.context; + const { setDataCollectionForMarketing } = this.props; return ( {}} + onClose={() => setDataCollectionForMarketing(false)} title={t('onboardedMetametricsTitle')} footer={ - - @@ -880,6 +889,7 @@ export default class Home extends PureComponent { completedOnboarding, onboardedInThisUISession, announcementsToShow, + dataCollectionForMarketing, firstTimeFlowType, newNetworkAddedConfigurationId, isSmartTransactionsOptInModalAvailable, @@ -963,7 +973,9 @@ export default class Home extends PureComponent { {isPopup && !connectedStatusPopoverHasBeenShown ? this.renderPopover() : null} - {this.renderOnboardingPopover()} + {dataCollectionForMarketing === null + ? this.renderOnboardingPopover() + : null} { ///: END:ONLY_INCLUDE_IF } diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js index 7bdece86384d..e561e953c3f8 100644 --- a/ui/pages/home/home.container.js +++ b/ui/pages/home/home.container.js @@ -68,6 +68,7 @@ import { setNewTokensImported, setActiveNetwork, setNewTokensImportedError, + setDataCollectionForMarketing, } from '../../store/actions'; import { hideWhatsNewPopup, @@ -98,6 +99,7 @@ const mapStateToProps = (state) => { connectedStatusPopoverHasBeenShown, defaultHomeActiveTabName, swapsState, + dataCollectionForMarketing, firstTimeFlowType, completedOnboarding, } = metamask; @@ -156,6 +158,7 @@ const mapStateToProps = (state) => { shouldShowSeedPhraseReminder: getShouldShowSeedPhraseReminder(state), isPopup, isNotification, + dataCollectionForMarketing, selectedAddress, firstPermissionsRequestId, totalUnapprovedCount, @@ -209,6 +212,8 @@ const mapDispatchToProps = (dispatch) => { ///: END:ONLY_INCLUDE_IF return { + setDataCollectionForMarketing: (val) => + dispatch(setDataCollectionForMarketing(val)), closeNotificationPopup: () => closeNotificationPopup(), setConnectedStatusPopoverHasBeenShown: () => dispatch(setConnectedStatusPopoverHasBeenShown()), diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.js b/ui/pages/onboarding-flow/metametrics/metametrics.js index a0e36a5d3c19..94b5125dd5aa 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.js @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useContext } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import Typography from '../../../components/ui/typography/typography'; @@ -11,8 +11,12 @@ import { } from '../../../helpers/constants/design-system'; import Button from '../../../components/ui/button'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { setParticipateInMetaMetrics } from '../../../store/actions'; import { + setParticipateInMetaMetrics, + setDataCollectionForMarketing, +} from '../../../store/actions'; +import { + getDataCollectionForMarketing, getFirstTimeFlowType, getFirstTimeFlowTypeRouteAfterMetaMetricsOptIn, } from '../../../selectors'; @@ -45,12 +49,10 @@ export default function OnboardingMetametrics() { const nextRoute = useSelector(getFirstTimeFlowTypeRouteAfterMetaMetricsOptIn); const firstTimeFlowType = useSelector(getFirstTimeFlowType); + const dataCollectionForMarketing = useSelector(getDataCollectionForMarketing); const trackEvent = useContext(MetaMetricsContext); - // TODO: maybe this can be a simple ref - const [hasAgreedToDataUse, setHasAgreedToDataUse] = useState(false); - const onConfirm = async () => { const [, metaMetricsId] = await dispatch(setParticipateInMetaMetrics(true)); try { @@ -325,8 +327,10 @@ export default function OnboardingMetametrics() { setHasAgreedToDataUse((prevValue) => !prevValue)} + isChecked={dataCollectionForMarketing} + onClick={() => + dispatch(setDataCollectionForMarketing(!dataCollectionForMarketing)) + } label={t('onboardingMetametricsUseDataCheckbox')} paddingBottom={3} /> diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index e51d453f64cb..91fcd68e3152 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -65,6 +65,8 @@ export default class SecurityTab extends PureComponent { setOpenSeaEnabled: PropTypes.func, useNftDetection: PropTypes.bool, setUseNftDetection: PropTypes.func, + dataCollectionForMarketing: PropTypes.bool.isRequired, + setDataCollectionForMarketing: PropTypes.func.isRequired, participateInMetaMetrics: PropTypes.bool.isRequired, setParticipateInMetaMetrics: PropTypes.func.isRequired, incomingTransactionsPreferences: PropTypes.object.isRequired, @@ -370,6 +372,8 @@ export default class SecurityTab extends PureComponent { renderDataCollectionForMarketing() { const { t } = this.context; + const { dataCollectionForMarketing, setDataCollectionForMarketing } = + this.props; return ( {}} + value={dataCollectionForMarketing} + onToggle={(value) => setDataCollectionForMarketing(!value)} offLabel={t('off')} onLabel={t('on')} /> @@ -1107,7 +1111,7 @@ export default class SecurityTab extends PureComponent { return (
{this.renderUseExternalServices()} - {this.renderDataCollectionWarning()} + {/* {this.renderDataCollectionWarning()} */} {warning &&
{warning}
} diff --git a/ui/pages/settings/security-tab/security-tab.container.js b/ui/pages/settings/security-tab/security-tab.container.js index f411e1ceb1f2..98a2c7c9c3f5 100644 --- a/ui/pages/settings/security-tab/security-tab.container.js +++ b/ui/pages/settings/security-tab/security-tab.container.js @@ -6,6 +6,7 @@ import { setIpfsGateway, setIsIpfsGatewayEnabled, setParticipateInMetaMetrics, + setDataCollectionForMarketing, setUseCurrencyRateCheck, setUseMultiAccountBalanceChecker, setUsePhishDetect, @@ -43,6 +44,7 @@ const mapStateToProps = (state) => { const { incomingTransactionsPreferences, participateInMetaMetrics, + dataCollectionForMarketing, usePhishDetect, useTokenDetection, ipfsGateway, @@ -64,6 +66,7 @@ const mapStateToProps = (state) => { incomingTransactionsPreferences, allNetworks, participateInMetaMetrics, + dataCollectionForMarketing, usePhishDetect, useTokenDetection, ipfsGateway, @@ -90,6 +93,8 @@ const mapDispatchToProps = (dispatch) => { dispatch(setIncomingTransactionsPreferences(chainId, value)), setParticipateInMetaMetrics: (val) => dispatch(setParticipateInMetaMetrics(val)), + setDataCollectionForMarketing: (val) => + dispatch(setDataCollectionForMarketing(val)), setUsePhishDetect: (val) => dispatch(setUsePhishDetect(val)), setUseCurrencyRateCheck: (val) => dispatch(setUseCurrencyRateCheck(val)), setUseTokenDetection: (val) => dispatch(setUseTokenDetection(val)), diff --git a/ui/selectors/metametrics.js b/ui/selectors/metametrics.js index 377181a3e6a8..e4363dfc53f4 100644 --- a/ui/selectors/metametrics.js +++ b/ui/selectors/metametrics.js @@ -2,6 +2,9 @@ import { createSelector } from 'reselect'; export const selectFragments = (state) => state.metamask.fragments; +export const getDataCollectionForMarketing = (state) => + state.metamask.dataCollectionForMarketing; + export const selectFragmentBySuccessEvent = createSelector( selectFragments, (_, fragmentOptions) => fragmentOptions, diff --git a/ui/store/actionConstants.ts b/ui/store/actionConstants.ts index d89aa1d1bbb3..d1321d9b1bae 100644 --- a/ui/store/actionConstants.ts +++ b/ui/store/actionConstants.ts @@ -78,6 +78,8 @@ export const DEPRECATED_NETWORK_POPOVER_CLOSE = export const UPDATE_CUSTOM_NONCE = 'UPDATE_CUSTOM_NONCE'; export const SET_PARTICIPATE_IN_METAMETRICS = 'SET_PARTICIPATE_IN_METAMETRICS'; +export const SET_DATA_COLLECTION_FOR_MARKETING = + 'SET_DATA_COLLECTION_FOR_MARKETING'; // locale export const SET_CURRENT_LOCALE = 'SET_CURRENT_LOCALE'; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 66426c37058b..94531b2d15c8 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -3269,6 +3269,45 @@ export function setParticipateInMetaMetrics( }; } +export function setDataCollectionForMarketing( + dataCollectionPreference: boolean, +): ThunkAction< + Promise<[boolean, string]>, + MetaMaskReduxState, + unknown, + AnyAction +> { + return (dispatch: MetaMaskReduxDispatch) => { + log.debug(`background.setDataCollectionForMarketing`); + return new Promise((resolve, reject) => { + callBackgroundMethod( + 'setDataCollectionForMarketing', + [dataCollectionPreference], + (err, metaMetricsId) => { + log.debug(err); + if (err) { + dispatch(displayWarning(err)); + reject(err); + return; + } + /** + * We need to inform sentry that the user's optin preference may have + * changed. The logic to determine which way to toggle is in the + * toggleSession handler in setupSentry.js. + */ + window.sentry?.toggleSession(); + + dispatch({ + type: actionConstants.SET_DATA_COLLECTION_FOR_MARKETING, + value: dataCollectionPreference, + }); + resolve([dataCollectionPreference, metaMetricsId as string]); + }, + ); + }); + }; +} + export function setUseBlockie( val: boolean, ): ThunkAction { From 3b8afb1a2d706595d68bfbaf0f6b7d2011b8158c Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Mon, 20 May 2024 14:16:58 +0100 Subject: [PATCH 08/36] Add warning message to settings --- .../security-tab/security-tab.component.js | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index 91fcd68e3152..fa7112af186b 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -107,6 +107,7 @@ export default class SecurityTab extends PureComponent { ipfsGateway: this.props.ipfsGateway || IPFS_DEFAULT_GATEWAY_URL, ipfsGatewayError: '', srpQuizModalVisible: false, + showDataCollectionDisclaimer: false, ipfsToggle: this.props.ipfsGateway.length > 0, }; @@ -123,9 +124,17 @@ export default class SecurityTab extends PureComponent { return React.createRef(); }); - componentDidUpdate() { + componentDidUpdate(prevProps) { const { t } = this.context; handleSettingsRefs(t, t('securityAndPrivacy'), this.settingsRefs); + + if ( + prevProps.dataCollectionForMarketing === true && + this.props.participateInMetaMetrics === true && + this.props.dataCollectionForMarketing === false + ) { + this.setState({ showDataCollectionDisclaimer: true }); + } } componentDidMount() { @@ -336,8 +345,11 @@ export default class SecurityTab extends PureComponent { renderMetaMetricsOptIn() { const { t } = this.context; - const { participateInMetaMetrics, setParticipateInMetaMetrics } = - this.props; + const { + participateInMetaMetrics, + setParticipateInMetaMetrics, + setDataCollectionForMarketing, + } = this.props; return ( setParticipateInMetaMetrics(!value)} + onToggle={(value) => { + setParticipateInMetaMetrics(!value); + if (value) { + setDataCollectionForMarketing(!value); + } + }} offLabel={t('off')} onLabel={t('on')} /> @@ -1077,7 +1094,7 @@ export default class SecurityTab extends PureComponent { {}} + onClose={() => this.setState({ showDataCollectionDisclaimer: false })} title={ } footer={ - } @@ -1107,11 +1130,14 @@ export default class SecurityTab extends PureComponent { render() { const { warning, petnamesEnabled } = this.props; + const { showDataCollectionDisclaimer } = this.state; return (
{this.renderUseExternalServices()} - {/* {this.renderDataCollectionWarning()} */} + {showDataCollectionDisclaimer + ? this.renderDataCollectionWarning() + : null} {warning &&
{warning}
} From f739f4675bb02daf715edd54f23e228dc7b07712 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Mon, 20 May 2024 14:25:41 +0100 Subject: [PATCH 09/36] Dont trigger wallet message after onboarding --- ui/pages/onboarding-flow/metametrics/metametrics.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.js b/ui/pages/onboarding-flow/metametrics/metametrics.js index 94b5125dd5aa..44b5e0323b0b 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.js @@ -54,6 +54,10 @@ export default function OnboardingMetametrics() { const trackEvent = useContext(MetaMetricsContext); const onConfirm = async () => { + if (dataCollectionForMarketing === null) { + await dispatch(setDataCollectionForMarketing(false)); + } + const [, metaMetricsId] = await dispatch(setParticipateInMetaMetrics(true)); try { trackEvent( @@ -80,6 +84,7 @@ export default function OnboardingMetametrics() { const onCancel = async () => { await dispatch(setParticipateInMetaMetrics(false)); + await dispatch(setDataCollectionForMarketing(false)); history.push(nextRoute); }; From cfc0c667f19e74afa12ff825f37206faa8d7f371 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Mon, 20 May 2024 14:33:30 +0100 Subject: [PATCH 10/36] Only show popover if participateInMetaMetrics is true --- ui/pages/home/home.component.js | 5 ++++- ui/pages/home/home.container.js | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 6d01aec87b89..bf71d6cf1251 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -165,6 +165,7 @@ export default class Home extends PureComponent { // eslint-disable-next-line react/no-unused-prop-types totalUnapprovedCount: PropTypes.number.isRequired, defaultHomeActiveTabName: PropTypes.string, + participateInMetaMetrics: PropTypes.bool.isRequired, onTabClick: PropTypes.func.isRequired, haveSwapsQuotes: PropTypes.bool.isRequired, showAwaitingSwapScreen: PropTypes.bool.isRequired, @@ -887,6 +888,7 @@ export default class Home extends PureComponent { showWhatsNewPopup, hideWhatsNewPopup, completedOnboarding, + participateInMetaMetrics, onboardedInThisUISession, announcementsToShow, dataCollectionForMarketing, @@ -973,7 +975,8 @@ export default class Home extends PureComponent { {isPopup && !connectedStatusPopoverHasBeenShown ? this.renderPopover() : null} - {dataCollectionForMarketing === null + {dataCollectionForMarketing === null && + participateInMetaMetrics === true ? this.renderOnboardingPopover() : null} { diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js index e561e953c3f8..2d64292608d0 100644 --- a/ui/pages/home/home.container.js +++ b/ui/pages/home/home.container.js @@ -100,6 +100,7 @@ const mapStateToProps = (state) => { defaultHomeActiveTabName, swapsState, dataCollectionForMarketing, + participateInMetaMetrics, firstTimeFlowType, completedOnboarding, } = metamask; @@ -162,6 +163,7 @@ const mapStateToProps = (state) => { selectedAddress, firstPermissionsRequestId, totalUnapprovedCount, + participateInMetaMetrics, hasApprovalFlows: getApprovalFlows(state)?.length > 0, connectedStatusPopoverHasBeenShown, defaultHomeActiveTabName, From cf3f7c2f29a7965558e4ba2bfe16bfbb1e571d51 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Mon, 20 May 2024 14:40:26 +0100 Subject: [PATCH 11/36] When toggling marketing on, also toggle participate in metametrics to on --- .../security-tab/security-tab.component.js | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index fa7112af186b..a1e222fd0b42 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -347,6 +347,7 @@ export default class SecurityTab extends PureComponent { const { t } = this.context; const { participateInMetaMetrics, + dataCollectionForMarketing, setParticipateInMetaMetrics, setDataCollectionForMarketing, } = this.props; @@ -375,8 +376,8 @@ export default class SecurityTab extends PureComponent { value={participateInMetaMetrics} onToggle={(value) => { setParticipateInMetaMetrics(!value); - if (value) { - setDataCollectionForMarketing(!value); + if (dataCollectionForMarketing) { + setDataCollectionForMarketing(false); } }} offLabel={t('off')} @@ -389,8 +390,12 @@ export default class SecurityTab extends PureComponent { renderDataCollectionForMarketing() { const { t } = this.context; - const { dataCollectionForMarketing, setDataCollectionForMarketing } = - this.props; + const { + dataCollectionForMarketing, + participateInMetaMetrics, + setDataCollectionForMarketing, + setParticipateInMetaMetrics, + } = this.props; return ( setDataCollectionForMarketing(!value)} + onToggle={(value) => { + setDataCollectionForMarketing(!value); + if (!participateInMetaMetrics) { + setParticipateInMetaMetrics(true); + } + }} offLabel={t('off')} onLabel={t('on')} /> From a353506836f9d51f1a814fb6d3735e0cc5f01bff Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Tue, 21 May 2024 00:58:33 +0100 Subject: [PATCH 12/36] Add segment events --- shared/constants/metametrics.ts | 1 + ui/pages/home/home.component.js | 36 +++++++++++++++++-- .../metametrics/metametrics.js | 25 +++++++++++++ .../security-tab/security-tab.component.js | 23 +++++++++++- ui/selectors/metametrics.js | 3 ++ 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index fd571b5ebfe0..56712a42522a 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -505,6 +505,7 @@ export enum MetaMetricsEventName { AccountRenamed = 'Account Renamed', ActivityDetailsOpened = 'Activity Details Opened', ActivityDetailsClosed = 'Activity Details Closed', + AnalyticsPreferenceSelected = 'Analytics Preference Selected', AppInstalled = 'App Installed', AppUnlocked = 'App Unlocked', AppUnlockedFailed = 'App Unlocked Failed', diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index bf71d6cf1251..d09f1de65828 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -770,7 +770,17 @@ export default class Home extends PureComponent { setDataCollectionForMarketing(false)} + onClose={() => { + setDataCollectionForMarketing(false); + this.context.trackEvent({ + category: MetaMetricsEventCategory.Home, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: { + has_marketing_consent: false, + location: 'marketing_consent_modal', + }, + }); + }} title={t('onboardedMetametricsTitle')} footer={ diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.js b/ui/pages/onboarding-flow/metametrics/metametrics.js index 44b5e0323b0b..96e69f25d1e1 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.js @@ -16,6 +16,7 @@ import { setDataCollectionForMarketing, } from '../../../store/actions'; import { + getParticipateInMetaMetrics, getDataCollectionForMarketing, getFirstTimeFlowType, getFirstTimeFlowTypeRouteAfterMetaMetricsOptIn, @@ -49,7 +50,9 @@ export default function OnboardingMetametrics() { const nextRoute = useSelector(getFirstTimeFlowTypeRouteAfterMetaMetricsOptIn); const firstTimeFlowType = useSelector(getFirstTimeFlowType); + const dataCollectionForMarketing = useSelector(getDataCollectionForMarketing); + const participateInMetaMetrics = useSelector(getParticipateInMetaMetrics); const trackEvent = useContext(MetaMetricsContext); @@ -77,6 +80,28 @@ export default function OnboardingMetametrics() { flushImmediately: true, }, ); + + if (participateInMetaMetrics) { + trackEvent({ + category: MetaMetricsEventCategory.Onboarding, + event: MetaMetricsEventName.AppInstalled, + }); + + trackEvent({ + category: MetaMetricsEventCategory.Onboarding, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: dataCollectionForMarketing + ? { + is_metrics_opted_in: true, + has_marketing_consent: true, + location: 'onboarding_metametrics', + } + : { + is_metrics_opted_in: true, + has_marketing_consent: false, + }, + }); + } } finally { history.push(nextRoute); } diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index a1e222fd0b42..b8120a238fb0 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -376,6 +376,17 @@ export default class SecurityTab extends PureComponent { value={participateInMetaMetrics} onToggle={(value) => { setParticipateInMetaMetrics(!value); + if (!value) { + this.context.trackEvent({ + category: MetaMetricsEventCategory.Settings, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: { + is_metrics_opted_in: false, + has_marketing_consent: false, + location: 'Settings', + }, + }); + } if (dataCollectionForMarketing) { setDataCollectionForMarketing(false); } @@ -421,7 +432,17 @@ export default class SecurityTab extends PureComponent { value={dataCollectionForMarketing} onToggle={(value) => { setDataCollectionForMarketing(!value); - if (!participateInMetaMetrics) { + if (participateInMetaMetrics) { + this.context.trackEvent({ + category: MetaMetricsEventCategory.Settings, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: { + is_metrics_opted_in: true, + has_marketing_consent: false, + location: 'Settings', + }, + }); + } else { setParticipateInMetaMetrics(true); } }} diff --git a/ui/selectors/metametrics.js b/ui/selectors/metametrics.js index e4363dfc53f4..c623e378c003 100644 --- a/ui/selectors/metametrics.js +++ b/ui/selectors/metametrics.js @@ -5,6 +5,9 @@ export const selectFragments = (state) => state.metamask.fragments; export const getDataCollectionForMarketing = (state) => state.metamask.dataCollectionForMarketing; +export const getParticipateInMetaMetrics = (state) => + Boolean(state.metamask.participateInMetaMetrics); + export const selectFragmentBySuccessEvent = createSelector( selectFragments, (_, fragmentOptions) => fragmentOptions, From 5b7f1a8f91a03d0b0cb60d79230ae7eab879fec1 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Tue, 21 May 2024 01:21:08 +0100 Subject: [PATCH 13/36] Refactor tracking event --- .../onboarding-flow/metametrics/metametrics.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.js b/ui/pages/onboarding-flow/metametrics/metametrics.js index 96e69f25d1e1..f874c99ca5e0 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.js @@ -90,16 +90,11 @@ export default function OnboardingMetametrics() { trackEvent({ category: MetaMetricsEventCategory.Onboarding, event: MetaMetricsEventName.AnalyticsPreferenceSelected, - properties: dataCollectionForMarketing - ? { - is_metrics_opted_in: true, - has_marketing_consent: true, - location: 'onboarding_metametrics', - } - : { - is_metrics_opted_in: true, - has_marketing_consent: false, - }, + properties: { + is_metrics_opted_in: true, + has_marketing_consent: Boolean(dataCollectionForMarketing), + location: 'onboarding_metametrics', + }, }); } } finally { From ddeb213adade630dc4822e3af65f7e14e1bc73f1 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Tue, 21 May 2024 13:54:03 +0100 Subject: [PATCH 14/36] Fix lint --- app/scripts/controllers/metametrics.js | 3 +- ui/components/ui/popover/index.scss | 2 +- ui/pages/home/index.scss | 2 +- .../__snapshots__/security-tab.test.js.snap | 68 +++++++++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index 7178df191dac..b93223a84701 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -472,8 +472,7 @@ export default class MetaMetricsController { } async setDataCollectionForMarketing(dataCollectionForMarketing) { - let { metaMetricsId } = this.state; - console.log({ dataCollectionForMarketing }); + const { metaMetricsId } = this.state; this.store.updateState({ dataCollectionForMarketing }); return metaMetricsId; } diff --git a/ui/components/ui/popover/index.scss b/ui/components/ui/popover/index.scss index ec1f89943960..a935508e7476 100644 --- a/ui/components/ui/popover/index.scss +++ b/ui/components/ui/popover/index.scss @@ -99,4 +99,4 @@ &-container .page-container { width: auto; } -} \ No newline at end of file +} diff --git a/ui/pages/home/index.scss b/ui/pages/home/index.scss index ea4c1d1629b0..dc615110352b 100644 --- a/ui/pages/home/index.scss +++ b/ui/pages/home/index.scss @@ -285,4 +285,4 @@ line-height: 140.62%; } } -} \ No newline at end of file +} diff --git a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap index c52cff34b99f..cb74bd6d0c54 100644 --- a/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap +++ b/ui/pages/settings/security-tab/__snapshots__/security-tab.test.js.snap @@ -1727,6 +1727,74 @@ exports[`Security Tab should match snapshot 1`] = `
+
+
+ + Data collection for marketing + +
+ + We'll use MetaMetrics to learn how you interact with our marketing communications. We may share relevant news (like product features and other materials). + +
+
+
+
From a7989220214d96a3e97968b04722e0a9b1932e63 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Tue, 21 May 2024 17:37:07 +0100 Subject: [PATCH 15/36] Add const --- ui/pages/home/home.component.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index d09f1de65828..e73c457e3610 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -121,6 +121,9 @@ function shouldCloseNotificationPopup({ return shouldClose; } +const METAMETRICS_SETTINGS_LINK = + 'https://support.metamask.io/privacy-and-security/how-to-manage-your-metametrics-settings/'; + export default class Home extends PureComponent { static contextTypes = { t: PropTypes.func, @@ -833,7 +836,7 @@ export default class Home extends PureComponent { {t('onboardedMetametricsParagraph1', [ Date: Tue, 21 May 2024 17:37:51 +0100 Subject: [PATCH 16/36] Change Typography for Text --- ui/pages/settings/security-tab/security-tab.component.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index b8120a238fb0..78d9857bb1b9 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -1151,9 +1151,7 @@ export default class SecurityTab extends PureComponent { gap={2} margin={4} > - - {t('dataCollectionWarningPopoverDescription')} - + {t('dataCollectionWarningPopoverDescription')}
); From b2eb670d94b07c2a1c46676fa9f74847fbbef750 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Wed, 22 May 2024 17:19:37 +0100 Subject: [PATCH 17/36] Add METAMETRICS_SETTINGS_LINK --- builds.yml | 3 ++- ui/helpers/constants/common.ts | 2 ++ ui/pages/home/home.component.js | 4 +--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/builds.yml b/builds.yml index a3698c36a86d..3ac212f179fd 100644 --- a/builds.yml +++ b/builds.yml @@ -187,6 +187,7 @@ env: - DEBUG: null - SUPPORT_LINK: https://support.metamask.io - SUPPORT_REQUEST_LINK: https://metamask.zendesk.com/hc/en-us + - METAMETRICS_SETTINGS_LINK: https://support.metamask.io/privacy-and-security/how-to-manage-your-metametrics-settings - SKIP_BACKGROUND_INITIALIZATION: false # TODO(ritave): Move ManifestV3 into a feature? @@ -310,4 +311,4 @@ env: # Account Abstraction (EIP-4337) ### - - EIP_4337_ENTRYPOINT: null \ No newline at end of file + - EIP_4337_ENTRYPOINT: null diff --git a/ui/helpers/constants/common.ts b/ui/helpers/constants/common.ts index fec96f08aad7..1bc76d8c5731 100644 --- a/ui/helpers/constants/common.ts +++ b/ui/helpers/constants/common.ts @@ -9,6 +9,8 @@ const _mmiWebSite = 'https://metamask.io/institutions/'; export const MMI_WEB_SITE = _mmiWebSite; ///: END:ONLY_INCLUDE_IF +// eslint-disable-next-line prefer-destructuring +export const METAMETRICS_SETTINGS_LINK = process.env.METAMETRICS_SETTINGS_LINK; // eslint-disable-next-line prefer-destructuring export const SUPPORT_REQUEST_LINK = process.env.SUPPORT_REQUEST_LINK; export const CONTRACT_ADDRESS_LINK = _contractAddressLink; diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index e73c457e3610..c18cf11309e1 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -15,6 +15,7 @@ import TermsOfUsePopup from '../../components/app/terms-of-use-popup'; import RecoveryPhraseReminder from '../../components/app/recovery-phrase-reminder'; import WhatsNewPopup from '../../components/app/whats-new-popup'; import { FirstTimeFlowType } from '../../../shared/constants/onboarding'; +import { METAMETRICS_SETTINGS_LINK } from '../../helpers/constants/common'; import SmartTransactionsOptInModal from '../../components/app/smart-transactions/smart-transactions-opt-in-modal'; ///: END:ONLY_INCLUDE_IF import HomeNotification from '../../components/app/home-notification'; @@ -121,9 +122,6 @@ function shouldCloseNotificationPopup({ return shouldClose; } -const METAMETRICS_SETTINGS_LINK = - 'https://support.metamask.io/privacy-and-security/how-to-manage-your-metametrics-settings/'; - export default class Home extends PureComponent { static contextTypes = { t: PropTypes.func, From 94997fbff25e0e9e4754756e4d8f6d09e85afc46 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Wed, 22 May 2024 17:22:37 +0100 Subject: [PATCH 18/36] Remove unused import --- ui/pages/settings/security-tab/security-tab.component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index 78d9857bb1b9..e7f3fb92ff7f 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -34,7 +34,6 @@ import { import TextField from '../../../components/ui/text-field'; import ToggleButton from '../../../components/ui/toggle-button'; import Popover from '../../../components/ui/popover'; -import Typography from '../../../components/ui/typography'; import { Display, BlockSize, From 58814db91174276cce59cac36e1c97a6c24bd648 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 23 May 2024 14:23:41 +0100 Subject: [PATCH 19/36] Fix conflicts --- .../metametrics-toggle/metametrics-toggle.tsx | 22 ++++++- .../security-tab/security-tab.component.js | 63 ++----------------- 2 files changed, 26 insertions(+), 59 deletions(-) diff --git a/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.tsx b/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.tsx index 60c208df8ccd..661dd7f974b4 100644 --- a/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.tsx +++ b/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.tsx @@ -22,7 +22,13 @@ import { TextVariant, } from '../../../../helpers/constants/design-system'; -const MetametricsToggle = () => { +const MetametricsToggle = ({ + dataCollectionForMarketing, + setDataCollectionForMarketing, +}: { + dataCollectionForMarketing: boolean; + setDataCollectionForMarketing: (value: boolean) => void; +}) => { const t = useI18nContext(); const trackEvent = useContext(MetaMetricsContext); const { enableMetametrics, error: enableMetametricsError } = @@ -46,6 +52,16 @@ const MetametricsToggle = () => { participateInMetaMetrics, }, }); + + trackEvent({ + category: MetaMetricsEventCategory.Settings, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: { + is_metrics_opted_in: false, + has_marketing_consent: false, + location: 'Settings', + }, + }); } else { await enableMetametrics(); trackEvent({ @@ -57,6 +73,10 @@ const MetametricsToggle = () => { }, }); } + + if (dataCollectionForMarketing) { + setDataCollectionForMarketing(false); + } }; return ( diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index 6b83c851e5d1..ab9fc7e4785a 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -344,62 +344,6 @@ export default class SecurityTab extends PureComponent { ); } - renderMetaMetricsOptIn() { - const { t } = this.context; - const { - participateInMetaMetrics, - dataCollectionForMarketing, - setParticipateInMetaMetrics, - setDataCollectionForMarketing, - } = this.props; - - return ( - -
- {t('participateInMetaMetrics')} -
- {t('participateInMetaMetricsDescription')} -
-
- -
- { - setParticipateInMetaMetrics(!value); - if (!value) { - this.context.trackEvent({ - category: MetaMetricsEventCategory.Settings, - event: MetaMetricsEventName.AnalyticsPreferenceSelected, - properties: { - is_metrics_opted_in: false, - has_marketing_consent: false, - location: 'Settings', - }, - }); - } - if (dataCollectionForMarketing) { - setDataCollectionForMarketing(false); - } - }} - offLabel={t('off')} - onLabel={t('on')} - /> -
-
- ); - } - renderDataCollectionForMarketing() { const { t } = this.context; const { @@ -1159,7 +1103,7 @@ export default class SecurityTab extends PureComponent { }; render() { - const { warning, petnamesEnabled } = this.props; + const { warning, petnamesEnabled, dataCollectionForMarketing } = this.props; const { showDataCollectionDisclaimer } = this.state; return ( @@ -1249,7 +1193,10 @@ export default class SecurityTab extends PureComponent { {this.context.t('metrics')}
- + {this.renderDataCollectionForMarketing()}
From 4fba09b33c7292d669054ced1b91a2688023d877 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 23 May 2024 14:49:18 +0100 Subject: [PATCH 20/36] Fix prop --- ui/pages/settings/security-tab/security-tab.component.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index ab9fc7e4785a..1514e209d5f0 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -1103,7 +1103,12 @@ export default class SecurityTab extends PureComponent { }; render() { - const { warning, petnamesEnabled, dataCollectionForMarketing } = this.props; + const { + warning, + petnamesEnabled, + dataCollectionForMarketing, + setDataCollectionForMarketing, + } = this.props; const { showDataCollectionDisclaimer } = this.state; return ( From eaeeec7a47c8bf0a95846bac3cf97d55ddb93e97 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 23 May 2024 15:08:56 +0100 Subject: [PATCH 21/36] Fix test --- .../metametrics-toggle.test.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.test.tsx b/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.test.tsx index 04a851797097..5e93bc9feaab 100644 --- a/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.test.tsx +++ b/ui/pages/settings/security-tab/metametrics-toggle/metametrics-toggle.test.tsx @@ -46,7 +46,11 @@ describe('MetametricsToggle', () => { mockUseSelectorReturnValue = false; const { getByTestId } = render( - + {}} + /> , ); expect(getByTestId('profileSyncToggle')).toBeInTheDocument(); @@ -56,7 +60,11 @@ describe('MetametricsToggle', () => { mockUseSelectorReturnValue = false; const { getByTestId } = render( - + {}} + /> , ); fireEvent.click(getByTestId('toggleButton')); @@ -67,7 +75,11 @@ describe('MetametricsToggle', () => { mockUseSelectorReturnValue = true; const { getByTestId } = render( - + {}} + /> , ); fireEvent.click(getByTestId('toggleButton')); From b147ae4a69e64db85e2449e215ab46e25e3c53f2 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 23 May 2024 17:29:45 +0100 Subject: [PATCH 22/36] Update traits --- app/scripts/controllers/metametrics.js | 4 ++++ shared/constants/metametrics.ts | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index ed8d02d3092c..ed245b65d87c 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -830,6 +830,10 @@ export default class MetaMetricsController { metamaskState.securityAlertsEnabled ? ['blockaid'] : [], [MetaMetricsUserTrait.PetnameAddressCount]: this._getPetnameAddressCount(metamaskState), + [MetaMetricsUserTrait.IsMetricsOptedIn]: + metamaskState.participateInMetaMetrics, + [MetaMetricsUserTrait.HasMarketingConsent]: + metamaskState.dataCollectionForMarketing, }; if (!previousUserTraits) { diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 5eaeff48a208..e1fd9c44441f 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -383,6 +383,14 @@ export type MetaMetricsUserTraits = { }; export enum MetaMetricsUserTrait { + /** + * Identifies if the user has opted in for MetaMetrics + */ + IsMetricsOptedIn = 'is_metrics_opted_in', + /** + * Identifies is the user has given marketing consent + */ + HasMarketingConsent = 'has_marketing_consent', /** * Identified when the user adds or modifies addresses in the address book. */ From 01c84d398c29f958ed1e8efcf06e047e21830ecb Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 23 May 2024 17:36:38 +0100 Subject: [PATCH 23/36] Fix test --- .../metametrics/metametrics.test.js | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/ui/pages/onboarding-flow/metametrics/metametrics.test.js b/ui/pages/onboarding-flow/metametrics/metametrics.test.js index 6b9fb41abda4..e427886b92c7 100644 --- a/ui/pages/onboarding-flow/metametrics/metametrics.test.js +++ b/ui/pages/onboarding-flow/metametrics/metametrics.test.js @@ -8,7 +8,10 @@ import { onboardingMetametricsAgree, onboardingMetametricsDisagree, } from '../../../../app/_locales/en/messages.json'; -import { setParticipateInMetaMetrics } from '../../../store/actions'; +import { + setParticipateInMetaMetrics, + setDataCollectionForMarketing, +} from '../../../store/actions'; import { FirstTimeFlowType } from '../../../../shared/constants/onboarding'; import OnboardingMetametrics from './metametrics'; @@ -29,6 +32,9 @@ jest.mock('../../../store/actions.ts', () => ({ setParticipateInMetaMetrics: jest .fn() .mockReturnValue(jest.fn((val) => Promise.resolve([val]))), + setDataCollectionForMarketing: jest + .fn() + .mockReturnValue(jest.fn((val) => Promise.resolve([val]))), })); describe('Onboarding Metametrics Component', () => { @@ -108,6 +114,24 @@ describe('Onboarding Metametrics Component', () => { }); }); + it('should set setDataCollectionForMarketing to false when clicking cancel', async () => { + const { queryByText } = renderWithProvider( + , + mockStore, + ); + + const confirmCancel = queryByText(onboardingMetametricsDisagree.message); + + fireEvent.click(confirmCancel); + + await waitFor(() => { + expect(setDataCollectionForMarketing).toHaveBeenCalledWith(false); + expect(mockPushHistory).toHaveBeenCalledWith( + ONBOARDING_CREATE_PASSWORD_ROUTE, + ); + }); + }); + it('should render the Onboarding component when the current date is after the new privacy policy date', () => { jest.useFakeTimers().setSystemTime(new Date('2026-01-01')); const { queryByTestId } = renderWithProvider( From f8d436c99ef1cf1c31f171b20e53ba2db88d93a0 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 23 May 2024 18:10:43 +0100 Subject: [PATCH 24/36] Add dataCollectionForMarketing to FixtureBuilder --- test/e2e/fixture-builder.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 3db6a2c2c457..8fae7fe3587b 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -127,6 +127,7 @@ function defaultFixture(inputChainId = CHAIN_IDS.LOCALHOST) { fragments: {}, metaMetricsId: null, participateInMetaMetrics: false, + dataCollectionForMarketing: false, traits: {}, }, NetworkController: { From 89fb6ff95ee8197995ff43c6fa258bd8c41305af Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Fri, 24 May 2024 13:05:13 +0530 Subject: [PATCH 25/36] prep build error fix for mmi --- ui/pages/home/home.component.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 266b71c0de0e..cb89d585940e 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -15,7 +15,6 @@ import TermsOfUsePopup from '../../components/app/terms-of-use-popup'; import RecoveryPhraseReminder from '../../components/app/recovery-phrase-reminder'; import WhatsNewPopup from '../../components/app/whats-new-popup'; import { FirstTimeFlowType } from '../../../shared/constants/onboarding'; -import { METAMETRICS_SETTINGS_LINK } from '../../helpers/constants/common'; import SmartTransactionsOptInModal from '../../components/app/smart-transactions/smart-transactions-opt-in-modal'; ///: END:ONLY_INCLUDE_IF import HomeNotification from '../../components/app/home-notification'; @@ -80,6 +79,7 @@ import { ///: END:ONLY_INCLUDE_IF } from '../../helpers/constants/routes'; import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; +import { METAMETRICS_SETTINGS_LINK } from '../../helpers/constants/common'; import { ///: BEGIN:ONLY_INCLUDE_IF(build-main) SUPPORT_LINK, @@ -910,6 +910,8 @@ export default class Home extends PureComponent { setBasicFunctionalityModalOpen, forgottenPassword, history, + participateInMetaMetrics, + dataCollectionForMarketing, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) connectedStatusPopoverHasBeenShown, isPopup, @@ -919,10 +921,8 @@ export default class Home extends PureComponent { showWhatsNewPopup, hideWhatsNewPopup, completedOnboarding, - participateInMetaMetrics, onboardedInThisUISession, announcementsToShow, - dataCollectionForMarketing, firstTimeFlowType, newNetworkAddedConfigurationId, isSmartTransactionsOptInModalAvailable, @@ -988,6 +988,10 @@ export default class Home extends PureComponent { exact />
+ {dataCollectionForMarketing === null && + participateInMetaMetrics === true + ? this.renderOnboardingPopover() + : null} { ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) } @@ -1008,10 +1012,6 @@ export default class Home extends PureComponent { {isPopup && !connectedStatusPopoverHasBeenShown ? this.renderPopover() : null} - {dataCollectionForMarketing === null && - participateInMetaMetrics === true - ? this.renderOnboardingPopover() - : null} { ///: END:ONLY_INCLUDE_IF } From 73a48ce9d83103c06c48774676979ddc95f8b392 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Fri, 24 May 2024 14:54:38 +0530 Subject: [PATCH 26/36] updated error specs --- .../errors-after-init-opt-in-background-state.json | 1 + .../state-snapshots/errors-after-init-opt-in-ui-state.json | 1 + .../errors-before-init-opt-in-background-state.json | 1 + .../state-snapshots/errors-before-init-opt-in-ui-state.json | 1 + 4 files changed, 4 insertions(+) diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index f3ff636806c0..c220bad9b54a 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -100,6 +100,7 @@ "traits": "object", "previousUserTraits": "object", "fragments": "object", + "dataCollectionForMarketing": "boolean", "segmentApiCalls": "object" }, "MetamaskNotificationsController": { diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index 498a1ce87b34..2047a3c5b7f3 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -40,6 +40,7 @@ "knownMethodData": "object", "use4ByteResolution": true, "participateInMetaMetrics": true, + "dataCollectionForMarketing": "boolean", "nextNonce": null, "currencyRates": { "ETH": { diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json index 156359f066cd..bdeb1ec83619 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-background-state.json @@ -66,6 +66,7 @@ "fragments": "object", "metaMetricsId": "fake-metrics-id", "participateInMetaMetrics": true, + "dataCollectionForMarketing": "boolean", "traits": "object" }, "NetworkController": { diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index 1b78ddeb4ff0..1198195000e7 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -66,6 +66,7 @@ "fragments": "object", "metaMetricsId": "fake-metrics-id", "participateInMetaMetrics": true, + "dataCollectionForMarketing": "boolean", "traits": "object" }, "NetworkController": { From 6699dd4c7bbfcfa2ad528e81f8e0b60dcb26a395 Mon Sep 17 00:00:00 2001 From: NidhiKJha Date: Mon, 27 May 2024 13:19:30 +0530 Subject: [PATCH 27/36] added state to sentry --- app/scripts/lib/setupSentry.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index e5b14dcbab3d..c841499c4cfd 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -167,6 +167,7 @@ export const SENTRY_BACKGROUND_STATE = { previousUserTraits: false, segmentApiCalls: false, traits: false, + dataCollectionForMarketing: false, }, NameController: { names: false, From 1c3cffa721a8e571a7b0296b81fa0114d8884407 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Tue, 4 Jun 2024 13:39:07 +0100 Subject: [PATCH 28/36] Fix snapshot --- .../__snapshots__/metametrics.test.js.snap | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap b/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap index bcf1d6371571..f669f81f59d7 100644 --- a/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap +++ b/ui/pages/onboarding-flow/metametrics/__snapshots__/metametrics.test.js.snap @@ -155,7 +155,7 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy

- We’d like to gather basic usage and diagnostics data to improve MetaMask. Know that we never sell the data you provide here. + MetaMask would like to gather usage data to better understand how our users interact with MetaMask. This data will be used to provide the service, which includes improving the service based on your use.

- When we gather metrics, it will always be... + MetaMask will...

    +
  • + + Always allow you to opt-out via Settings +
  • +
  • + + Send anonymized click and pageview events +
  • @@ -187,9 +201,9 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy - Private: + Never - clicks and views on the app are stored, but other details (like your public address) are not. + collect information we don’t need to provide the service (such as keys, addresses, transaction hashes, or balances)
    @@ -199,8 +213,8 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy class="box box--flex-direction-row" > @@ -208,9 +222,9 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy - General: + Never - we temporarily use your IP address to detect a general location (like your country or region), but it's never stored. + collect your full IP address*

@@ -220,8 +234,8 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy class="box box--flex-direction-row" > @@ -229,47 +243,42 @@ exports[`Onboarding Metametrics Component should match snapshot after new policy - Optional: + Never - you decide if you want to share or delete your usage data via settings any time. + sell data. Ever!
- + This data is aggregated and is therefore anonymous for the purposes of General Data Protection Regulation (EU) 2016/679. +
- We’ll let you know if we decide to use this data for other purposes. You can review our + * When you use Infura as your default RPC provider in MetaMask, Infura will collect your IP address and your Ethereum wallet address when you send a transaction. We don’t store this information in a way that allows our systems to associate those two pieces of data. For more information on how MetaMask and Infura interact from a data collection perspective, see our update + + here + + . For more information on our privacy practices in general, see our - Privacy Policy + Privacy Policy here - for more information. Remember, you can go to settings and opt out at any time. + .
From 1e0507554421b5dc46b9114b5117dd0de4507b12 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 6 Jun 2024 17:34:53 +0100 Subject: [PATCH 29/36] Fixes --- app/scripts/controllers/metametrics.js | 2 +- ui/pages/home/home.component.js | 2 +- ui/pages/home/index.scss | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index ed245b65d87c..6fd620a26c38 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -471,7 +471,7 @@ export default class MetaMetricsController { return metaMetricsId; } - async setDataCollectionForMarketing(dataCollectionForMarketing) { + setDataCollectionForMarketing(dataCollectionForMarketing) { const { metaMetricsId } = this.state; this.store.updateState({ dataCollectionForMarketing }); return metaMetricsId; diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 16987f054560..9f56a02ca8a2 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -19,7 +19,6 @@ import AutoDetectTokenModal from '../../components/app/auto-detect-token/auto-de import HomeNotification from '../../components/app/home-notification'; import MultipleNotifications from '../../components/app/multiple-notifications'; import Typography from '../../components/ui/typography/typography'; -import Popover from '../../components/ui/popover'; import Button from '../../components/ui/button'; import ConnectedSites from '../connected-sites'; import ConnectedAccounts from '../connected-accounts'; @@ -41,6 +40,7 @@ import { Box, Text, Icon, + Popover, } from '../../components/component-library'; import { RESTORE_VAULT_ROUTE, diff --git a/ui/pages/home/index.scss b/ui/pages/home/index.scss index 08524dd8ad18..035f6ea4a70b 100644 --- a/ui/pages/home/index.scss +++ b/ui/pages/home/index.scss @@ -3,7 +3,7 @@ .home { &__onboarding_list { list-style: initial; - margin-left: 20px; + margin-inline-start: 20px; display: flex; flex-direction: column; gap: 10px; @@ -255,4 +255,4 @@ line-height: 140.62%; } } -} +} \ No newline at end of file From 266127cf1c4cf7ff8db4f96cdde25eed3e42d4c8 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Thu, 6 Jun 2024 17:48:55 +0100 Subject: [PATCH 30/36] Rollback popover component --- ui/pages/home/home.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 9f56a02ca8a2..2ad15d125c3c 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -20,6 +20,7 @@ import HomeNotification from '../../components/app/home-notification'; import MultipleNotifications from '../../components/app/multiple-notifications'; import Typography from '../../components/ui/typography/typography'; import Button from '../../components/ui/button'; +import Popover from '../../components/ui/popover'; import ConnectedSites from '../connected-sites'; import ConnectedAccounts from '../connected-accounts'; @@ -40,7 +41,6 @@ import { Box, Text, Icon, - Popover, } from '../../components/component-library'; import { RESTORE_VAULT_ROUTE, From a4cc618d718531e7a28e8023f5a90c47f7041ff4 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Fri, 7 Jun 2024 01:14:02 +0100 Subject: [PATCH 31/36] Refactor Popover for Modal --- ui/pages/home/home.component.js | 179 ++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 79 deletions(-) diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 2ad15d125c3c..f08e29ac26a0 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -32,6 +32,8 @@ import { TextVariant, FlexDirection, BlockSize, + AlignItems, + JustifyContent, } from '../../helpers/constants/design-system'; import { SECOND } from '../../../shared/constants/time'; import { @@ -41,6 +43,12 @@ import { Box, Text, Icon, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, } from '../../components/component-library'; import { RESTORE_VAULT_ROUTE, @@ -753,91 +761,104 @@ export default class Home extends PureComponent { const { t } = this.context; const { setDataCollectionForMarketing } = this.props; + const handleClose = () => { + setDataCollectionForMarketing(false); + this.context.trackEvent({ + category: MetaMetricsEventCategory.Home, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: { + has_marketing_consent: false, + location: 'marketing_consent_modal', + }, + }); + }; + + const handleDisagree = () => { + setDataCollectionForMarketing(false); + this.context.trackEvent({ + category: MetaMetricsEventCategory.Home, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: { + has_marketing_consent: false, + location: 'marketing_consent_modal', + }, + }); + }; + + const handleAccept = () => { + setDataCollectionForMarketing(true); + this.context.trackEvent({ + category: MetaMetricsEventCategory.Home, + event: MetaMetricsEventName.AnalyticsPreferenceSelected, + properties: { + has_marketing_consent: true, + location: 'marketing_consent_modal', + }, + }); + }; + return ( - { - setDataCollectionForMarketing(false); - this.context.trackEvent({ - category: MetaMetricsEventCategory.Home, - event: MetaMetricsEventName.AnalyticsPreferenceSelected, - properties: { - has_marketing_consent: false, - location: 'marketing_consent_modal', - }, - }); - }} - title={t('onboardedMetametricsTitle')} - footer={ - + + + - - - - } - > - - - {t('onboardedMetametricsParagraph1', [ - - {t('onboardedMetametricsLink')} - , - ])} - - {t('onboardedMetametricsParagraph2')} -
    -
  • {t('onboardedMetametricsKey1')}
  • -
  • {t('onboardedMetametricsKey2')}
  • -
  • {t('onboardedMetametricsKey3')}
  • -
- {t('onboardedMetametricsParagraph3')} -
-
+ + + + + + ); }; From 60759a835374fc1a4972bbb62b9bf583a1ebfa04 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Thu, 6 Jun 2024 22:08:10 -0500 Subject: [PATCH 32/36] Fix lint --- ui/pages/home/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/home/index.scss b/ui/pages/home/index.scss index 035f6ea4a70b..03b4cd5d7cf9 100644 --- a/ui/pages/home/index.scss +++ b/ui/pages/home/index.scss @@ -255,4 +255,4 @@ line-height: 140.62%; } } -} \ No newline at end of file +} From 8006556b6c5c5e4480b406347b1f529198a62d21 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Fri, 7 Jun 2024 17:45:04 +0100 Subject: [PATCH 33/36] Fix proptypes --- ui/pages/home/home.component.js | 2 +- ui/pages/settings/security-tab/security-tab.component.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index f08e29ac26a0..3b52f83ff289 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -162,7 +162,7 @@ export default class Home extends PureComponent { haveSwapsQuotes: PropTypes.bool.isRequired, showAwaitingSwapScreen: PropTypes.bool.isRequired, setDataCollectionForMarketing: PropTypes.func.isRequired, - dataCollectionForMarketing: PropTypes.bool.isRequired, + dataCollectionForMarketing: PropTypes.bool, swapsFetchParams: PropTypes.object, location: PropTypes.object, shouldShowWeb3ShimUsageNotification: PropTypes.bool.isRequired, diff --git a/ui/pages/settings/security-tab/security-tab.component.js b/ui/pages/settings/security-tab/security-tab.component.js index 82d721ccead2..b20435e4b258 100644 --- a/ui/pages/settings/security-tab/security-tab.component.js +++ b/ui/pages/settings/security-tab/security-tab.component.js @@ -66,7 +66,7 @@ export default class SecurityTab extends PureComponent { setOpenSeaEnabled: PropTypes.func, useNftDetection: PropTypes.bool, setUseNftDetection: PropTypes.func, - dataCollectionForMarketing: PropTypes.bool.isRequired, + dataCollectionForMarketing: PropTypes.bool, setDataCollectionForMarketing: PropTypes.func.isRequired, participateInMetaMetrics: PropTypes.bool.isRequired, setParticipateInMetaMetrics: PropTypes.func.isRequired, From 6f7cf8541b3de715841492d3066a1746e581c5d7 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Fri, 7 Jun 2024 18:52:03 +0100 Subject: [PATCH 34/36] Unify consent function --- app/scripts/controllers/metametrics.js | 1 + ui/pages/home/home.component.js | 22 +++++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index 6fd620a26c38..e178d75c2754 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -154,6 +154,7 @@ export default class MetaMetricsController { this.store = new ObservableStore({ participateInMetaMetrics: null, metaMetricsId: null, + dataCollectionForMarketing: null, eventsBeforeMetricsOptIn: [], traits: {}, previousUserTraits: {}, diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 3b52f83ff289..38235ddc7156 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -773,25 +773,13 @@ export default class Home extends PureComponent { }); }; - const handleDisagree = () => { - setDataCollectionForMarketing(false); - this.context.trackEvent({ - category: MetaMetricsEventCategory.Home, - event: MetaMetricsEventName.AnalyticsPreferenceSelected, - properties: { - has_marketing_consent: false, - location: 'marketing_consent_modal', - }, - }); - }; - - const handleAccept = () => { - setDataCollectionForMarketing(true); + const handleConsent = (consent) => { + setDataCollectionForMarketing(consent); this.context.trackEvent({ category: MetaMetricsEventCategory.Home, event: MetaMetricsEventName.AnalyticsPreferenceSelected, properties: { - has_marketing_consent: true, + has_marketing_consent: consent, location: 'marketing_consent_modal', }, }); @@ -849,10 +837,10 @@ export default class Home extends PureComponent { gap={2} width={BlockSize.Full} > - - From 085e4821f0d7ce63e1100948c14fec676e4d7df9 Mon Sep 17 00:00:00 2001 From: Jonathan Bursztyn Date: Mon, 10 Jun 2024 08:55:09 +0200 Subject: [PATCH 35/36] Update ui store action method --- ui/store/actions.ts | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/ui/store/actions.ts b/ui/store/actions.ts index e38cc88c1811..4fc26985ddce 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -3290,33 +3290,14 @@ export function setDataCollectionForMarketing( unknown, AnyAction > { - return (dispatch: MetaMaskReduxDispatch) => { + return async (dispatch: MetaMaskReduxDispatch) => { log.debug(`background.setDataCollectionForMarketing`); - return new Promise((resolve, reject) => { - callBackgroundMethod( - 'setDataCollectionForMarketing', - [dataCollectionPreference], - (err, metaMetricsId) => { - log.debug(err); - if (err) { - dispatch(displayWarning(err)); - reject(err); - return; - } - /** - * We need to inform sentry that the user's optin preference may have - * changed. The logic to determine which way to toggle is in the - * toggleSession handler in setupSentry.js. - */ - window.sentry?.toggleSession(); - - dispatch({ - type: actionConstants.SET_DATA_COLLECTION_FOR_MARKETING, - value: dataCollectionPreference, - }); - resolve([dataCollectionPreference, metaMetricsId as string]); - }, - ); + await submitRequestToBackground('setDataCollectionForMarketing', [ + dataCollectionPreference, + ]); + dispatch({ + type: actionConstants.SET_DATA_COLLECTION_FOR_MARKETING, + value: dataCollectionPreference, }); }; } From 0dc9878780ef75ba03bff37a8e7e284e8ffe6ec9 Mon Sep 17 00:00:00 2001 From: David Walsh Date: Tue, 11 Jun 2024 19:05:19 -0500 Subject: [PATCH 36/36] Embed link, add localization description --- app/_locales/en/messages.json | 3 ++- builds.yml | 1 - ui/helpers/constants/common.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index ccb88c093694..745e60244e44 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3352,7 +3352,8 @@ "message": "MetaMetrics" }, "onboardedMetametricsParagraph1": { - "message": "In addition to $1, we'd like to use data to understand how you interact with marketing communications." + "message": "In addition to $1, we'd like to use data to understand how you interact with marketing communications.", + "description": "$1 represents the 'onboardedMetametricsLink' locale string" }, "onboardedMetametricsParagraph2": { "message": "This helps us personalize what we share with you, like:" diff --git a/builds.yml b/builds.yml index c6837ae32cd1..626d9ef82eb4 100644 --- a/builds.yml +++ b/builds.yml @@ -189,7 +189,6 @@ env: - DEBUG: null - SUPPORT_LINK: https://support.metamask.io - SUPPORT_REQUEST_LINK: https://metamask.zendesk.com/hc/en-us - - METAMETRICS_SETTINGS_LINK: https://support.metamask.io/privacy-and-security/how-to-manage-your-metametrics-settings - SKIP_BACKGROUND_INITIALIZATION: false - ENABLE_MV3: true diff --git a/ui/helpers/constants/common.ts b/ui/helpers/constants/common.ts index ba9d6868ae6b..8170536690b0 100644 --- a/ui/helpers/constants/common.ts +++ b/ui/helpers/constants/common.ts @@ -10,7 +10,8 @@ export const MMI_WEB_SITE = _mmiWebSite; ///: END:ONLY_INCLUDE_IF // eslint-disable-next-line prefer-destructuring -export const METAMETRICS_SETTINGS_LINK = process.env.METAMETRICS_SETTINGS_LINK; +export const METAMETRICS_SETTINGS_LINK = + 'https://support.metamask.io/privacy-and-security/how-to-manage-your-metametrics-settings'; // eslint-disable-next-line prefer-destructuring export const SUPPORT_REQUEST_LINK = process.env.SUPPORT_REQUEST_LINK; export const CONTRACT_ADDRESS_LINK = _contractAddressLink;