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.
+
+
+ Learn more →
+
+
+
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.
-
-
- Learn more →
-
-
-
-
-
- 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.
-
-
- Learn more →
-
-
-
);
}
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 (
);
@@ -484,6 +494,7 @@ function RenderNewSideBanner({
version = 1,
typ,
heading,
+ showNoAds,
}: PlacementRenderArgs) {
return (
);
}
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): (