feat: broadcast

This commit is contained in:
gosangam
2026-08-29 18:34:21 +05:30
parent 011b5bba91
commit 03c8799f22
4 changed files with 181 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
/**
* Browser-side client for admin announcements.
*
* Deliberately thin: an announcement is fetched, shown once, and dismissed.
* Nothing is cached client-side — "have I seen this?" is the server's answer,
* so it survives a new device, a cleared browser, and a second tab.
*/
import type { MessageEntity } from "@/lib/chat";
const BFF_BASE = process.env.NEXT_PUBLIC_BFF_URL || "http://localhost:8080";
async function call<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${BFF_BASE}/api/v1${path}`, {
credentials: "include",
headers: { "Content-Type": "application/json" },
...init,
});
if (!res.ok) throw new Error(`broadcast request failed: ${res.status}`);
if (res.status === 204) return undefined as T;
const json = await res.json();
return (json?.data ?? json) as T;
}
export type Broadcast = {
id: string;
title: string;
body: string;
/** Formatting spans over `body`, same shape chat messages use. */
entities: MessageEntity[];
banner_url?: string | null;
cta_label?: string | null;
cta_url?: string | null;
published_at?: string | null;
expires_at?: string | null;
};
export const broadcasts = {
/** The announcement waiting for this user, or null. */
pending: () => call<Broadcast | null>("/broadcasts/pending"),
/** Dismiss it. Idempotent, so a double-tap or two tabs are both fine. */
markSeen: (id: string) =>
call<void>(`/broadcasts/${id}/seen`, { method: "POST" }),
};