wire real ad rotation: Blackmagic 300x600 fixed + rotating 300x250s

Replace the placeholder Unsplash banner array with the live AdSanity
inventory served from /legacy/ads/ (same 27 active creatives currently
on broadcastbeat.com).

New:
- src/lib/ads.ts: ad inventory (1 fixed Blackmagic 300x600, 22 rotating
  300x250s, 4 leaderboard 728x90s) + Fisher-Yates shuffle + pickAds()
  helper that draws N unique ads excluding any caller-supplied src set.
- src/components/AdImage.tsx: renders one ad as <a target=_blank rel=
  sponsored noopener noreferrer><AppImage>.
- src/components/SidebarAdStack.tsx: Blackmagic 300x600 fixed on top,
  then count rotating 300x250s. Memoizes the rotation per render so a
  refresh re-shuffles. Accepts excludeSrcs to coordinate with other
  slots on the same page.
- src/components/ArticleBody.tsx: splits article HTML at </p>
  boundaries, splices a 300x250 ad after every Nth paragraph (default
  4). Same exclude-set pattern.

Wiring:
- ArticleFeed.tsx: remove the inline sidebarAds array (was 5 Unsplash
  placeholders + 1 LiveU 300x250). Sidebar now <SidebarAdStack count=5/>.
- NewsArticleDetailClient.tsx: sidebar 300x600 + 300x250 AdSlot
  placeholders replaced with <SidebarAdStack count=4/>. Article body
  innerHTML replaced with <ArticleBody html=... everyN=4/> so
  300x250 ads slot in between paragraphs.

Same banners as the live broadcastbeat.com (sourced from the existing
WP AdSanity install we cached locally during Phase 2). Each <a> opens
in a new tab and uses rel='sponsored noopener noreferrer'.
This commit is contained in:
Ryan Salazar
2026-05-08 18:16:07 +00:00
parent 7c9d2c8c79
commit 9c5b0e4959
6 changed files with 236 additions and 55 deletions

View File

@@ -5,6 +5,8 @@ import AppImage from "@/components/ui/AppImage";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import AdSlot from "@/components/AdSlot";
import SidebarAdStack from "@/components/SidebarAdStack";
import ArticleBody from "@/components/ArticleBody";
import { createClient } from "@/lib/supabase/client";
import type { Article } from "@/lib/articles/types";
@@ -377,8 +379,10 @@ export default function NewsArticleDetailClient({
/>
</div>
{/* Article Body */}
<div
{/* Article Body — paragraphs interleaved with rotating 300×250 ads every 4 paragraphs */}
<ArticleBody
html={article.content}
everyN={4}
className="prose prose-invert max-w-none mb-8 font-body text-[#aaa] leading-relaxed
[&_h2]:font-heading [&_h2]:text-[#e0e0e0] [&_h2]:text-xl [&_h2]:font-bold [&_h2]:mt-8 [&_h2]:mb-4 [&_h2]:border-b [&_h2]:border-[#222] [&_h2]:pb-2
[&_p]:mb-4 [&_p]:leading-relaxed
@@ -386,7 +390,6 @@ export default function NewsArticleDetailClient({
[&_li]:text-[#aaa]
[&_strong]:text-[#e0e0e0] [&_strong]:font-bold
[&_a]:text-[#3b82f6] [&_a]:hover:underline"
dangerouslySetInnerHTML={{ __html: article.content }}
/>
{/* Tags */}
@@ -450,8 +453,8 @@ export default function NewsArticleDetailClient({
{/* Sidebar */}
<aside className="lg:col-span-4 space-y-6">
{/* Sidebar fixed half-page */}
<AdSlot zone="article-sidebar-top" size="300x600" />
{/* Blackmagic 300x600 fixed at top + rotating 300x250 banners */}
<SidebarAdStack count={4} />
{/* Related Articles Sidebar */}
<div className="bg-[#111] border border-[#222] p-5">
@@ -500,7 +503,6 @@ export default function NewsArticleDetailClient({
</div>
{/* Ad placeholder */}
<AdSlot zone="article-sidebar-mid" size="300x250" />
</aside>
</div>
</article>