"use client"; import { useQuery } from "@tanstack/react-query"; import { useMemo } from "react"; import { getMyMeshStreamResponseSchema, type GetMyMeshStreamResponse, } from "@turbostarter/api/schema"; import { handle } from "@turbostarter/api/utils"; import { api } from "~/lib/api/client"; import { PeerGraph, type GraphPeer, type GraphEdge, } from "~/modules/mesh/peer-graph"; const POLL_INTERVAL_MS = 4000; /* ------------------------------------------------------------------ */ /* Transform broker response into graph-friendly structures */ /* ------------------------------------------------------------------ */ const buildGraphData = (data: GetMyMeshStreamResponse) => { // Count messages per sender const countMap = new Map(); for (const e of data.envelopes) { countMap.set(e.senderMemberId, (countMap.get(e.senderMemberId) ?? 0) + 1); } const peers: GraphPeer[] = data.presences.map((p) => ({ id: p.memberId, name: p.displayName ?? p.memberId.slice(0, 8), status: p.status === "dnd" ? "dnd" : p.status, messageCount: countMap.get(p.memberId) ?? 0, })); const edges: GraphEdge[] = data.envelopes.map((e) => ({ key: e.id, from: e.senderMemberId, to: e.targetSpec === "*" ? null : e.targetSpec, priority: e.priority, createdAt: new Date(e.createdAt), })); return { peers, edges }; }; /* ------------------------------------------------------------------ */ /* Panel component */ /* ------------------------------------------------------------------ */ export const PeerGraphPanel = ({ meshId }: { meshId: string }) => { const { data, isFetching, dataUpdatedAt } = useQuery({ queryKey: ["mesh", "stream", meshId], queryFn: () => handle(api.my.meshes[":id"].stream.$get, { schema: getMyMeshStreamResponseSchema, })({ param: { id: meshId } }), refetchInterval: POLL_INTERVAL_MS, refetchIntervalInBackground: false, }); const { peers, edges } = useMemo( () => (data ? buildGraphData(data) : { peers: [], edges: [] }), [data], ); const secondsAgo = dataUpdatedAt ? Math.max(0, Math.floor((Date.now() - dataUpdatedAt) / 1000)) : null; return (
{/* Header */}
peer graph
{peers.length} peers ยท{" "} {isFetching ? "polling\u2026" : `${secondsAgo ?? "\u2014"}s ago`}
{/* Graph area */}
{/* Legend */}
idle working dnd | low next now
); };