diff --git a/libs/shared/assets/src/images/pocket-text-logo.svg b/libs/shared/assets/src/images/pocket-text-logo.svg
new file mode 100644
index 00000000000..2a0127844d2
--- /dev/null
+++ b/libs/shared/assets/src/images/pocket-text-logo.svg
@@ -0,0 +1 @@
+
diff --git a/libs/shared/l10n/src/lib/branding.ftl b/libs/shared/l10n/src/lib/branding.ftl
index d542c7ac9c3..773db844295 100644
--- a/libs/shared/l10n/src/lib/branding.ftl
+++ b/libs/shared/l10n/src/lib/branding.ftl
@@ -48,6 +48,7 @@
-product-mozilla-monitor-short = Monitor
-product-firefox-relay = Firefox Relay
-product-firefox-relay-short = Relay
+-product-pocket = Pocket
-brand-apple = Apple
-brand-apple-pay = Apple Pay
diff --git a/packages/fxa-settings/src/components/CardHeader/index.stories.tsx b/packages/fxa-settings/src/components/CardHeader/index.stories.tsx
index d82349d08eb..d0e24b66179 100644
--- a/packages/fxa-settings/src/components/CardHeader/index.stories.tsx
+++ b/packages/fxa-settings/src/components/CardHeader/index.stories.tsx
@@ -70,8 +70,7 @@ export const WithSeparateSubheadingDefaultServiceName = storyWithProps(
headingText: MOCK_HEADING,
headingTextFtlId: MOCK_DEFAULT_HEADING_FTL_ID,
subheadingWithDefaultServiceFtlId: MOCK_DEFAULT_HEADING_FTL_ID,
- subheadingWithCustomServiceFtlId: MOCK_CUSTOM_HEADING_FTL_ID,
- serviceName: MozServices.Default,
+ subheadingWithLogoFtlId: MOCK_DEFAULT_HEADING_FTL_ID,
},
'Separate l10n for subheading, with default service'
);
@@ -81,8 +80,19 @@ export const WithSeparateSubheadingCustomServiceName = storyWithProps(
serviceName: MOCK_SERVICE_NAME,
headingText: MOCK_HEADING,
headingTextFtlId: MOCK_DEFAULT_HEADING_FTL_ID,
- subheadingWithDefaultServiceFtlId: MOCK_DEFAULT_HEADING_FTL_ID,
subheadingWithCustomServiceFtlId: MOCK_CUSTOM_HEADING_FTL_ID,
+ subheadingWithLogoFtlId: MOCK_CUSTOM_HEADING_FTL_ID,
},
'Separate l10n for subheading, with custom service name'
);
+
+export const WithSeparateSubheadingLogo = storyWithProps(
+ {
+ serviceName: MozServices.Pocket,
+ headingText: MOCK_HEADING,
+ headingTextFtlId: MOCK_DEFAULT_HEADING_FTL_ID,
+ subheadingWithCustomServiceFtlId: MOCK_CUSTOM_HEADING_FTL_ID,
+ subheadingWithLogoFtlId: MOCK_DEFAULT_HEADING_FTL_ID,
+ },
+ 'Separate l10n for subheading, with logo'
+);
diff --git a/packages/fxa-settings/src/components/CardHeader/index.tsx b/packages/fxa-settings/src/components/CardHeader/index.tsx
index 493f5c4206c..a15e18da3ce 100644
--- a/packages/fxa-settings/src/components/CardHeader/index.tsx
+++ b/packages/fxa-settings/src/components/CardHeader/index.tsx
@@ -6,6 +6,7 @@ import React from 'react';
import { ReactElement } from 'react';
import { FtlMsg } from 'fxa-react/lib/utils';
import { MozServices } from '../../lib/types';
+import PocketTextLogo from '@fxa/shared/assets/images/pocket-text-logo.svg';
// NOTE: this component is heavily tested in components that use it and has complete line
// coverage. However, we may file an issue out of FXA-6589 to add more explicit coverage.
@@ -39,6 +40,7 @@ interface CardHeaderSeparateSubheadingProps extends CardHeaderRequiredProps {
headingTextFtlId: string;
subheadingWithDefaultServiceFtlId: string;
subheadingWithCustomServiceFtlId: string;
+ subheadingWithLogoFtlId?: string;
serviceName: MozServices;
}
@@ -107,7 +109,9 @@ function isDefaultService(
);
}
-function isCmsHeader(props: CardHeaderProps): props is CardHeaderCmsProps {
+function isCmsHeader(
+ props: CardHeaderProps
+): props is CardHeaderCmsProps {
return (
(props as CardHeaderCmsProps).cmsLogoUrl !== undefined ||
(props as CardHeaderCmsProps).cmsLogoAltText !== undefined ||
@@ -127,6 +131,33 @@ function isBasicWithCustomSubheading(
);
}
+const serviceLogos: {
+ [key in MozServices]?: ReactElement;
+} = {
+ // This is not inlined because text inside of an SVG can have rendering problems
+ [MozServices.Pocket]: (
+
+ ),
+};
+
+// TODO in FXA-8290: do we want to check against these unique client IDs instead
+// of serviceName? We have a service names enum, but in theory an RP could change their
+// service name and we'd have to update the enum, vs these that don't change.
+// export const POCKET_CLIENTIDS = [
+// '7377719276ad44ee', // pocket-mobile
+// '749818d3f2e7857f', // pocket-web
+// ];
+// This also applies to Monitor
+// export const MONITOR_CLIENTIDS = [
+// '802d56ef2a9af9fa', // Mozilla Monitor
+// '946bfd23df91404c', // Mozilla Monitor stage
+// 'edd29a80019d61a1', // Mozilla Monitor local dev
+// };
+
const CardHeader = (props: CardHeaderProps) => {
const { headingText } = props;
@@ -142,7 +173,9 @@ const CardHeader = (props: CardHeaderProps) => {
/>
)}
+ If you subscribe to Pocket Premium, please make sure that you{' '}
+ cancel your subscription {' '}
+ before deleting your account.
+
+
+
Please acknowledge that by deleting your account:
diff --git a/packages/fxa-settings/src/components/TermsPrivacyAgreement/en.ftl b/packages/fxa-settings/src/components/TermsPrivacyAgreement/en.ftl
index e803c45eac5..388a165b7d5 100644
--- a/packages/fxa-settings/src/components/TermsPrivacyAgreement/en.ftl
+++ b/packages/fxa-settings/src/components/TermsPrivacyAgreement/en.ftl
@@ -3,6 +3,8 @@
# This message is followed by a bulleted list
terms-privacy-agreement-intro-2 = By proceeding, you agree to the:
+# links to Pocket's Terms of Service and Privacy Notice, part of a bulleted list
+terms-privacy-agreement-pocket-2 = { -product-pocket } Terms of Service and Privacy Notice
# link to Monitor's Terms of Service and Privacy Notice, part of a bulleted list
terms-privacy-agreement-monitor-3 = { -brand-mozilla } Subscription Services Terms of Service and Privacy Notice
# links to Mozilla Accounts Terms of Service and Privacy Notice, part of a bulleted list
diff --git a/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.stories.tsx b/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.stories.tsx
index a713e16d41f..656df9c8ad3 100644
--- a/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.stories.tsx
+++ b/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.stories.tsx
@@ -20,6 +20,12 @@ export const FirefoxOnly = () => (
);
+export const PocketClient = () => (
+
+
+
+);
+
export const MonitorClient = () => (
diff --git a/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.test.tsx b/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.test.tsx
index 1f44be67c0f..3c5eecba7b1 100644
--- a/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.test.tsx
+++ b/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.test.tsx
@@ -45,6 +45,25 @@ describe('TermsPrivacyAgreement', () => {
expect(linkElements[0]).toHaveAttribute('href', '/legal/terms');
expect(linkElements[1]).toHaveAttribute('href', '/legal/privacy');
});
+
+ it('renders component as expected for Pocket clients', () => {
+ renderWithLocalizationProvider( );
+ // testAllL10n(screen, bundle);
+
+ const linkElements: HTMLElement[] = screen.getAllByRole('link');
+
+ expect(linkElements).toHaveLength(4);
+ expect(linkElements[0]).toHaveAttribute(
+ 'href',
+ 'https://getpocket.com/tos/'
+ );
+ expect(linkElements[1]).toHaveAttribute(
+ 'href',
+ 'https://getpocket.com/privacy/'
+ );
+ expect(linkElements[2]).toHaveAttribute('href', '/legal/terms');
+ expect(linkElements[3]).toHaveAttribute('href', '/legal/privacy');
+ });
});
it('renders component as expected for Monitor clients', () => {
diff --git a/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.tsx b/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.tsx
index dac4fbe35fd..5d277f623f0 100644
--- a/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.tsx
+++ b/packages/fxa-settings/src/components/TermsPrivacyAgreement/index.tsx
@@ -8,12 +8,14 @@ import { Link } from '@reach/router';
import LinkExternal from 'fxa-react/components/LinkExternal';
export type TermsPrivacyAgreementProps = {
+ isPocketClient?: boolean;
isMonitorClient?: boolean;
isRelayClient?: boolean; // Relay is oauth RP
isFirefoxClientServiceRelay?: boolean; // `service=relay` on Fx desktop or mobile client ID
};
const TermsPrivacyAgreement = ({
+ isPocketClient = false,
isMonitorClient = false,
isRelayClient = false,
isFirefoxClientServiceRelay = false,
@@ -22,12 +24,55 @@ const TermsPrivacyAgreement = ({
- {isMonitorClient || isFirefoxClientServiceRelay || isRelayClient ? (
+ {isPocketClient ||
+ isMonitorClient ||
+ isFirefoxClientServiceRelay ||
+ isRelayClient ? (
<>
By proceeding, you agree to the:
+ {isPocketClient && (
+
+ Terms of Service
+
+ ),
+ pocketPrivacy: (
+
+ Privacy Notice
+
+ ),
+ }}
+ >
+
+ Pocket{' '}
+
+ Terms of Service
+ {' '}
+ and{' '}
+
+ Privacy Notice
+
+
+
+ )}
{(isMonitorClient ||
isFirefoxClientServiceRelay ||
isRelayClient) && (
diff --git a/packages/fxa-settings/src/constants/index.tsx b/packages/fxa-settings/src/constants/index.tsx
index b89b88800ee..da733dde511 100644
--- a/packages/fxa-settings/src/constants/index.tsx
+++ b/packages/fxa-settings/src/constants/index.tsx
@@ -31,6 +31,9 @@ export const LINK = {
MDN: 'https://developer.mozilla.org/',
MONITOR: 'https://monitor.mozilla.org/',
MONITOR_STAGE: 'https://monitor-stage.allizom.org/',
+ MONITOR_PLUS: 'https://monitor.mozilla.org/subscription-plans',
+ MONITOR_PLUS_STAGE: 'https://monitor-stage.allizom.org/subscription-plans',
+ POCKET: 'https://getpocket.com/',
RELAY: 'https://relay.firefox.com/',
VPN: 'https://vpn.mozilla.org/',
};
diff --git a/packages/fxa-settings/src/lib/constants.ts b/packages/fxa-settings/src/lib/constants.ts
index 8e17a5d66eb..252893b97d3 100644
--- a/packages/fxa-settings/src/lib/constants.ts
+++ b/packages/fxa-settings/src/lib/constants.ts
@@ -134,6 +134,9 @@ export const Constants = {
MOZ_ORG_SYNC_GET_STARTED_LINK:
'https://www.mozilla.org/firefox/sync?utm_source=fx-website&utm_medium=fx-accounts&utm_campaign=fx-signup&utm_content=fx-sync-get-started', //eslint-disable-line max-len
+ POCKET_MORE_INFO_LINK:
+ 'https://support.mozilla.org/kb/pocket-firefox-account-migration',
+
// 20 most popular email domains, used for metrics. Matches the list
// we use in the auth server, converted to a map for faster lookup.
POPULAR_EMAIL_DOMAINS: popularDomains.reduce(
diff --git a/packages/fxa-settings/src/lib/glean/index.test.ts b/packages/fxa-settings/src/lib/glean/index.test.ts
index 87c8fea8a2c..7608a1af7a0 100644
--- a/packages/fxa-settings/src/lib/glean/index.test.ts
+++ b/packages/fxa-settings/src/lib/glean/index.test.ts
@@ -909,6 +909,15 @@ describe('lib/glean', () => {
sinon.assert.calledOnce(spy);
});
+ it('submits a ping with the account_pref_bento_pocket event name', async () => {
+ GleanMetrics.accountPref.bentoPocket();
+ const spy = sandbox.spy(accountPref.bentoPocket, 'record');
+ await GleanMetrics.isDone();
+ sinon.assert.calledOnce(setEventNameStub);
+ sinon.assert.calledWith(setEventNameStub, 'account_pref_bento_pocket');
+ sinon.assert.calledOnce(spy);
+ });
+
it('submits a ping with the account_pref_bento_relay event name', async () => {
GleanMetrics.accountPref.bentoRelay();
const spy = sandbox.spy(accountPref.bentoRelay, 'record');
diff --git a/packages/fxa-settings/src/lib/glean/index.ts b/packages/fxa-settings/src/lib/glean/index.ts
index f91b3f0f5b2..79d74c295f2 100644
--- a/packages/fxa-settings/src/lib/glean/index.ts
+++ b/packages/fxa-settings/src/lib/glean/index.ts
@@ -33,10 +33,7 @@ import * as deleteAccount from 'fxa-shared/metrics/glean/web/deleteAccount';
import * as thirdPartyAuth from 'fxa-shared/metrics/glean/web/thirdPartyAuth';
import * as thirdPartyAuthSetPassword from 'fxa-shared/metrics/glean/web/thirdPartyAuthSetPassword';
import { userIdSha256, userId } from 'fxa-shared/metrics/glean/web/account';
-import {
- appFramework,
- cmsCustomizationEnrollment,
-} from 'fxa-shared/metrics/glean/web/event';
+import { appFramework, cmsCustomizationEnrollment } from 'fxa-shared/metrics/glean/web/event';
import {
oauthClientId,
service,
@@ -566,6 +563,9 @@ const recordEventMetric = (
case 'account_pref_bento_monitor':
accountPref.bentoMonitor.record();
break;
+ case 'account_pref_bento_pocket':
+ accountPref.bentoPocket.record();
+ break;
case 'account_pref_bento_relay':
accountPref.bentoRelay.record();
break;
diff --git a/packages/fxa-settings/src/lib/types.ts b/packages/fxa-settings/src/lib/types.ts
index 73b8b7585a2..b9e015734c4 100644
--- a/packages/fxa-settings/src/lib/types.ts
+++ b/packages/fxa-settings/src/lib/types.ts
@@ -55,6 +55,7 @@ export enum MozServices {
Monitor = 'Mozilla Monitor',
FirefoxSync = 'Firefox Sync',
MozillaVPN = 'Mozilla VPN',
+ Pocket = 'Pocket',
Relay = 'Mozilla Relay',
TestService = '123Done',
MonitorStage = 'Mozilla Monitor Stage',
diff --git a/packages/fxa-settings/src/models/integrations/client-matching.ts b/packages/fxa-settings/src/models/integrations/client-matching.ts
index beab21428fe..ea40f1780b4 100644
--- a/packages/fxa-settings/src/models/integrations/client-matching.ts
+++ b/packages/fxa-settings/src/models/integrations/client-matching.ts
@@ -9,12 +9,21 @@ export const MONITOR_CLIENTIDS = [
'edd29a80019d61a1', // Mozilla Monitor local dev
];
+export const POCKET_CLIENTIDS = [
+ '7377719276ad44ee', // pocket-mobile
+ '749818d3f2e7857f', // pocket-web
+];
+
export const RELAY_CLIENTIDS = [
'41b4363ae36440a9', // Relay stage
'723aa3bce05884d8', // Relay dev
'9ebfe2c2f9ea3c58', // Relay prod
];
+export const isClientPocket = (clientId?: string) => {
+ return !!(clientId && POCKET_CLIENTIDS.includes(clientId));
+};
+
export const isClientMonitor = (clientId?: string) => {
return !!(clientId && MONITOR_CLIENTIDS.includes(clientId));
};
diff --git a/packages/fxa-settings/src/models/integrations/integration.ts b/packages/fxa-settings/src/models/integrations/integration.ts
index 90f16cf2693..28762f273c7 100644
--- a/packages/fxa-settings/src/models/integrations/integration.ts
+++ b/packages/fxa-settings/src/models/integrations/integration.ts
@@ -130,6 +130,8 @@ export class GenericIntegration<
case MozServices.MozillaVPN:
return MozServices.MozillaVPN;
+ case MozServices.Pocket:
+ return MozServices.Pocket;
case MozServices.TestService:
return MozServices.TestService;
diff --git a/packages/fxa-settings/src/pages/Index/en.ftl b/packages/fxa-settings/src/pages/Index/en.ftl
index 0c125c757a9..ee826325d5d 100644
--- a/packages/fxa-settings/src/pages/Index/en.ftl
+++ b/packages/fxa-settings/src/pages/Index/en.ftl
@@ -7,6 +7,7 @@ index-relay-header = Create an email mask
index-relay-subheader = Please provide the email address where you’d like to forward emails from your masked email.
# $serviceName - the service (e.g., Pontoon) that the user is signing into with a Mozilla account
index-subheader-with-servicename = Continue to { $serviceName }
+index-subheader-with-logo = Continue to { $serviceLogo }
index-subheader-default = Continue to account settings
index-cta = Sign up or sign in
index-account-info = A { -product-mozilla-account } also unlocks access to more privacy-protecting products from { -brand-mozilla }.
diff --git a/packages/fxa-settings/src/pages/Index/index.stories.tsx b/packages/fxa-settings/src/pages/Index/index.stories.tsx
index bf6da5b5d79..eb760a9be6e 100644
--- a/packages/fxa-settings/src/pages/Index/index.stories.tsx
+++ b/packages/fxa-settings/src/pages/Index/index.stories.tsx
@@ -14,6 +14,7 @@ import {
} from './mocks';
import {
MONITOR_CLIENTIDS,
+ POCKET_CLIENTIDS,
} from '../../models/integrations/client-matching';
import { MozServices } from '../../lib/types';
import { MOCK_EMAIL, MOCK_CMS_INFO } from '../mocks';
@@ -60,6 +61,13 @@ export const Monitor = storyWithProps({
serviceName: MozServices.Monitor,
});
+export const Pocket = storyWithProps({
+ integration: createMockIndexOAuthIntegration({
+ clientId: POCKET_CLIENTIDS[0],
+ }),
+ serviceName: MozServices.Pocket,
+});
+
export const WithCms = storyWithProps({
integration: createMockIndexOAuthNativeIntegration({
isFirefoxClientServiceRelay: true,
diff --git a/packages/fxa-settings/src/pages/Index/index.test.tsx b/packages/fxa-settings/src/pages/Index/index.test.tsx
index a622f98af78..7dfadd97148 100644
--- a/packages/fxa-settings/src/pages/Index/index.test.tsx
+++ b/packages/fxa-settings/src/pages/Index/index.test.tsx
@@ -11,8 +11,8 @@ import {
Subject,
} from './mocks';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
+import { POCKET_CLIENTIDS } from '../../models/integrations/client-matching';
import { MozServices } from '../../lib/types';
-import { MONITOR_CLIENTIDS } from '../../models/integrations/client-matching';
import GleanMetrics from '../../lib/glean';
import { MOCK_CMS_INFO } from '../mocks';
@@ -221,7 +221,7 @@ describe('Index page', () => {
renderWithLocalizationProvider(
@@ -231,6 +231,29 @@ describe('Index page', () => {
});
});
+ it('renders as expected when client is Pocket', () => {
+ renderWithLocalizationProvider(
+
+ );
+
+ screen.getByRole('heading', { name: 'Enter your email' });
+ screen.getByAltText('Pocket');
+
+ thirdPartyAuthWithSeparatorRendered();
+
+ const tosLinks = screen.getAllByRole('link', {
+ name: /Terms of Service/,
+ });
+
+ expect(tosLinks[0]).toHaveAttribute('href', 'https://getpocket.com/tos/');
+ expect(tosLinks[1]).toHaveAttribute('href', '/legal/terms');
+ });
+
describe('glean metrics', () => {
it('emits emailFirst.view on initial render', () => {
const viewSpy = jest.spyOn(GleanMetrics.emailFirst, 'view');
diff --git a/packages/fxa-settings/src/pages/Index/index.tsx b/packages/fxa-settings/src/pages/Index/index.tsx
index 70b435da2c2..fc9716340c3 100644
--- a/packages/fxa-settings/src/pages/Index/index.tsx
+++ b/packages/fxa-settings/src/pages/Index/index.tsx
@@ -13,6 +13,7 @@ import ThirdPartyAuth from '../../components/ThirdPartyAuth';
import TermsPrivacyAgreement from '../../components/TermsPrivacyAgreement';
import {
isClientMonitor,
+ isClientPocket,
isClientRelay,
} from '../../models/integrations/client-matching';
import { isOAuthIntegration } from '../../models';
@@ -40,6 +41,7 @@ export const Index = ({
const isSync = integration.isSync();
const isFirefoxClientServiceRelay = integration.isFirefoxClientServiceRelay();
const isOAuth = isOAuthIntegration(integration);
+ const isPocketClient = isOAuth && isClientPocket(clientId);
const isMonitorClient = isOAuth && isClientMonitor(clientId);
const isRelayClient = isOAuth && isClientRelay(clientId);
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -149,6 +151,7 @@ export const Index = ({
headingTextFtlId="index-header"
subheadingWithDefaultServiceFtlId="index-subheader-default"
subheadingWithCustomServiceFtlId="index-subheader-with-servicename"
+ subheadingWithLogoFtlId="index-subheader-with-logo"
{...{ clientId, serviceName }}
/>
)}
@@ -212,6 +215,7 @@ export const Index = ({
)}
elements appear as a subheading.
signin-password-needed-header-2 = Enter your password for your { -product-mozilla-account }
+# $serviceLogo - an image of the logo of the service which the user is authenticating for.
+# For languages structured like English, the phrase can read "to continue to"
+signin-subheader-with-logo = Continue to { $serviceLogo }
+
# $serviceName - the name of the service which the user authenticating for
# For languages structured like English, the phrase can read "to continue to { $serviceName }"
signin-subheader-without-logo-with-servicename = Continue to { $serviceName }
diff --git a/packages/fxa-settings/src/pages/Signin/index.stories.tsx b/packages/fxa-settings/src/pages/Signin/index.stories.tsx
index 1bc5241a464..d0f31013ece 100644
--- a/packages/fxa-settings/src/pages/Signin/index.stories.tsx
+++ b/packages/fxa-settings/src/pages/Signin/index.stories.tsx
@@ -4,6 +4,7 @@
import React from 'react';
import Signin from '.';
+import { MozServices } from '../../lib/types';
import { Meta } from '@storybook/react';
import {
Subject,
@@ -16,7 +17,6 @@ import { SigninProps } from './interfaces';
import { MOCK_SERVICE, MOCK_SESSION_TOKEN } from '../mocks';
import { AuthUiErrors } from '../../lib/auth-errors/auth-errors';
import { BeginSigninError } from '../../lib/error-utils';
-import { MozServices } from '../../lib/types';
export default {
title: 'Pages/Signin',
@@ -44,6 +44,11 @@ export const SignInToRelyingPartyWithPassword = storyWithProps({
serviceName: MOCK_SERVICE,
});
+export const SignInToPocketWithPassword = storyWithProps({
+ serviceName: MozServices.Pocket,
+ integration: createMockSigninOAuthIntegration(),
+});
+
export const SignInToSettingsWithCachedCredentials = storyWithProps({
sessionToken: MOCK_SESSION_TOKEN,
});
@@ -52,6 +57,12 @@ export const SignInToRelyingPartyWithCachedCredentials = storyWithProps({
serviceName: MOCK_SERVICE,
});
+export const SignInToPocketWithCachedCredentials = storyWithProps({
+ sessionToken: MOCK_SESSION_TOKEN,
+ serviceName: MozServices.Pocket,
+ integration: createMockSigninOAuthIntegration({ wantsKeys: false }),
+});
+
export const SignInToSyncWithCachedCredentials = storyWithProps({
sessionToken: MOCK_SESSION_TOKEN,
integration: createMockSigninOAuthIntegration({ wantsKeys: true }),
diff --git a/packages/fxa-settings/src/pages/Signin/index.test.tsx b/packages/fxa-settings/src/pages/Signin/index.test.tsx
index a16cbe1a469..26d64b25d84 100644
--- a/packages/fxa-settings/src/pages/Signin/index.test.tsx
+++ b/packages/fxa-settings/src/pages/Signin/index.test.tsx
@@ -40,7 +40,10 @@ import VerificationMethods from '../../constants/verification-methods';
import VerificationReasons from '../../constants/verification-reasons';
import { SigninProps } from './interfaces';
import { AuthUiErrors } from '../../lib/auth-errors/auth-errors';
-import { MONITOR_CLIENTIDS } from '../../models/integrations/client-matching';
+import {
+ MONITOR_CLIENTIDS,
+ POCKET_CLIENTIDS,
+} from '../../models/integrations/client-matching';
import firefox from '../../lib/channels/firefox';
import { navigate } from '@reach/router';
import {
@@ -1326,6 +1329,53 @@ describe('Signin component', () => {
});
});
+ describe('when client is Pocket', () => {
+ it('shows Pocket in header', () => {
+ renderWithLocalizationProvider(
+
+ );
+
+ const pocketLogo = screen.getByAltText('Pocket');
+ expect(pocketLogo).toBeInTheDocument();
+ });
+
+ it('shows Pocket-specific TOS', () => {
+ renderWithLocalizationProvider(
+
+ );
+
+ // Pocket links should always open in a new window (announced by screen readers)
+ const pocketTermsLink = screen.getByRole('link', {
+ name: 'Terms of Service Opens in new window',
+ });
+ const pocketPrivacyLink = screen.getByRole('link', {
+ name: 'Privacy Notice Opens in new window',
+ });
+
+ expect(pocketTermsLink).toHaveAttribute(
+ 'href',
+ 'https://getpocket.com/tos/'
+ );
+ expect(pocketPrivacyLink).toHaveAttribute(
+ 'href',
+ 'https://getpocket.com/privacy/'
+ );
+ });
+ });
+
describe('when client is Monitor', () => {
it('shows Monitor-specific TOS', async () => {
renderWithLocalizationProvider(
diff --git a/packages/fxa-settings/src/pages/Signin/index.tsx b/packages/fxa-settings/src/pages/Signin/index.tsx
index 07370111ac1..e07b2a86272 100644
--- a/packages/fxa-settings/src/pages/Signin/index.tsx
+++ b/packages/fxa-settings/src/pages/Signin/index.tsx
@@ -28,6 +28,7 @@ import {
} from '../../models';
import {
isClientMonitor,
+ isClientPocket,
isClientRelay,
} from '../../models/integrations/client-matching';
import { SigninFormData, SigninProps } from './interfaces';
@@ -87,6 +88,7 @@ const Signin = ({
const isOAuth = isOAuthIntegration(integration);
const isFirefoxClientServiceRelay = integration.isFirefoxClientServiceRelay();
const clientId = integration.getClientId();
+ const isPocketClient = isOAuth && isClientPocket(clientId);
const isMonitorClient = isOAuth && isClientMonitor(clientId);
const isRelayClient = isOAuth && isClientRelay(clientId);
const hasLinkedAccountAndNoPassword = hasLinkedAccount && !hasPassword;
@@ -402,6 +404,7 @@ const Signin = ({
headingTextFtlId="signin-header"
subheadingWithDefaultServiceFtlId="signin-subheader-without-logo-default"
subheadingWithCustomServiceFtlId="signin-subheader-without-logo-with-servicename"
+ subheadingWithLogoFtlId="signin-subheader-with-logo"
{...{
clientId,
serviceName,
@@ -519,6 +522,7 @@ const Signin = ({
;
export const CantChangeEmail = () => ;
+export const ClientIsPocket = () => (
+
+);
export const ClientIsMonitor = () => (
{
expect(firefoxPrivacyLink).toHaveAttribute('href', '/legal/privacy');
});
+ it('shows an info banner and Pocket-specific TOS when client is Pocket', async () => {
+ renderWithLocalizationProvider(
+
+ );
+
+ const infoBannerLink = screen.getByRole('link', {
+ name: /Find out here/,
+ });
+ await waitFor(() => {
+ expect(infoBannerLink).toBeInTheDocument();
+ });
+
+ // info banner is dismissible
+ const infoBannerDismissButton = screen.getByRole('button', {
+ name: 'Close banner',
+ });
+ fireEvent.click(infoBannerDismissButton);
+ await waitFor(() => {
+ expect(infoBannerLink).not.toBeInTheDocument();
+ });
+
+ // Pocket links should always open in a new window (announced by screen readers)
+ const pocketTermsLink = screen.getByRole('link', {
+ name: 'Terms of Service Opens in new window',
+ });
+ const pocketPrivacyLink = screen.getByRole('link', {
+ name: 'Privacy Notice Opens in new window',
+ });
+
+ expect(pocketTermsLink).toHaveAttribute(
+ 'href',
+ 'https://getpocket.com/tos/'
+ );
+ expect(pocketPrivacyLink).toHaveAttribute(
+ 'href',
+ 'https://getpocket.com/privacy/'
+ );
+ });
+
it('renders as expected when integration is sync', async () => {
await act(() => {
renderWithLocalizationProvider(
diff --git a/packages/fxa-settings/src/pages/Signup/index.tsx b/packages/fxa-settings/src/pages/Signup/index.tsx
index 78e4a8c4932..0cb375b0b0d 100644
--- a/packages/fxa-settings/src/pages/Signup/index.tsx
+++ b/packages/fxa-settings/src/pages/Signup/index.tsx
@@ -32,6 +32,7 @@ import {
} from '../../models';
import {
isClientMonitor,
+ isClientPocket,
isClientRelay,
} from '../../models/integrations/client-matching';
import { SignupFormData, SignupProps } from './interfaces';
@@ -80,6 +81,10 @@ export const Signup = ({
const [beginSignupLoading, setBeginSignupLoading] = useState(false);
const [bannerErrorText, setBannerErrorText] = useState('');
const [isFocused, setIsFocused] = useState(false);
+ const [
+ isAccountSuggestionBannerVisible,
+ setIsAccountSuggestionBannerVisible,
+ ] = useState(false);
const navigateWithQuery = useNavigateWithQuery();
// no newsletters are selected by default
@@ -91,6 +96,10 @@ export const Signup = ({
useEffect(() => {
if (isOAuth) {
const clientId = integration.getClientId();
+ if (isClientPocket(clientId)) {
+ setClient(MozServices.Pocket);
+ setIsAccountSuggestionBannerVisible(true);
+ }
if (isClientMonitor(clientId)) {
setClient(MozServices.Monitor);
}
@@ -332,6 +341,30 @@ export const Signup = ({
)}
+ {/* AccountSuggestion is only shown to Pocket clients */}
+ {isAccountSuggestionBannerVisible && (
+ setIsAccountSuggestionBannerVisible(false),
+ }}
+ />
+ )}
+
{email}
@@ -389,6 +422,7 @@ export const Signup = ({
)}