diff --git a/client/src/placement-context.tsx b/client/src/placement-context.tsx index 847b86e6e330..ed5eeb288bbb 100644 --- a/client/src/placement-context.tsx +++ b/client/src/placement-context.tsx @@ -17,6 +17,7 @@ export enum Status { type PlacementType = "side" | "top" | "hpMain" | "hpFooter" | "bottom"; export interface PlacementContextData extends Partial> { + plusAvailable?: boolean; status: Status; } @@ -89,14 +90,17 @@ export function PlacementProvider(props: { children: React.ReactNode }) { } try { - const placementResponse: PlacementContextData = await response.json(); + const { + plusAvailable = true, // Fall back to true for seamless migration. + ...placementResponse + }: PlacementContextData = await response.json(); const typs = Object.entries(PLACEMENT_MAP) .filter(([key]) => key in placementResponse) .map(([, { typ }]) => typ); if (typs.length) { gleanClick(`pong: pong->served ${typs.join()}`); } - return placementResponse; + return { plusAvailable, ...placementResponse }; } catch (e) { throw Error(response.statusText); } diff --git a/client/src/plus/offer-overview/offer-overview-feature/index.tsx b/client/src/plus/offer-overview/offer-overview-feature/index.tsx index e6294a270fd0..a646964a2e9f 100644 --- a/client/src/plus/offer-overview/offer-overview-feature/index.tsx +++ b/client/src/plus/offer-overview/offer-overview-feature/index.tsx @@ -24,6 +24,21 @@ function OfferOverviewFeature({ id, img, imgAlt, children }) { export default function OfferOverviewFeatures() { return (
+ +
+

Go ads free

+

Enjoy MDN ads-free with an MDN Plus subscription.

+

+ Support MDN and enjoy a focused, ad-free experience alongside other + features such as curated collections, custom web platform updates, + offline access, and more. Subscribers to paid tiers of MDN Plus have + the option to browse MDN without ads. +

+ +
+

AI Help

@@ -96,35 +111,6 @@ export default function OfferOverviewFeatures() {
- -
-

MDN Offline

-

MDN's entire library at your fingertips: offline

-

- Taking your projects beyond the nearest wifi signal? Say goodbye to - inaccessible pages or cluttered tabs. With MDN Plus, have the fully - navigable resources of MDN at your disposal even when offline. -

- -
-
- -
-

Go ads free

-

Enjoy MDN ads-free with an MDN Plus subscription.

-

- Support MDN and enjoy a focused, ad-free experience alongside other - features such as curated collections, custom web platform updates, - offline access, and more. Subscribers to paid tiers of MDN Plus have - the option to browse MDN without ads. -

- -
-
); } diff --git a/client/src/ui/organisms/placement/index.tsx b/client/src/ui/organisms/placement/index.tsx index a02859efbb52..dd35633fec44 100644 --- a/client/src/ui/organisms/placement/index.tsx +++ b/client/src/ui/organisms/placement/index.tsx @@ -25,6 +25,7 @@ interface PlacementRenderArgs { version?: number; typ: string; heading?: string; + showNoAds: boolean; } function viewed(pong?: PlacementData) { @@ -255,6 +256,8 @@ export function PlacementInner({ const isServer = useIsServer(); const user = useUserData(); const gleanClick = useGleanClick(); + const { plusAvailable } = usePlacement() || {}; + const showNoAds = Boolean(user?.isSubscriber || plusAvailable); const place = useViewed(() => { viewed(pong); @@ -281,6 +284,7 @@ export function PlacementInner({ version, typ, heading, + showNoAds, })} ); @@ -300,6 +304,7 @@ function RenderSideOrTopBanner({ style, version = 1, typ, + showNoAds, }: PlacementRenderArgs) { return (

- settings" : "pong->plus") - } - href={ - user?.isSubscriber - ? "/en-US/plus/settings?ref=nope" - : "/en-US/plus?ref=nope#subscribe" - } - > - Don't want to see ads? - + {showNoAds && ( + settings" : "pong->plus") + } + href={ + user?.isSubscriber + ? "/en-US/plus/settings?ref=nope" + : "/en-US/plus?ref=nope" + } + > + Don't want to see ads? + + )}
); } @@ -419,6 +426,7 @@ function RenderBottomBanner({ style, version = 1, typ, + showNoAds, }: PlacementRenderArgs) { return (
@@ -451,19 +459,21 @@ function RenderBottomBanner({ > Mozilla ads - settings" : "pong->plus") - } - href={ - user?.isSubscriber - ? "/en-US/plus/settings?ref=nope" - : "/en-US/plus?ref=nope#subscribe" - } - > - Don't want to see ads? - + {showNoAds && ( + settings" : "pong->plus") + } + href={ + user?.isSubscriber + ? "/en-US/plus/settings?ref=nope" + : "/en-US/plus?ref=nope" + } + > + Don't want to see ads? + + )}
); @@ -484,6 +494,7 @@ function RenderNewSideBanner({ version = 1, typ, heading, + showNoAds, }: PlacementRenderArgs) { return (
@@ -521,19 +532,21 @@ function RenderNewSideBanner({ - settings" : "pong->plus") - } - href={ - user?.isSubscriber - ? "/en-US/plus/settings?ref=nope" - : "/en-US/plus?ref=nope#subscribe" - } - > - Don't want to see ads? - + {showNoAds && ( + settings" : "pong->plus") + } + href={ + user?.isSubscriber + ? "/en-US/plus/settings?ref=nope" + : "/en-US/plus?ref=nope" + } + > + Don't want to see ads? + + )}
); } diff --git a/cloud-function/src/handlers/handle-stripe-plans.ts b/cloud-function/src/handlers/handle-stripe-plans.ts index 43e4281be772..aa9b9120f761 100644 --- a/cloud-function/src/handlers/handle-stripe-plans.ts +++ b/cloud-function/src/handlers/handle-stripe-plans.ts @@ -19,8 +19,6 @@ export async function handleStripePlans(req: Request, res: Response) { const lookupData = ORIGIN_MAIN === "developer.mozilla.org" ? prodLookup : stageLookup; - const localeHeader = req.headers["accept-language"]; - const countryCode = getRequestCountry(req); const supportedCurrency = lookupData.countryToCurrency[countryCode]; @@ -29,6 +27,7 @@ export async function handleStripePlans(req: Request, res: Response) { return res.sendStatus(404).end(); } + const localeHeader = req.headers["accept-language"]; const acceptLanguage = typeof localeHeader === "string" ? localeHeader : null; let supportedLanguageOrDefault; diff --git a/cloud-function/src/handlers/proxy-bsa.ts b/cloud-function/src/handlers/proxy-bsa.ts index 38be31ca2341..06aba9e116b8 100644 --- a/cloud-function/src/handlers/proxy-bsa.ts +++ b/cloud-function/src/handlers/proxy-bsa.ts @@ -8,20 +8,26 @@ import { fetchImage, } from "../internal/pong/index.js"; +import stagePlusLookup from "../stripe-plans/stage.js"; +import prodPlusLookup from "../stripe-plans/prod.js"; import * as env from "../env.js"; import { getRequestCountry } from "../utils.js"; -const { SIGN_SECRET, BSA_ZONE_KEYS } = env; +const { SIGN_SECRET, BSA_ZONE_KEYS, ORIGIN_MAIN } = env; const coder = new Coder(SIGN_SECRET); const handleGet = createPong2GetHandler(BSA_ZONE_KEYS, coder, env); const handleClick = createPong2ClickHandler(coder); const handleViewed = createPong2ViewedHandler(coder); +const plusLookup = + ORIGIN_MAIN === "developer.mozilla.org" ? prodPlusLookup : stagePlusLookup; export async function proxyBSA(req: Request, res: Response) { const countryCode = getRequestCountry(req); + const plusAvailable = countryCode in plusLookup.countryToCurrency; + const userAgent = req.headers["user-agent"] ?? ""; const parsedUrl = new URL(req.url, `${req.protocol}://${req.headers.host}/`); @@ -40,6 +46,8 @@ export async function proxyBSA(req: Request, res: Response) { userAgent ); + payload.plusAvailable = plusAvailable; + return res .status(status) .setHeader("cache-control", "no-store") diff --git a/cloud-function/src/handlers/proxy-kevel.ts b/cloud-function/src/handlers/proxy-kevel.ts index a176b15e4628..3bb88b8337c6 100644 --- a/cloud-function/src/handlers/proxy-kevel.ts +++ b/cloud-function/src/handlers/proxy-kevel.ts @@ -9,11 +9,13 @@ import { fetchImage, } from "../internal/pong/index.js"; +import stagePlusLookup from "../stripe-plans/stage.js"; +import prodPlusLookup from "../stripe-plans/prod.js"; import * as env from "../env.js"; import { getRequestCountry } from "../utils.js"; -const { KEVEL_SITE_ID, KEVEL_NETWORK_ID, SIGN_SECRET } = env; +const { KEVEL_SITE_ID, KEVEL_NETWORK_ID, ORIGIN_MAIN, SIGN_SECRET } = env; const siteId = KEVEL_SITE_ID; const networkId = KEVEL_NETWORK_ID; @@ -23,10 +25,14 @@ const coder = new Coder(SIGN_SECRET); const handleGet = createPongGetHandler(client, coder, env); const handleClick = createPongClickHandler(coder); const handleViewed = createPongViewedHandler(coder); +const plusLookup = + ORIGIN_MAIN === "developer.mozilla.org" ? prodPlusLookup : stagePlusLookup; export async function proxyKevel(req: Request, res: Response) { const countryCode = getRequestCountry(req); + const plusAvailable = countryCode in plusLookup.countryToCurrency; + const userAgent = req.headers["user-agent"] ?? ""; const parsedUrl = new URL(req.url, `${req.protocol}://${req.headers.host}`); @@ -45,6 +51,8 @@ export async function proxyKevel(req: Request, res: Response) { userAgent ); + payload.plusAvailable = plusAvailable; + return res .status(status) .setHeader("cache-control", "no-store") diff --git a/libs/pong/pong.d.ts b/libs/pong/pong.d.ts index cf6f471374b7..483bf5f9f04f 100644 --- a/libs/pong/pong.d.ts +++ b/libs/pong/pong.d.ts @@ -11,7 +11,7 @@ export function createPongGetHandler( userAgent: string ) => Promise<{ statusCode: number; - payload: Payload; + payload: { plusAvailable?: bool; [index: string]: Payload }; }>; export function createPongClickHandler(coder: Coder): ( diff --git a/libs/pong/pong2.d.ts b/libs/pong/pong2.d.ts index 8de5c1b4700a..a8253557610d 100644 --- a/libs/pong/pong2.d.ts +++ b/libs/pong/pong2.d.ts @@ -11,7 +11,7 @@ export function createPong2GetHandler( userAgent: string ) => Promise<{ statusCode: number; - payload: Payload; + payload: { plusAvailable?: bool; [index: string]: Payload }; }>; export function createPong2ClickHandler(coder: Coder): (