"use client";
import { cx, formatDateTime, formatDuration, relativeTime, STATUS_LABEL, toMs } from "@/lib/format";
import { useLiveQuote, useNow } from "@/lib/stream";
import type { Quote } from "@/lib/types";
/**
* Human freshness label, ticking every second:
* REALTIME → "Updated 320 ms ago" · DELAYED → "Delayed 15 min · 12s ago" · AT_CLOSE → "At close · Fri 16:00 ET"
* END_OF_DAY → "End of day · Sep 11" · STALE → "Stale · last 4h 12m ago" · WITHHELD → "Withheld (rights)".
*/
export function FreshnessLabel({ quote, className, exchangeTz }: { quote: Quote | null | undefined; className?: string; exchangeTz?: string | null }) {
const now = useNow(1000);
const live = useLiveQuote(quote?.instrument_id);
if (!quote) return No observation yet;
const status = quote.data_status;
const liveTs = live && live.received >= Date.parse(quote.updated_at) ? live.timestamp : null;
const srcTs = liveTs ?? toMs(quote.source_timestamp) ?? toMs(quote.updated_at);
const age = srcTs != null && now ? Math.max(0, now - srcTs) : quote.freshness_ms;
let text: string;
switch (status) {
case "REALTIME":
text = age == null ? "Live" : age < 60_000 ? `Updated ${formatDuration(age)} ago` : `Live · last ${relativeTime(srcTs, now || Date.now())}`;
break;
case "DELAYED":
text = `Delayed 15 min · ${relativeTime(srcTs, now || Date.now())}`;
break;
case "AT_CLOSE":
text = `At close · ${formatDateTime(srcTs, { tz: exchangeTz ?? "America/New_York" })}`;
break;
case "END_OF_DAY":
text = `End of day · ${formatDateTime(srcTs, { tz: "UTC", dateOnly: true })}`;
break;
case "STALE":
text = `Stale · last update ${relativeTime(srcTs, now || Date.now())}`;
break;
case "WITHHELD":
text = "Value withheld (data rights)";
break;
default:
text = STATUS_LABEL[status] ?? status;
}
const tone = status === "REALTIME" ? "text-positive" : status === "STALE" || status === "WITHHELD" ? "text-stale" : status === "DELAYED" ? "text-warning" : "text-ink-3";
return (
{status === "REALTIME" && }
{text}
);
}
export function RelativeTime({ value, className }: { value: string | number | null | undefined; className?: string }) {
const now = useNow(1000);
const ms = typeof value === "number" ? value : toMs(value);
return (
);
}