Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add power users survey support #27361

Merged
merged 60 commits into from
Oct 3, 2024
Merged
Changes from 3 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
e92ed6c
Add power user survey support
jonybur Sep 24, 2024
09bbad8
Add metametrics
jonybur Sep 24, 2024
cc7de50
Add metametrics, basic functionality gates
jonybur Sep 24, 2024
f3284e7
Add test, bugfix
jonybur Sep 24, 2024
5d985d9
Lint
jonybur Sep 24, 2024
67532c7
Add snapshot matching
jonybur Sep 24, 2024
b89dbcb
Add snapshot
jonybur Sep 24, 2024
3de0419
Add test
jonybur Sep 24, 2024
8f1c289
Fix crash
jonybur Sep 24, 2024
9397653
Close toast
jonybur Sep 24, 2024
4019875
Revisions
jonybur Sep 25, 2024
273de84
Fix test
jonybur Sep 25, 2024
99f1e3f
Fix snapshot
jonybur Sep 25, 2024
c314570
lint
jonybur Sep 25, 2024
7ae015e
Readd test
jonybur Sep 25, 2024
fc93fd6
Improve tests
jonybur Sep 25, 2024
9c82a0d
Merge branch 'develop' into jb-power-users
jonybur Sep 25, 2024
d6200a7
Remove unused toast
jonybur Sep 25, 2024
9a9d65c
Use metametrics id
jonybur Sep 25, 2024
8b9ba3e
Remove index 0 to get survey
jonybur Sep 26, 2024
7332167
Rename surveyId to id
jonybur Sep 26, 2024
37f8fc3
Avoid erroring
jonybur Sep 27, 2024
7309e54
Use prod URL
jonybur Sep 27, 2024
6929a76
Mock endpoint
jonybur Sep 27, 2024
a1ef8fe
Add to builds.yml
jonybur Sep 27, 2024
a10f0df
Update snap
jonybur Sep 27, 2024
b1a785e
Fix test
jonybur Sep 27, 2024
0d1e0d5
Update snap
jonybur Sep 27, 2024
74ec16e
Fix tests
jonybur Sep 27, 2024
1f40a5c
Merge branch 'develop' into jb-power-users
jonybur Sep 27, 2024
2d9097a
Output MMID
jonybur Sep 27, 2024
da0279c
Merge branch 'jb-power-users' of github.com:MetaMask/metamask-extensi…
jonybur Sep 27, 2024
91356d9
Fix mock
jonybur Sep 27, 2024
60db42c
Edit mock
jonybur Sep 27, 2024
a69cd62
Fix e2e mock
jonybur Sep 27, 2024
7c6f2d1
Add e2e
jonybur Sep 27, 2024
c9f6436
Update tests
jonybur Sep 27, 2024
b44976c
Fix tests
jonybur Sep 27, 2024
2c0cdef
Fix test
jonybur Sep 27, 2024
d52a76d
Separate mock
jonybur Sep 27, 2024
e62983a
Add power user mock
jonybur Sep 27, 2024
ed63fdb
Update privacy snapshot
jonybur Sep 27, 2024
a5110eb
Lint fix
jonybur Sep 27, 2024
9cc3b03
Update snaps
jonybur Sep 30, 2024
6325061
Merge branch 'develop' into jb-power-users
jonybur Sep 30, 2024
824c6a3
Add mock endpoint
jonybur Sep 30, 2024
6a4e227
Merge branch 'jb-power-users' of github.com:MetaMask/metamask-extensi…
jonybur Sep 30, 2024
bfd455a
Fix mock
jonybur Sep 30, 2024
9313acd
Disable toast when participateInMetametrics is off
jonybur Sep 30, 2024
ddd696f
Update test
jonybur Sep 30, 2024
fa83767
Fix test
jonybur Sep 30, 2024
2c3338d
Update endpoints
jonybur Sep 30, 2024
763be5f
Add e2e test, fix mock endpoints
jonybur Oct 2, 2024
9df3c50
Merge branch 'develop' into jb-power-users
jonybur Oct 2, 2024
24b4986
Fix test
jonybur Oct 2, 2024
ec9a8e0
Merge branch 'jb-power-users' of github.com:MetaMask/metamask-extensi…
jonybur Oct 2, 2024
a7b9f4b
Change name of test
jonybur Oct 2, 2024
a42bd5f
Merge branch 'develop' of github.com:MetaMask/metamask-extension into…
jonybur Oct 2, 2024
4e8c2c8
Merge branch 'develop' into jb-power-users
jonybur Oct 2, 2024
887de62
Merge branch 'develop' into jb-power-users
jonybur Oct 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/scripts/controllers/app-state.js
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@ export default class AppStateController extends EventEmitter {
trezorModel: null,
currentPopupId: undefined,
onboardingDate: null,
lastViewedUserSurvey: null,
newPrivacyPolicyToastClickedOrClosed: null,
newPrivacyPolicyToastShownDate: null,
// This key is only used for checking if the user had set advancedGasFee
@@ -196,6 +197,12 @@ export default class AppStateController extends EventEmitter {
});
}

setLastViewedUserSurvey(id) {
this.store.updateState({
lastViewedUserSurvey: id,
});
}

setNewPrivacyPolicyToastClickedOrClosed() {
this.store.updateState({
newPrivacyPolicyToastClickedOrClosed: true,
2 changes: 2 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
@@ -3467,6 +3467,8 @@ export default class MetamaskController extends EventEmitter {
),
setOnboardingDate:
appStateController.setOnboardingDate.bind(appStateController),
setLastViewedUserSurvey:
appStateController.setLastViewedUserSurvey.bind(appStateController),
setNewPrivacyPolicyToastClickedOrClosed:
appStateController.setNewPrivacyPolicyToastClickedOrClosed.bind(
appStateController,
2 changes: 2 additions & 0 deletions shared/constants/metametrics.ts
Original file line number Diff line number Diff line change
@@ -617,6 +617,7 @@ export enum MetaMetricsEventName {
SrpCopiedToClipboard = 'Copies SRP to clipboard',
SrpToConfirmBackup = 'SRP Backup Confirm Displayed',
StakingEntryPointClicked = 'Stake Button Clicked',
SurveyToast = 'Survey Toast',
SupportLinkClicked = 'Support Link Clicked',
TermsOfUseShown = 'Terms of Use Shown',
TermsOfUseAccepted = 'Terms of Use Accepted',
@@ -774,6 +775,7 @@ export enum MetaMetricsEventCategory {
Retention = 'Retention',
Send = 'Send',
Settings = 'Settings',
Feedback = 'Feedback',
Snaps = 'Snaps',
Swaps = 'Swaps',
Tokens = 'Tokens',
1 change: 1 addition & 0 deletions ui/components/ui/survey-toast/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SurveyToast } from './survey-toast';
128 changes: 128 additions & 0 deletions ui/components/ui/survey-toast/survey-toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { useEffect, useState, useContext } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import fetchWithCache from '../../../../shared/lib/fetch-with-cache';
import { DAY } from '../../../../shared/constants/time';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import {
MetaMetricsEventCategory,
MetaMetricsEventName,
} from '../../../../shared/constants/metametrics';
import {
getSelectedInternalAccount,
getLastViewedUserSurvey,
getUseExternalServices,
getParticipateInMetaMetrics,
} from '../../../selectors';
import { setLastViewedUserSurvey } from '../../../store/actions';
import { Toast } from '../../multichain';

type Survey = {
url: string;
description: string;
cta: string;
surveyId: number;
};

export function SurveyToast() {
const [survey, setSurvey] = useState<Survey | null>(null);
const dispatch = useDispatch();
const trackEvent = useContext(MetaMetricsContext);
const lastViewedUserSurvey = useSelector(getLastViewedUserSurvey);
const participateInMetaMetrics = useSelector(getParticipateInMetaMetrics);
const basicFunctionality = useSelector(getUseExternalServices);
const internalAccount = useSelector(getSelectedInternalAccount);

useEffect(() => {
if (!basicFunctionality) {
return;
}

const surveyId = 1;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is set to 1, we should remove this

const surveyUrl = `https://accounts.dev-api.cx.metamask.io/v1/users/${internalAccount.address}/surveys?surveyId=${surveyId}`;

const fetchSurvey = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can useCallback for fetchSurvey
const fetchSurvey = useCallback(async () => { ... }, [ internalAccount?.address, lastViewedUserSurvey, basicFunctionality, metaMetricsId, fetchSurvey, ]);

try {
const response = await fetchWithCache({
url: surveyUrl,
fetchOptions: {
method: 'GET',
headers: {
'x-metamask-clientproduct': 'metamask-extension',
},
},
functionName: 'fetchSurveys',
cacheOptions: { cacheRefreshTime: DAY * 7 },
});

const _survey: Survey = response?.surveys?.[0];

if (
response.surveys.length === 0 ||
!_survey ||
lastViewedUserSurvey <= _survey.surveyId
) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You can improve the readability and efficiency of this code by simplifying the condition. Instead of checking if the object keys length is zero, you can directly check if the survey is falsy or if the survey ID is less than or equal to the last viewed survey ID.

if (!_survey?.id || _survey.id <= lastViewedUserSurvey) { return; }


setSurvey(_survey);
} catch (error) {
console.error('Failed to fetch survey:', error);
}
};

fetchSurvey();
}, [
internalAccount.address,
lastViewedUserSurvey,
basicFunctionality,
dispatch,
]);

function handleActionClick() {
if (!survey) {
return;
}
window.open(survey.url, '_blank');
dispatch(setLastViewedUserSurvey(survey.surveyId));
trackAction('accept');
}

function handleClose() {
if (!survey) {
return;
}
dispatch(setLastViewedUserSurvey(survey.surveyId));
trackAction('deny');
}

function trackAction(response: 'accept' | 'deny') {
if (!participateInMetaMetrics || !survey) {
return;
}

trackEvent({
event: MetaMetricsEventName.SurveyToast,
category: MetaMetricsEventCategory.Feedback,
properties: {
response,
survey: survey.surveyId,
},
});
}

if (!survey) {
return null;
}

return (
<Toast
dataTestId="survey-toast"
key="survey-toast"
text={survey.description}
actionText={survey.cta}
onActionClick={handleActionClick}
onClose={handleClose}
startAdornment={undefined}
/>
);
}
2 changes: 2 additions & 0 deletions ui/pages/routes/routes.component.js
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ import {
ToastContainer,
Toast,
} from '../../components/multichain';
import { SurveyToast } from '../../components/ui/survey-toast';
import UnlockPage from '../unlock-page';
import Alerts from '../../components/app/alerts';
import Asset from '../asset';
@@ -651,6 +652,7 @@ export default class Routes extends Component {

return (
<ToastContainer>
<SurveyToast />
{showConnectAccountToast &&
!this.state.hideConnectAccountToast &&
isEvmAccount ? (
4 changes: 4 additions & 0 deletions ui/selectors/selectors.js
Original file line number Diff line number Diff line change
@@ -1937,6 +1937,10 @@ export function getShowPrivacyPolicyToast(state) {
);
}

export function getLastViewedUserSurvey(state) {
return state.metamask.lastViewedUserSurvey;
}

export function getShowOutdatedBrowserWarning(state) {
const { outdatedBrowserWarningLastShown } = state.metamask;
if (!outdatedBrowserWarningLastShown) {
6 changes: 6 additions & 0 deletions ui/store/actions.ts
Original file line number Diff line number Diff line change
@@ -4144,6 +4144,12 @@ export function setNewPrivacyPolicyToastClickedOrClosed() {
};
}

export function setLastViewedUserSurvey(id: number) {
return async () => {
await submitRequestToBackground('setLastViewedUserSurvey', [id]);
};
}

export function setOnboardingDate() {
return async () => {
await submitRequestToBackground('setOnboardingDate');