Revert "home: replace single LiveWire ticker with stacked dual ticker"
This reverts commit a52f8adb21.
This commit is contained in:
@@ -4,7 +4,7 @@ import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import FeaturedBentoFromDb from '@/components/FeaturedBentoFromDb';
|
||||
import FeaturedCarouselServer from '@/components/FeaturedCarouselServer';
|
||||
import DoubleTicker from '@/components/DoubleTicker';
|
||||
import LiveWireTicker from '@/components/LiveWireTicker';
|
||||
import NewsTicker from "./components/NewsTicker";
|
||||
import FeaturedBento from "./components/FeaturedBento";
|
||||
import SpotlightCarousel from "./components/SpotlightCarousel";
|
||||
@@ -147,10 +147,10 @@ export default function HomePage() {
|
||||
<h1>Broadcast Beat — Broadcast Engineering News & Insights</h1>
|
||||
</div>
|
||||
|
||||
{/* Scrolling news + forum tickers */}
|
||||
{/* Scrolling news ticker */}
|
||||
<div className="section-enter section-enter-1">
|
||||
<Suspense fallback={<TickerSkeleton />}>
|
||||
<DoubleTicker />
|
||||
<LiveWireTicker />
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import Link from "next/link";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
interface TickerItem {
|
||||
href: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default async function DoubleTicker() {
|
||||
const sb = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
db: { schema: (process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb") as "public" },
|
||||
auth: { persistSession: false },
|
||||
}
|
||||
);
|
||||
|
||||
const [wireRes, forumRes] = await Promise.all([
|
||||
sb
|
||||
.from("wp_imported_posts")
|
||||
.select("wp_slug, title")
|
||||
.eq("status", "published")
|
||||
.order("wp_published_at", { ascending: false, nullsFirst: false })
|
||||
.limit(14),
|
||||
sb
|
||||
.from("forum_threads")
|
||||
.select("id, title")
|
||||
.order("last_reply_at", { ascending: false, nullsFirst: false })
|
||||
.limit(14),
|
||||
]);
|
||||
|
||||
const wire: TickerItem[] = (wireRes.data || []).map((r: any) => ({
|
||||
href: `/news/${r.wp_slug}`,
|
||||
title: r.title,
|
||||
}));
|
||||
const forum: TickerItem[] = (forumRes.data || []).map((r: any) => ({
|
||||
href: `/forum/thread/${r.id}`,
|
||||
title: r.title,
|
||||
}));
|
||||
|
||||
// Fallback to keep layout sensible if either feed is empty
|
||||
const wireItems = wire.length > 0 ? wire : [{ href: "/news", title: "Latest broadcast industry news loading…" }];
|
||||
const forumItems = forum.length > 0 ? forum : [{ href: "/forum", title: "Join the conversation in the forum" }];
|
||||
|
||||
return (
|
||||
<div className="bb-ticker-stack" aria-label="Site activity tickers">
|
||||
{/* LANE 1 — LIVE WIRE (red) */}
|
||||
<div className="bb-ticker bb-ticker--wire" role="region" aria-label="Live wire — latest articles">
|
||||
<span className="bb-ticker-label bb-ticker-label--red">
|
||||
<span className="bb-ticker-pulse" aria-hidden="true" />
|
||||
LIVE WIRE
|
||||
</span>
|
||||
<div className="bb-ticker-mask">
|
||||
<div className="bb-ticker-track bb-ticker-track--wire">
|
||||
{[...wireItems, ...wireItems].map((item, i) => (
|
||||
<Link
|
||||
key={`wire-${i}`}
|
||||
href={item.href}
|
||||
className="bb-ticker-item bb-ticker-item--wire">
|
||||
<span className="bb-ticker-bullet" aria-hidden="true">›</span>
|
||||
<span>{item.title}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* LANE 2 — RECENT FORUM POSTS (teal-green) */}
|
||||
<div className="bb-ticker bb-ticker--forum" role="region" aria-label="Recent forum posts">
|
||||
<span className="bb-ticker-label bb-ticker-label--green">
|
||||
<span className="bb-ticker-pulse bb-ticker-pulse--green" aria-hidden="true" />
|
||||
RECENT FORUM POSTS
|
||||
</span>
|
||||
<div className="bb-ticker-mask">
|
||||
<div className="bb-ticker-track bb-ticker-track--forum">
|
||||
{[...forumItems, ...forumItems].map((item, i) => (
|
||||
<Link
|
||||
key={`forum-${i}`}
|
||||
href={item.href}
|
||||
className="bb-ticker-item bb-ticker-item--forum">
|
||||
<span className="bb-ticker-bullet" aria-hidden="true">›</span>
|
||||
<span>{item.title}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
118
src/components/LiveWireTicker.tsx
Normal file
118
src/components/LiveWireTicker.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import Link from "next/link";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
interface Item {
|
||||
slug: string;
|
||||
title: string;
|
||||
published_at: string;
|
||||
source_code: string;
|
||||
}
|
||||
|
||||
const SOURCE_CODES: { match: RegExp; code: string }[] = [
|
||||
{ match: /blackmagic|davinci/i, code: "BMD" },
|
||||
{ match: /magewell/i, code: "MGW" },
|
||||
{ match: /aja/i, code: "AJA" },
|
||||
{ match: /sony/i, code: "SONY" },
|
||||
{ match: /matrox/i, code: "MTRX" },
|
||||
{ match: /telestream/i, code: "TLST" },
|
||||
{ match: /grass valley/i, code: "GV" },
|
||||
{ match: /lawo/i, code: "LAWO" },
|
||||
{ match: /calrec/i, code: "CLRC" },
|
||||
{ match: /zixi/i, code: "ZIXI" },
|
||||
{ match: /liveu/i, code: "LVU" },
|
||||
{ match: /netflix/i, code: "NFLX" },
|
||||
{ match: /youtube/i, code: "YT" },
|
||||
{ match: /amazon|aws/i, code: "AWS" },
|
||||
];
|
||||
|
||||
function codeFor(title: string): string {
|
||||
for (const { match, code } of SOURCE_CODES) if (match.test(title)) return code;
|
||||
return "WIRE";
|
||||
}
|
||||
|
||||
function hhmm(iso: string): string {
|
||||
const d = new Date(iso);
|
||||
const h = String(d.getUTCHours()).padStart(2, "0");
|
||||
const m = String(d.getUTCMinutes()).padStart(2, "0");
|
||||
return `${h}:${m}`;
|
||||
}
|
||||
|
||||
export default async function LiveWireTicker() {
|
||||
const sb = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
db: { schema: (process.env.NEXT_PUBLIC_SUPABASE_SCHEMA || "bb") as "public" },
|
||||
auth: { persistSession: false },
|
||||
}
|
||||
);
|
||||
|
||||
const [wp, ai] = await Promise.all([
|
||||
sb
|
||||
.from("wp_imported_posts")
|
||||
.select("wp_slug, title, wp_published_at")
|
||||
.eq("status", "published")
|
||||
.order("wp_published_at", { ascending: false })
|
||||
.limit(12),
|
||||
sb
|
||||
.from("ai_rewritten_articles")
|
||||
.select("slug, title, published_at")
|
||||
.eq("status", "published")
|
||||
.order("published_at", { ascending: false, nullsFirst: false })
|
||||
.limit(6),
|
||||
]);
|
||||
|
||||
const items: Item[] = [];
|
||||
for (const r of (wp.data || []) as any[]) {
|
||||
items.push({
|
||||
slug: r.wp_slug,
|
||||
title: r.title,
|
||||
published_at: r.wp_published_at,
|
||||
source_code: codeFor(r.title || ""),
|
||||
});
|
||||
}
|
||||
for (const r of (ai.data || []) as any[]) {
|
||||
items.push({
|
||||
slug: r.slug,
|
||||
title: r.title,
|
||||
published_at: r.published_at,
|
||||
source_code: codeFor(r.title || ""),
|
||||
});
|
||||
}
|
||||
items.sort(
|
||||
(a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime()
|
||||
);
|
||||
|
||||
const display = items.slice(0, 14);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full bg-[var(--color-background-secondary,#111)] border-b border-[#252525] overflow-hidden"
|
||||
role="region"
|
||||
aria-label="Live wire ticker"
|
||||
>
|
||||
<div className="max-w-container mx-auto px-4 h-[36px] flex items-center gap-4 text-[12px]">
|
||||
<span className="inline-flex items-center gap-1.5 font-mono uppercase tracking-wider text-[var(--color-text-danger,#ef4444)] shrink-0">
|
||||
<span className="bb-pulse-dot bb-pulse-dot--red" /> Live wire
|
||||
</span>
|
||||
<div className="overflow-hidden flex-1">
|
||||
<div className="bb-marquee">
|
||||
{[...display, ...display].map((it, i) => (
|
||||
<Link
|
||||
key={`${it.slug}-${i}`}
|
||||
href={`/news/${it.slug}`}
|
||||
className="inline-flex items-center gap-2 px-4 text-[#d1d5db] hover:text-[var(--color-text-info,#60a5fa)] whitespace-nowrap"
|
||||
>
|
||||
<span className="font-mono text-[#6b7280]">[{hhmm(it.published_at)}]</span>
|
||||
<span className="font-mono text-[var(--color-text-info,#60a5fa)]">[{it.source_code}]</span>
|
||||
<span>{it.title}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -807,197 +807,4 @@ h1, h2, h3 {
|
||||
.ProseMirror strong { font-weight: 700; color: #fff; }
|
||||
.ProseMirror em { font-style: italic; }
|
||||
.ProseMirror u { text-decoration: underline; }
|
||||
.ProseMirror s { text-decoration: line-through; }
|
||||
|
||||
/* =====================
|
||||
DOUBLE TICKER (LIVE WIRE + RECENT FORUM POSTS)
|
||||
===================== */
|
||||
.bb-ticker-stack {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #1a1a1a;
|
||||
}
|
||||
|
||||
.bb-ticker {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bb-ticker--wire {
|
||||
background: linear-gradient(90deg, #0a0a0a 0%, #161616 50%, #0a0a0a 100%);
|
||||
border-bottom: 1px solid #1f1212;
|
||||
}
|
||||
|
||||
.bb-ticker--forum {
|
||||
background: linear-gradient(90deg, #08120e 0%, #0f1d18 50%, #08120e 100%);
|
||||
}
|
||||
|
||||
.bb-ticker-label {
|
||||
flex-shrink: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0 16px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.22em;
|
||||
color: #ffffff;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.bb-ticker-label--red {
|
||||
background: linear-gradient(90deg, #d40000 0%, #a30000 100%);
|
||||
box-shadow: 0 0 18px rgba(212, 0, 0, 0.45), inset 0 -1px 0 rgba(255, 255, 255, 0.05);
|
||||
clip-path: polygon(0 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.bb-ticker-label--green {
|
||||
background: linear-gradient(90deg, #0c8a63 0%, #086149 100%);
|
||||
box-shadow: 0 0 18px rgba(12, 138, 99, 0.40), inset 0 -1px 0 rgba(255, 255, 255, 0.05);
|
||||
clip-path: polygon(0 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.bb-ticker-pulse {
|
||||
display: inline-block;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 8px rgba(255, 255, 255, 0.9);
|
||||
animation: bb-ticker-pulse-anim 1.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.bb-ticker-pulse--green {
|
||||
animation-duration: 1.8s;
|
||||
}
|
||||
|
||||
@keyframes bb-ticker-pulse-anim {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.4; transform: scale(1.25); }
|
||||
}
|
||||
|
||||
.bb-ticker-mask {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
-webkit-mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
|
||||
mask-image: linear-gradient(to right, transparent 0, #000 28px, #000 calc(100% - 28px), transparent 100%);
|
||||
}
|
||||
|
||||
.bb-ticker-track {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: max-content;
|
||||
white-space: nowrap;
|
||||
animation-name: bb-ticker-scroll;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Different speeds so the two lanes never sync up — feels alive */
|
||||
.bb-ticker-track--wire { animation-duration: 65s; }
|
||||
.bb-ticker-track--forum { animation-duration: 82s; }
|
||||
|
||||
.bb-ticker-track:hover,
|
||||
.bb-ticker-track:focus-within {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
@keyframes bb-ticker-scroll {
|
||||
0% { transform: translate3d(0, 0, 0); }
|
||||
100% { transform: translate3d(-50%, 0, 0); }
|
||||
}
|
||||
|
||||
.bb-ticker-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 0 22px;
|
||||
height: 34px;
|
||||
text-decoration: none;
|
||||
font-family: var(--font-body);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.005em;
|
||||
color: #d8d8d8;
|
||||
transition: color 0.18s ease;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.bb-ticker-item--wire:hover { color: #ff6b6b; }
|
||||
.bb-ticker-item--forum:hover { color: #5eead4; }
|
||||
|
||||
.bb-ticker-bullet {
|
||||
flex-shrink: 0;
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
|
||||
.bb-ticker-item--wire:hover .bb-ticker-bullet { color: #ff6b6b; }
|
||||
.bb-ticker-item--forum:hover .bb-ticker-bullet { color: #5eead4; }
|
||||
|
||||
/* =====================
|
||||
BROWSE NAV BAR (sits below the 728x90 header banner)
|
||||
===================== */
|
||||
.bb-browse-bar {
|
||||
background: linear-gradient(180deg, #0b0b0b 0%, #0e0e0e 100%);
|
||||
border-top: 1px solid #1a1a1a;
|
||||
border-bottom: 1px solid #1a1a1a;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.bb-browse-row {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.bb-browse-item {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: #cccccc;
|
||||
text-decoration: none;
|
||||
border-right: 1px solid #1a1a1a;
|
||||
transition: background 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.bb-browse-item:first-of-type {
|
||||
border-left: 1px solid #1a1a1a;
|
||||
}
|
||||
|
||||
.bb-browse-item:hover,
|
||||
.bb-browse-item[aria-expanded="true"] {
|
||||
background: #161616;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.bb-browse-item .bb-browse-caret {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
font-size: 8px;
|
||||
opacity: 0.7;
|
||||
transform: translateY(-1px);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.bb-browse-item[aria-expanded="true"] .bb-browse-caret {
|
||||
transform: rotate(180deg) translateY(1px);
|
||||
}
|
||||
.ProseMirror s { text-decoration: line-through; }
|
||||
Reference in New Issue
Block a user