"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { useAuth } from "@/auth/clerk"; import { customFetch } from "@/api/mutator"; interface TickerItem { id: string; source: string; message: string; created_at: string; } function fmtRelative(isoString: string): string { const diffMs = Date.now() - new Date(isoString).getTime(); const s = Math.round(diffMs / 1000); if (s < 60) return `${s}s ago`; const m = Math.floor(s / 60); if (m < 60) return `${m}m ago`; const h = Math.floor(m / 60); if (h < 24) return `${h}h ago`; return `${Math.floor(h / 24)}d ago`; } async function fetchTickerItems(limit = 20): Promise { const res = await customFetch<{ data: TickerItem[]; status: number }>( `/api/v1/activity/ticker?limit=${limit}`, { method: "GET" }, ); if (res.status === 200) return res.data; return []; } export function AgentActivityTicker() { const { isSignedIn } = useAuth(); const [items, setItems] = useState([]); const intervalRef = useRef | null>(null); const load = useCallback(async () => { try { const data = await fetchTickerItems(20); if (data.length > 0) setItems(data); } catch { // Silent — ticker is non-critical } }, []); useEffect(() => { if (!isSignedIn) return; void load(); intervalRef.current = setInterval(() => void load(), 30_000); return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [isSignedIn, load]); if (items.length === 0) return null; // Duplicate items for a seamless loop (animate-ticker moves -50%) const display = [...items, ...items]; return (
Live
{display.map((item, idx) => ( {item.source} · {item.message} {fmtRelative(item.created_at)} ))}
); }