Skip to content

Commit

Permalink
fix (cherry-pick) : hide privacy policy toast during onboarding, and …
Browse files Browse the repository at this point in the history
…for onboarded users (#25303)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

Fixes two bugs:
- Privacy policy toast should not be seen during onboarding
- Users who onboarded after the privacy policy date should not see the
new privacy policy toast.

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25217?quickstart=1)

## **Related issues**

Fixes:

## **Manual testing steps**

New user onbaording after the date:
1. Set the privacy policy date to a date in the future from today
2. Onboard (create a new user)
3. User should not see the privacy policy toast at any moment

New user onboarding before the date:
1. Set the privacy policy date to a date in the past from today.
2. Onboard (create a new user)
3. User should not see the privacy policy toast at any moment
4. Set the privacy policy date to a date in the future from today
5. User should not see the privacy policy toast on the home screen

Old user after the privacy policy date:
1. Have a user already onboarded
2. Make sure the `onboardingDate` is `null` in the redux state (this
simulates a user who has registered before this PR has been in
production)
3. Set the privacy policy date to a date in the future from today
4. User should not see the new privacy policy toast

Old user before the privacy policy date:
1. Have a user onboarded
2. Make sure the `onboardingDate` is `null` in the redux state (this
simulates a user who has registered before this PR has been in
production)
3. Set the privacy policy date to a date in the past before today
4. User should see the new privacy policy toast

TL;DR: If the privacy policy date is in the future, the user should
never see it -BUT- if the privacy policy is in the past, the user should
see it, as long as the user onboarded before the privacy policy date.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

Co-authored-by: David Walsh <[email protected]>
Co-authored-by: Brian Bergeron <[email protected]>
Co-authored-by: NidhiKJha <[email protected]>
Co-authored-by: Nidhi Kumari <[email protected]>
  • Loading branch information
5 people authored Jun 13, 2024
1 parent 9d6eaa1 commit 3464aea
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 12 deletions.
7 changes: 7 additions & 0 deletions app/scripts/controllers/app-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default class AppStateController extends EventEmitter {
showAccountBanner: true,
trezorModel: null,
currentPopupId: undefined,
onboardingDate: null,
newPrivacyPolicyToastClickedOrClosed: null,
newPrivacyPolicyToastShownDate: null,
// This key is only used for checking if the user had set advancedGasFee
Expand Down Expand Up @@ -186,6 +187,12 @@ export default class AppStateController extends EventEmitter {
});
}

setOnboardingDate() {
this.store.updateState({
onboardingDate: Date.now(),
});
}

setNewPrivacyPolicyToastClickedOrClosed() {
this.store.updateState({
newPrivacyPolicyToastClickedOrClosed: true,
Expand Down
1 change: 1 addition & 0 deletions app/scripts/lib/setupSentry.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const SENTRY_BACKGROUND_STATE = {
browserEnvironment: true,
connectedStatusPopoverHasBeenShown: true,
currentPopupId: false,
onboardingDate: false,
currentExtensionPopupId: false,
defaultHomeActiveTabName: true,
fullScreenGasPollTokens: true,
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3184,6 +3184,8 @@ export default class MetamaskController extends EventEmitter {
appStateController.setSurveyLinkLastClickedOrClosed.bind(
appStateController,
),
setOnboardingDate:
appStateController.setOnboardingDate.bind(appStateController),
setNewPrivacyPolicyToastClickedOrClosed:
appStateController.setNewPrivacyPolicyToastClickedOrClosed.bind(
appStateController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"showNetworkBanner": true,
"showAccountBanner": true,
"trezorModel": null,
"onboardingDate": "object",
"hadAdvancedGasFeesSetPriorToMigration92_3": false,
"nftsDropdownState": {},
"termsOfUseLastAgreed": "number",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"showNetworkBanner": true,
"showAccountBanner": true,
"trezorModel": null,
"onboardingDate": "object",
"hadAdvancedGasFeesSetPriorToMigration92_3": false,
"nftsDropdownState": {},
"termsOfUseLastAgreed": "number",
Expand Down
5 changes: 5 additions & 0 deletions ui/pages/onboarding-flow/onboarding-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
createNewVaultAndGetSeedPhrase,
unlockAndGetSeedPhrase,
createNewVaultAndRestore,
setOnboardingDate,
} from '../../store/actions';
import { getFirstTimeFlowTypeRouteAfterUnlock } from '../../selectors';
import { MetaMetricsContext } from '../../contexts/metametrics';
Expand Down Expand Up @@ -70,6 +71,10 @@ export default function OnboardingFlow() {
const isFromReminder = new URLSearchParams(search).get('isFromReminder');
const trackEvent = useContext(MetaMetricsContext);

useEffect(() => {
dispatch(setOnboardingDate());
}, [dispatch]);

useEffect(() => {
if (completedOnboarding && !isFromReminder) {
history.push(DEFAULT_ROUTE);
Expand Down
1 change: 1 addition & 0 deletions ui/pages/onboarding-flow/onboarding-flow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jest.mock('../../store/actions', () => ({
createNewVaultAndGetSeedPhrase: jest.fn().mockResolvedValue(null),
unlockAndGetSeedPhrase: jest.fn().mockResolvedValue(null),
createNewVaultAndRestore: jest.fn(),
setOnboardingDate: jest.fn(() => ({ type: 'TEST_DISPATCH' })),
}));

describe('Onboarding Flow', () => {
Expand Down
8 changes: 5 additions & 3 deletions ui/pages/routes/routes.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,13 @@ export default class Routes extends Component {
const isPrivacyToastRecent = this.getIsPrivacyToastRecent();
const isPrivacyToastNotShown = !newPrivacyPolicyToastShownDate;

if (!this.onHomeScreen()) {
return null;
}

return (
<ToastContainer>
{showConnectAccountToast &&
this.onHomeScreen() &&
!this.state.hideConnectAccountToast ? (
{showConnectAccountToast && !this.state.hideConnectAccountToast ? (
<Toast
key="connect-account-toast"
startAdornment={
Expand Down
35 changes: 33 additions & 2 deletions ui/pages/routes/routes.component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import configureMockStore from 'redux-mock-store';
import { act, fireEvent } from '@testing-library/react';

import { SEND_STAGES } from '../../ducks/send';
import { renderWithProvider } from '../../../test/jest';
import mockSendState from '../../../test/data/mock-send-state.json';
import {
CONFIRMATION_V_NEXT_ROUTE,
DEFAULT_ROUTE,
} from '../../helpers/constants/routes';
import {
CHAIN_IDS,
GOERLI_DISPLAY_NAME,
NETWORK_TYPES,
} from '../../../shared/constants/network';
import { renderWithProvider } from '../../../test/jest';
import mockSendState from '../../../test/data/mock-send-state.json';
import mockState from '../../../test/data/mock-state.json';
import { useIsOriginalNativeTokenSymbol } from '../../hooks/useIsOriginalNativeTokenSymbol';
import Routes from '.';

Expand Down Expand Up @@ -182,3 +187,29 @@ describe('Routes Component', () => {
});
});
});

describe('toast display', () => {
const testState = {
...mockState,
metamask: {
...mockState.metamask,
announcements: {},
approvalFlows: [],
completedOnboarding: true,
usedNetworks: [],
swapsState: { swapsFeatureIsLive: true },
},
};

it('renders toastContainer on default route', async () => {
await render([DEFAULT_ROUTE], testState);
const toastContainer = document.querySelector('.toasts-container');
expect(toastContainer).toBeInTheDocument();
});

it('does not render toastContainer on confirmation route', async () => {
await render([CONFIRMATION_V_NEXT_ROUTE], testState);
const toastContainer = document.querySelector('.toasts-container');
expect(toastContainer).not.toBeInTheDocument();
});
});
14 changes: 12 additions & 2 deletions ui/selectors/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1878,11 +1878,17 @@ export function getShowSurveyToast(state) {
* @returns {boolean} True if the current date is on or after the new privacy policy date and the privacy policy toast was not clicked or closed. False otherwise.
*/
export function getShowPrivacyPolicyToast(state) {
const { newPrivacyPolicyToastClickedOrClosed } = state.metamask;
const { newPrivacyPolicyToastClickedOrClosed, onboardingDate } =
state.metamask;
const newPrivacyPolicyDate = new Date(PRIVACY_POLICY_DATE);
const currentDate = new Date(Date.now());
return (
!newPrivacyPolicyToastClickedOrClosed && currentDate >= newPrivacyPolicyDate
!newPrivacyPolicyToastClickedOrClosed &&
currentDate >= newPrivacyPolicyDate &&
// users who onboarded before the privacy policy date should see the notice
// and
// old users who don't have onboardingDate set should see the notice
(onboardingDate < newPrivacyPolicyDate || !onboardingDate)
);
}

Expand All @@ -1899,6 +1905,10 @@ export function getNewPrivacyPolicyToastShownDate(state) {
return state.metamask.newPrivacyPolicyToastShownDate;
}

export function getOnboardingDate(state) {
return state.metamask.onboardingDate;
}

export function getShowBetaHeader(state) {
return state.metamask.showBetaHeader;
}
Expand Down
55 changes: 50 additions & 5 deletions ui/selectors/selectors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1259,30 +1259,46 @@ describe('Selectors', () => {

dateNowSpy = jest
.spyOn(Date, 'now')
.mockReturnValue(dayAfterPolicyDate);
.mockReturnValue(dayAfterPolicyDate.getTime());
});

afterEach(() => {
dateNowSpy.mockRestore();
});

it('shows the privacy policy toast when not yet seen and on or after the policy date', () => {
it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: null,
onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate(
new Date(PRIVACY_POLICY_DATE).getDate() - 2,
),
},
});
expect(result).toBe(true);
});

it('does not show the privacy policy toast when seen and on or after the policy date', () => {
it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: true,
onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate(
new Date(PRIVACY_POLICY_DATE).getDate() - 2,
),
},
});
expect(result).toBe(false);
});

it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: null,
onboardingDate: null,
},
});
expect(result).toBe(true);
});
});

describe('mock same day', () => {
Expand All @@ -1296,23 +1312,39 @@ describe('Selectors', () => {
dateNowSpy.mockRestore();
});

it('shows the privacy policy toast when not yet seen and on or after the policy date', () => {
it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is before the policy date', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: null,
onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate(
new Date(PRIVACY_POLICY_DATE).getDate() - 2,
),
},
});
expect(result).toBe(true);
});

it('does not show the privacy policy toast when seen and on or after the policy date', () => {
it('does not show the privacy policy toast when seen, even if on or after the policy date and onboardingDate is before the policy date', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: true,
onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate(
new Date(PRIVACY_POLICY_DATE).getDate() - 2,
),
},
});
expect(result).toBe(false);
});

it('shows the privacy policy toast when not yet seen, on or after the policy date, and onboardingDate is not set', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: null,
onboardingDate: null,
},
});
expect(result).toBe(true);
});
});

describe('mock day before', () => {
Expand All @@ -1333,6 +1365,19 @@ describe('Selectors', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: null,
onboardingDate: new Date(PRIVACY_POLICY_DATE).setDate(
new Date(PRIVACY_POLICY_DATE).getDate() - 2,
),
},
});
expect(result).toBe(false);
});

it('does not show the privacy policy toast before the policy date even if onboardingDate is not set', () => {
const result = selectors.getShowPrivacyPolicyToast({
metamask: {
newPrivacyPolicyToastClickedOrClosed: null,
onboardingDate: null,
},
});
expect(result).toBe(false);
Expand Down
6 changes: 6 additions & 0 deletions ui/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4231,6 +4231,12 @@ export function setNewPrivacyPolicyToastClickedOrClosed() {
};
}

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

export function setNewPrivacyPolicyToastShownDate(time: number) {
return async () => {
await submitRequestToBackground('setNewPrivacyPolicyToastShownDate', [
Expand Down

0 comments on commit 3464aea

Please sign in to comment.