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

Use gu_hide_support_messaging cookie as the only source of truth for support messaging #13037

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 0 additions & 2 deletions dotcom-rendering/src/client/userFeatures/user-features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ const validateResponse = (
isObject(response) &&
isBoolean(response.showSupportMessaging) &&
isObject(response.contentAccess) &&
isBoolean(response.contentAccess.paidMember) &&
isBoolean(response.contentAccess.recurringContributor) &&
Copy link
Member Author

@rupertbates rupertbates Dec 19, 2024

Choose a reason for hiding this comment

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

These were just missed in #12983

isBoolean(response.contentAccess.digitalPack)
);
};
Expand Down
85 changes: 6 additions & 79 deletions dotcom-rendering/src/lib/contributions.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { setCookie, storage } from '@guardian/libs';
import MockDate from 'mockdate';
import {
getLastOneOffContributionTimestamp,
hasSupporterCookie,
HIDE_SUPPORT_MESSAGING_COOKIE,
isRecentOneOffContributor,
NO_RR_BANNER_KEY,
recentlyClosedBanner,
setLocalNoBannerCachePeriod,
SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
shouldHideSupportMessaging,
withinLocalNoBannerCachePeriod,
} from './contributions';

Expand All @@ -21,75 +17,6 @@ const clearAllCookies = () => {
}
};

describe('getLastOneOffContributionTimestamp', () => {
beforeEach(clearAllCookies);

it('returns a support cookie date if found', () => {
const somePastDate = 1582567969093;
setCookie({
name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
value: String(somePastDate),
});
const lastOneOffContributionDate = getLastOneOffContributionTimestamp();
expect(lastOneOffContributionDate).toBe(somePastDate);
});

it('returns undefined if the date cannot be parsed correctly', () => {
setCookie({
name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
value: 'NOT_A_DATE',
});

const lastOneOffContributionDate = getLastOneOffContributionTimestamp();
expect(lastOneOffContributionDate).toBeUndefined();
});

it('returns an empty string if no one-off contribution found', () => {
const lastOneOffContributionDate = getLastOneOffContributionTimestamp();
expect(lastOneOffContributionDate).toBeUndefined();
});
});

describe('isRecentOneOffContributor', () => {
beforeEach(clearAllCookies);
afterEach(() => {
MockDate.reset();
});

it('returns false if there is no one-off contribution cookie', () => {
expect(isRecentOneOffContributor()).toBe(false);
});

it('returns true if there are 5 days between the last contribution date and now', () => {
setCookie({
name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
value: Date.parse('2018-08-01').toString(),
});

MockDate.set(Date.parse('2018-08-07T10:50:34'));
expect(isRecentOneOffContributor()).toBe(true);
});

it('returns true if there are 0 days between the last contribution date and now', () => {
const theDate = Date.parse('2018-08-01T13:00:30');
setCookie({
name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
value: theDate.toString(),
});
MockDate.set(theDate);
expect(isRecentOneOffContributor()).toBe(true);
});

it('returns false if the one-off contribution was more than 3 months ago', () => {
setCookie({
name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
value: Date.parse('2018-08-01').toString(),
});
MockDate.set(Date.parse('2019-08-01T13:00:30'));
expect(isRecentOneOffContributor()).toBe(false);
});
});

describe('getPurchaseInfo', () => {
let getPurchaseInfo: () => any;

Expand Down Expand Up @@ -176,30 +103,30 @@ describe('withinLocalNoBannerCachePeriod', () => {
});
});

describe('hasSupporterCookie', () => {
describe('shouldHideSupportMessaging', () => {
Copy link
Member Author

@rupertbates rupertbates Dec 19, 2024

Choose a reason for hiding this comment

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

shouldHideSupportMessging is exactly equivalent to hasSupporterCookie now the one-time stuff has been removed, so hasSupporterCookie is no longer needed.

beforeEach(clearAllCookies);

it('returns false if cookie exists and is set to false', () => {
setCookie({
name: HIDE_SUPPORT_MESSAGING_COOKIE,
value: 'false',
});
expect(hasSupporterCookie(true)).toEqual(false);
expect(shouldHideSupportMessaging(true)).toEqual(false);
});

it('returns true if cookie exists and is set to true', () => {
setCookie({
name: HIDE_SUPPORT_MESSAGING_COOKIE,
value: 'true',
});
expect(hasSupporterCookie(true)).toEqual(true);
expect(shouldHideSupportMessaging(true)).toEqual(true);
});

it('returns false if cookie does not exist and user is signed out', () => {
expect(hasSupporterCookie(false)).toEqual(false);
expect(shouldHideSupportMessaging(false)).toEqual(false);
});

it('returns Pending if cookie does not exist and user is signed in', () => {
expect(hasSupporterCookie(true)).toEqual('Pending');
expect(shouldHideSupportMessaging(true)).toEqual('Pending');
});
});
72 changes: 5 additions & 67 deletions dotcom-rendering/src/lib/contributions.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import {
getCookie,
isUndefined,
onConsentChange,
storage,
} from '@guardian/libs';
import { getCookie, onConsentChange, storage } from '@guardian/libs';
import type { HeaderPayload } from '@guardian/support-dotcom-components/dist/dotcom/types';
import { useEffect, useState } from 'react';
import type { ArticleDeprecated } from '../types/article';
import type { DCRFrontType } from '../types/front';
import type { DCRNewslettersPageType } from '../types/newslettersPage';
import type { DCRTagPageType } from '../types/tagPage';

// User Attributes API cookies (created on sign-in)
// User Attributes API cookie (created on sign-in)
export const HIDE_SUPPORT_MESSAGING_COOKIE = 'gu_hide_support_messaging';
export const RECURRING_CONTRIBUTOR_COOKIE = 'gu_recurring_contributor';
export const OPT_OUT_OF_ARTICLE_COUNT_COOKIE = 'gu_article_count_opt_out';

// Support Frontend cookie (created when a contribution is made)
export const SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE =
'gu.contributions.contrib-timestamp';
export const OPT_OUT_OF_ARTICLE_COUNT_COOKIE = 'gu_article_count_opt_out';

// Local storage keys
const WEEKLY_ARTICLE_COUNT_KEY = 'gu.history.weeklyArticleCount';
Expand All @@ -28,12 +19,8 @@ export const NO_RR_BANNER_KEY = 'gu.noRRBanner';
export const MODULES_VERSION = 'v3';

// Returns true if we should hide support messaging because the user is a supporter.
// Checks the cookie that is set by the User Attributes API upon signing in.
// Value computed server-side and looks at all of the user's active products,
// including but not limited to recurring & one-off contributions,
// paper & digital subscriptions, as well as user tiers (GU supporters/staff/partners/patrons).
// https://github.com/guardian/members-data-api/blob/3a72dc00b9389968d91e5930686aaf34d8040c52/membership-attribute-service/app/models/Attributes.scala
export const hasSupporterCookie = (
// Checks the cookie that is set by support-frontend on checkout and the User Attributes API upon signing in.
export const shouldHideSupportMessaging = (
isSignedIn: boolean,
): boolean | 'Pending' => {
const cookie = getCookie({ name: HIDE_SUPPORT_MESSAGING_COOKIE });
Expand All @@ -56,55 +43,6 @@ export const hasSupporterCookie = (
}
};

// looks at the SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE (set by support-frontend when making one-off contribution)
// and returns a Unix epoch int of the date if it exists.
export const getLastOneOffContributionTimestamp = (): number | undefined => {
// Support cookies - expects Unix epoch
const contributionDateFromSupport = getCookie({
name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
});

if (!contributionDateFromSupport) {
return undefined;
}

// Parse dates into common a number
const parsedDateFromSupport = contributionDateFromSupport
? parseInt(contributionDateFromSupport, 10)
: 0;

return parsedDateFromSupport || undefined; // This guards against 'parsedDateFromSupport' being NaN
};

const dateDiffDays = (from: number, to: number): number => {
const oneDayMs = 1000 * 60 * 60 * 24;
const diffMs = to - from;
return Math.floor(diffMs / oneDayMs);
};

const AskPauseDays = 90;

export const isRecentOneOffContributor = (): boolean => {
const lastContributionDate = getLastOneOffContributionTimestamp();
if (!isUndefined(lastContributionDate)) {
const now = Date.now();
return dateDiffDays(lastContributionDate, now) <= AskPauseDays;
}

return false;
};

export const shouldHideSupportMessaging = (
isSignedIn: boolean,
): boolean | 'Pending' => {
const hasCookie = hasSupporterCookie(isSignedIn);
if (hasCookie === 'Pending') {
return 'Pending';
} else {
return hasCookie || isRecentOneOffContributor();
}
};

export const REQUIRED_CONSENTS_FOR_ARTICLE_COUNT = [1, 3, 7];
const REQUIRED_CONSENTS_FOR_BROWSER_ID = [1, 3, 5, 7];

Expand Down
22 changes: 2 additions & 20 deletions dotcom-rendering/src/lib/readerRevenueDevUtils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import { getCookie, removeCookie, setCookie, storage } from '@guardian/libs';
import {
HIDE_SUPPORT_MESSAGING_COOKIE,
RECURRING_CONTRIBUTOR_COOKIE,
SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
} from './contributions';
import { HIDE_SUPPORT_MESSAGING_COOKIE } from './contributions';
import { getLocaleCode } from './getCountryCode';

const readerRevenueCookies = [
HIDE_SUPPORT_MESSAGING_COOKIE,
RECURRING_CONTRIBUTOR_COOKIE,
SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
];
const readerRevenueCookies = [HIDE_SUPPORT_MESSAGING_COOKIE];

const clearEpicViewLog = (): void =>
storage.local.remove('gu.contributions.views');
Expand All @@ -22,12 +14,6 @@ const clearBannerLastClosedAt = (): void => {
storage.local.remove('gu.noRRBannerTimestamp');
};

const fakeOneOffContributor = (): void =>
setCookie({
name: SUPPORT_ONE_OFF_CONTRIBUTION_COOKIE,
value: Date.now().toString(),
});

const MULTIVARIATE_ID_COOKIE = 'GU_mvt_id';
const MAX_CLIENT_MVT_ID = 1000000;
const incrementMvtCookie = (): void => {
Expand Down Expand Up @@ -76,10 +62,6 @@ const clearCommonReaderRevenueStateAndReload = (
for (const cookie of readerRevenueCookies) removeCookie({ name: cookie });
clearEpicViewLog();

if (asExistingSupporter) {
fakeOneOffContributor();
}

window.location.reload();
};

Expand Down
Loading