From 2623fbe4acfefb8307e0a6dd389c63919e53590b Mon Sep 17 00:00:00 2001 From: Ryan Salazar Date: Tue, 16 Jun 2026 18:11:13 +0000 Subject: [PATCH] ads: pin aja-colorbox-300x250 as the first banner in every 300x250 rotation --- src/lib/ads.ts | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/lib/ads.ts b/src/lib/ads.ts index 1e10c0b..af6cc75 100644 --- a/src/lib/ads.ts +++ b/src/lib/ads.ts @@ -196,10 +196,38 @@ export function shuffle(arr: T[]): T[] { return out; } +// Slugs (or campaign_ids) that must be the FIRST banner of their size in +// every rotation, ahead of the shuffled pool. AJA 2026 sits at the top of +// every 300x250 stack per Ryan 2026-06-16. +const PINNED_SLUGS_BY_SIZE: Partial> = { + "300x250": ["aja-colorbox-300x250"], +}; + export function rotateAll(size: Ad["size"], exclude: Set = new Set()): Ad[] { const pool = readCachedAds().filter((a) => a.size === size); const key = (a: Ad) => a.src ?? a.label; - return shuffle(pool.filter((a) => !exclude.has(key(a)))); + const remaining = pool.filter((a) => !exclude.has(key(a))); + + const pinnedSlugs = PINNED_SLUGS_BY_SIZE[size] || []; + if (pinnedSlugs.length === 0) return shuffle(remaining); + + const pinned: Ad[] = []; + const rest: Ad[] = []; + for (const ad of remaining) { + const slugMatch = ad.slug && pinnedSlugs.includes(ad.slug); + const idMatch = ad.campaign_id && pinnedSlugs.includes(ad.campaign_id); + if (slugMatch || idMatch) { + pinned.push(ad); + } else { + rest.push(ad); + } + } + pinned.sort((a, b) => { + const ai = pinnedSlugs.indexOf((a.slug || a.campaign_id) ?? ""); + const bi = pinnedSlugs.indexOf((b.slug || b.campaign_id) ?? ""); + return ai - bi; + }); + return [...pinned, ...shuffle(rest)]; } export function pickAds(