spb/market-atlas
Public
TypeScript 96.7%
SQL 1.6%
CSS 0.8%
JavaScript 0.5%
1"use client";23import { useMemo } from "react";4import { EventRow } from "./event-row";5import { cx } from "@/lib/format";6import { useLiveEvents, useMarketStream } from "@/lib/stream";7import type { MarketEvent } from "@/lib/types";89/** Initial events (SSR) merged with events arriving on `events:*` (or a narrower channel). */10export function LiveEvents({ initial, channel = "events:*", max = 40, dense, className, types }: { initial: MarketEvent[]; channel?: string; max?: number; dense?: boolean; className?: string; types?: string[] }) {11 useMarketStream([channel]);12 const live = useLiveEvents(max);13 const merged = useMemo(() => {14 const seen = new Set<string>();15 const out: MarketEvent[] = [];16 for (const e of [...live, ...initial]) {17 if (seen.has(e.id)) continue;18 if (types && !types.includes(e.type)) continue;19 seen.add(e.id);20 out.push(e);21 if (out.length >= max) break;22 }23 return out;24 }, [live, initial, max, types]);25 if (!merged.length) return <div className={cx("rounded-md border border-dashed border-rule px-4 py-8 text-center text-sm text-ink-3", className)}>No events yet — the feed fills as sources emit changes.</div>;26 return (27 <ul className={cx("rounded-md border border-rule bg-surface px-3", className)}>28 {merged.map((e) => (29 <EventRow key={e.id} e={e} dense={dense} />30 ))}31 </ul>32 );33}34