import type { UpdateItem } from "../../lib/types"; import { formatAgo } from "../../lib/utils"; const isEmoji = (s: string) => !!s && /\p{Extended_Pictographic}/u.test(s); // Same slug cleaner used above const cleanTitle = (t?: string) => { if (!t) return t; return t .replace(/^(?:[a-z0-9]+-){1,3}(?=[A-Z])/i, "") .replace(/^(?:[a-z0-9]+-){1,3}\s+/i, ""); }; export default function UpdatesPanel({ activeTab, setActiveTab, localUpdates, globalUpdates, loadingLocal, loadingGlobal, selectedLL, onView, reactionsById, onReact, }: { activeTab: "local" | "global"; setActiveTab: (t: "local" | "global") => void; localUpdates: UpdateItem[]; globalUpdates: UpdateItem[]; loadingLocal: boolean; loadingGlobal: boolean; selectedLL: [number, number] | null; onView: (u: UpdateItem) => void; reactionsById: Record; onReact: (rid: string, action: "verify" | "clear") => void; }) { const renderList = ( list: UpdateItem[], loading: boolean, emptyMsg: string ) => ( <> {loading &&
Loading…
} {!loading && list.length === 0 &&
{emptyMsg}
} {!loading && list.map((u, i) => { // get reaction info const rid = u.rid; const rx = rid ? reactionsById[rid] : null; const meVerified = !!rx?.me?.verified; const meCleared = !!rx?.me?.cleared; const verifyCount = rx?.verify_count ?? 0; const clearCount = rx?.clear_count ?? 0; const showEmoji = u.emoji && isEmoji(String(u.emoji)) ? u.emoji : null; const title = cleanTitle(u.title) || u.title || "Update"; return (
{showEmoji ?
{showEmoji}
: null}
{title}
{formatAgo(u.time)} · {u.kind} {u.severity ? <> · {String(u.severity)} : null}
{u.sourceUrl && (
Source
)}
{u.kind === "report" && rid ? ( <> ) : null}
); })} ); return (
e.stopPropagation()} > {activeTab === "local" ? ( selectedLL ? ( renderList(localUpdates, loadingLocal, "No recent updates here.") ) : (
Pick a point (search/📍/click) to load local updates within 25 miles (last 48h).
) ) : ( renderList( globalUpdates, loadingGlobal, "No global updates right now." ) )}
); }