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:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
import React, { useState, useRef, useId, useEffect, useCallback } from "react";
|
||||
import AppImage from "@/components/ui/AppImage";
|
||||
import SidebarAdStack from "@/components/SidebarAdStack";
|
||||
import { PersonIcon, ChevronLeftIcon, ChevronRightIcon, SearchIcon, CloseIcon } from "@/components/ui/Icons";
|
||||
import Link from "next/link";
|
||||
|
||||
@@ -202,14 +203,6 @@ const staticArticles: ArticleItem[] = [
|
||||
}
|
||||
];
|
||||
|
||||
const sidebarAds = [
|
||||
{ src: "https://images.unsplash.com/photo-1611532736597-de2d4265fba3?w=300&h=600&fit=crop", alt: "Blackmagic Design advertisement 300x600", label: "Blackmagic", size: "300x600" },
|
||||
{ src: "https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=300&h=250&fit=crop", alt: "Zixi broadcast technology advertisement 300x250", label: "Zixi", size: "300x250" },
|
||||
{ src: "https://www.broadcastbeat.com/wp-content/uploads/728-x-90-pxEN.gif", alt: "LiveU live production solutions advertisement 300x250", label: "LiveU", size: "300x250" },
|
||||
{ src: "https://images.unsplash.com/photo-1563986768609-322da13575f3?w=300&h=250&fit=crop", alt: "TelyCam broadcast camera advertisement 300x250", label: "TelyCam", size: "300x250" },
|
||||
{ src: "https://images.unsplash.com/photo-1574717024653-61fd2cf4d44d?w=300&h=250&fit=crop", alt: "AJA Video Systems advertisement 300x250", label: "AJA", size: "300x250" },
|
||||
{ src: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=300&h=250&fit=crop", alt: "Studio Suite production management advertisement 300x250", label: "Studio Suite", size: "300x250" }
|
||||
];
|
||||
|
||||
const ALL_CATEGORIES = ["All", "BROADCAST", "FEATURED", "POST PRODUCTION", "ANIMATION"];
|
||||
const SOURCE_FILTERS = ["All", "Live", "Imported"] as const;
|
||||
@@ -907,49 +900,10 @@ export default function ArticleFeed() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Sidebar */}
|
||||
{/* Sidebar — Blackmagic 300x600 fixed at top + 5 rotating 300x250s */}
|
||||
<aside className="lg:col-span-4" aria-label="Advertisements">
|
||||
<div className="lg:sticky lg:top-24">
|
||||
<div className="grid grid-cols-2 gap-3 lg:hidden">
|
||||
{sidebarAds?.filter(ad => ad?.size !== "300x600")?.map((ad, i) => (
|
||||
<a key={i} href="#" aria-label={`${ad?.label} advertisement`} className="block focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]">
|
||||
<div className="ad-placeholder overflow-hidden h-[120px] w-full relative">
|
||||
<AppImage
|
||||
src={ad?.src}
|
||||
alt={ad?.alt}
|
||||
fill
|
||||
className="w-full h-full object-cover"
|
||||
sizes="(max-width: 1024px) 50vw"
|
||||
/>
|
||||
<div className="absolute bottom-1 right-1 bg-black/40 text-white/60 font-body text-[9px] px-1 py-0.5">
|
||||
AD · {ad?.label}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="hidden lg:flex lg:flex-col lg:space-y-4">
|
||||
{sidebarAds?.map((ad, i) => (
|
||||
<a key={i} href="#" aria-label={`${ad?.label} advertisement`} className="block focus:outline-none focus-visible:ring-2 focus-visible:ring-[#3b82f6]">
|
||||
<div
|
||||
className={`ad-placeholder overflow-hidden ${ad?.size === "300x600" ? "h-[600px]" : "h-[250px]"}`}
|
||||
style={{ width: "100%", maxWidth: 300 }}>
|
||||
<AppImage
|
||||
src={ad?.src}
|
||||
alt={ad?.alt}
|
||||
width={300}
|
||||
height={ad?.size === "300x600" ? 600 : 250}
|
||||
className="w-full h-full object-cover"
|
||||
sizes="300px"
|
||||
/>
|
||||
<div className="absolute bottom-1 right-1 bg-black/40 text-white/60 font-body text-[9px] px-1 py-0.5">
|
||||
AD · {ad?.label}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
<SidebarAdStack count={5} />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user