Skip to content

Commit

Permalink
fix(placement): hide ad-free link if plus isn't available (#12010)
Browse files Browse the repository at this point in the history
* fix(pong): hode no-pong if plus isn't available
  • Loading branch information
fiji-flo authored Oct 23, 2024
1 parent 0dea955 commit 55dcf6f
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 76 deletions.
8 changes: 6 additions & 2 deletions client/src/placement-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export enum Status {
type PlacementType = "side" | "top" | "hpMain" | "hpFooter" | "bottom";
export interface PlacementContextData
extends Partial<Record<PlacementType, PlacementData>> {
plusAvailable?: boolean;
status: Status;
}

Expand Down Expand Up @@ -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);
}
Expand Down
44 changes: 15 additions & 29 deletions client/src/plus/offer-overview/offer-overview-feature/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ function OfferOverviewFeature({ id, img, imgAlt, children }) {
export default function OfferOverviewFeatures() {
return (
<section id="features">
<OfferOverviewFeature id="afree" img="/assets/afree.png" imgAlt="">
<section aria-labelledby="afree-section-title">
<h2 id="afree-section-title">Go ads free</h2>
<h3>Enjoy MDN ads-free with an MDN Plus subscription.</h3>
<p>
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.
</p>
<Button href="/en-US/advertising" target="_self">
Learn more →
</Button>
</section>
</OfferOverviewFeature>
<OfferOverviewFeature id="ai-help" img={screenshotAiHelp} imgAlt="">
<section aria-labelledby="ai-help-section-title">
<h2 id="ai-help-section-title">AI Help</h2>
Expand Down Expand Up @@ -96,35 +111,6 @@ export default function OfferOverviewFeatures() {
</Button>
</section>
</OfferOverviewFeature>
<OfferOverviewFeature id="offline" img="/assets/offline.png" imgAlt="">
<section aria-labelledby="offline-section-title">
<h2 id="offline-section-title">MDN Offline</h2>
<h3>MDN's entire library at your fingertips: offline</h3>
<p>
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.
</p>
<Button href="/en-US/plus/docs/features/offline" target="_self">
Learn more →
</Button>
</section>
</OfferOverviewFeature>
<OfferOverviewFeature id="afree" img="/assets/afree.png" imgAlt="">
<section aria-labelledby="afree-section-title">
<h2 id="afree-section-title">Go ads free</h2>
<h3>Enjoy MDN ads-free with an MDN Plus subscription.</h3>
<p>
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.
</p>
<Button href="/en-US/advertising" target="_self">
Learn more →
</Button>
</section>
</OfferOverviewFeature>
</section>
);
}
91 changes: 52 additions & 39 deletions client/src/ui/organisms/placement/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface PlacementRenderArgs {
version?: number;
typ: string;
heading?: string;
showNoAds: boolean;
}

function viewed(pong?: PlacementData) {
Expand Down Expand Up @@ -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);
Expand All @@ -281,6 +284,7 @@ export function PlacementInner({
version,
typ,
heading,
showNoAds,
})}
</>
);
Expand All @@ -300,6 +304,7 @@ function RenderSideOrTopBanner({
style,
version = 1,
typ,
showNoAds,
}: PlacementRenderArgs) {
return (
<section
Expand Down Expand Up @@ -350,19 +355,21 @@ function RenderSideOrTopBanner({
</a>
</p>

<a
className="no-pong"
data-glean={
"pong: " + (user?.isSubscriber ? "pong->settings" : "pong->plus")
}
href={
user?.isSubscriber
? "/en-US/plus/settings?ref=nope"
: "/en-US/plus?ref=nope#subscribe"
}
>
Don't want to see ads?
</a>
{showNoAds && (
<a
className="no-pong"
data-glean={
"pong: " + (user?.isSubscriber ? "pong->settings" : "pong->plus")
}
href={
user?.isSubscriber
? "/en-US/plus/settings?ref=nope"
: "/en-US/plus?ref=nope"
}
>
Don't want to see ads?
</a>
)}
</section>
);
}
Expand Down Expand Up @@ -419,6 +426,7 @@ function RenderBottomBanner({
style,
version = 1,
typ,
showNoAds,
}: PlacementRenderArgs) {
return (
<div className="bottom-banner-container" style={style}>
Expand Down Expand Up @@ -451,19 +459,21 @@ function RenderBottomBanner({
>
Mozilla ads
</a>
<a
className="no-pong"
data-glean={
"pong: " + (user?.isSubscriber ? "pong->settings" : "pong->plus")
}
href={
user?.isSubscriber
? "/en-US/plus/settings?ref=nope"
: "/en-US/plus?ref=nope#subscribe"
}
>
Don't want to see ads?
</a>
{showNoAds && (
<a
className="no-pong"
data-glean={
"pong: " + (user?.isSubscriber ? "pong->settings" : "pong->plus")
}
href={
user?.isSubscriber
? "/en-US/plus/settings?ref=nope"
: "/en-US/plus?ref=nope"
}
>
Don't want to see ads?
</a>
)}
</section>
</div>
);
Expand All @@ -484,6 +494,7 @@ function RenderNewSideBanner({
version = 1,
typ,
heading,
showNoAds,
}: PlacementRenderArgs) {
return (
<section ref={place} className={["place", ...extraClassNames].join(" ")}>
Expand Down Expand Up @@ -521,19 +532,21 @@ function RenderNewSideBanner({
</a>
</div>

<a
className="no-pong"
data-glean={
"pong: " + (user?.isSubscriber ? "pong->settings" : "pong->plus")
}
href={
user?.isSubscriber
? "/en-US/plus/settings?ref=nope"
: "/en-US/plus?ref=nope#subscribe"
}
>
Don't want to see ads?
</a>
{showNoAds && (
<a
className="no-pong"
data-glean={
"pong: " + (user?.isSubscriber ? "pong->settings" : "pong->plus")
}
href={
user?.isSubscriber
? "/en-US/plus/settings?ref=nope"
: "/en-US/plus?ref=nope"
}
>
Don't want to see ads?
</a>
)}
</section>
);
}
3 changes: 1 addition & 2 deletions cloud-function/src/handlers/handle-stripe-plans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion cloud-function/src/handlers/proxy-bsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}/`);
Expand All @@ -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")
Expand Down
10 changes: 9 additions & 1 deletion cloud-function/src/handlers/proxy-kevel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}`);
Expand All @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion libs/pong/pong.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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): (
Expand Down
2 changes: 1 addition & 1 deletion libs/pong/pong2.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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): (
Expand Down

0 comments on commit 55dcf6f

Please sign in to comment.