homepage: auto-promote advertiser stories + premium slots in Industry News
Three-tier sort on the Industry News feed: 1. Premium — articles pinned via adv.featured_story_slots (paid slots). Sorted by position ascending. Highlighted with an amber left-border gradient + ★ Featured ribbon. 2. Advertiser — articles whose title contains the company name of a current active banner advertiser on this property, AND were published in the last 48 hours. Highlighted with a blue left-border gradient + ◆ Advertiser ribbon. Multiple matches ordered by publish date. 3. General — everything else, newest first. After 48h, advertiser stories naturally drop back into the general tier. Premium slots fall off when their ends_at passes. Detection is title-only with case-insensitive word-boundary match; the longest matching company name wins so "Sony Pictures" beats "Sony" when both advertise. Backed by the new adv.active_advertisers_by_ property view. createAdminClient() now accepts an optional schema arg so we can hit adv from BB without juggling clients. API revalidate dropped 300 → 60s so the feed reflects campaign flips within a minute.
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getLatestImportedArticles } from "@/lib/articles/legacy-source";
|
||||
import { decorateHomepageFeed } from "@/lib/promoted-articles";
|
||||
|
||||
export const revalidate = 300;
|
||||
// Lowered from 300 → 60 so the promoted-story sort reflects current
|
||||
// advertiser state within a minute of a campaign flip.
|
||||
export const revalidate = 60;
|
||||
|
||||
// Homepage feed now renders every imported post; pagination + infinite-
|
||||
// scroll happen client-side over the full set. Keep an absolute ceiling
|
||||
// only as a safety valve, not a UX cap.
|
||||
const MAX_LIMIT = 10000;
|
||||
const DEFAULT_LIMIT = 100;
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function GET(request: Request) {
|
||||
|
||||
const { items, total } = await getLatestImportedArticles(limit, offset);
|
||||
|
||||
const payload = items.map((a) => ({
|
||||
const base = items.map((a) => ({
|
||||
title: a.title,
|
||||
excerpt: a.excerpt,
|
||||
category: a.category,
|
||||
@@ -33,6 +33,16 @@ export async function GET(request: Request) {
|
||||
source: "imported" as const,
|
||||
}));
|
||||
|
||||
// Decorate + reorder so premium and current-advertiser stories surface
|
||||
// at the top of the feed.
|
||||
const decorated = await decorateHomepageFeed("broadcastbeat", base);
|
||||
const payload = decorated.map((d) => ({
|
||||
...d.article,
|
||||
promoted_kind: d.promoted_kind,
|
||||
promoted_advertiser: d.promoted_advertiser || null,
|
||||
promoted_until: d.promoted_until || null,
|
||||
}));
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
items: payload,
|
||||
@@ -43,7 +53,7 @@ export async function GET(request: Request) {
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Cache-Control": "public, s-maxage=300, stale-while-revalidate=600",
|
||||
"Cache-Control": "public, s-maxage=60, stale-while-revalidate=120",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user