import { parseAttention, parseDigest, parseSessionItem, parseSessionList } from "agents-shared/contract"; import { activityByDay } from "agents-shared/timeline"; import { useQuery, useQueryClient, type QueryKey } from "@tanstack/react-query"; import { useMemo } from "react"; import { getConditional, getJSON, HttpError, Unauthorized, type Conditional, type Parser } from "./api"; import { useCalendar } from "./zone"; const MINUTE = 60_000; export function useDigest() { const cal = useCalendar(); return useQuery({ queryKey: ["digest", cal.zone], queryFn: ({ signal }) => getJSON(`/api/attention/digest?tz=${encodeURIComponent(cal.zone)}`, parseDigest, signal), staleTime: MINUTE, refetchInterval: MINUTE, }); } export function useAttention() { return useQuery({ queryKey: ["attention"], queryFn: ({ signal }) => getJSON("/api/attention", parseAttention, signal), staleTime: MINUTE, refetchInterval: MINUTE, }); } /** Interactions per day for the day strip. */ export function useActivity() { const cal = useCalendar(); const { data } = useAttention(); return useMemo(() => (data ? activityByDay(data, cal) : null), [data, cal]); } function useConditional(key: QueryKey, path: string, parse: Parser, interval: number | false) { const client = useQueryClient(); return useQuery({ queryKey: key, queryFn: ({ signal }) => getConditional(path, parse, client.getQueryData>(key), signal), refetchInterval: interval, staleTime: interval === false ? 30_000 : interval - 500, }); } /** Metadata only; unchanged polls cost a 304. */ export function useSessionList(active: boolean) { return useConditional(["sessions"], "/api/sessions?view=list", parseSessionList, active ? 5000 : false); } export function useSessionItem(key: string, active: boolean) { return useConditional(["session", key], `/api/sessions/item?id=${encodeURIComponent(key)}`, parseSessionItem, active ? 3000 : false); } export function describeFailure(error: Error | null) { if (error instanceof Unauthorized) return "Sign in to see your history."; if (error instanceof HttpError && error.status === 503) return "Your Mac is busy. Showing what was already loaded."; return "Couldn’t reach your Mac. Showing what was already loaded."; }