/** * @file sessionChange.ts * @description `session_created` / `session_updated` matcher for live-refresh * callers that care about only a few session columns. A session row is * re-broadcast on nearly every hook (last activity, name, tokens…), so * matching the bare message types made every such frame refetch; this one * fires on a new session, or when the chosen key of a session actually * changed. Each call keeps its own memory of the keys it has seen. * @author Nguyễn Ngọc Trí Vĩ */ import type { Session, WSMessage } from "./types"; /** The columns that move a session between the active / waiting / archived counts. */ export const sessionStateKeyOf = (s: Partial): string => `${s.status}:${s.awaiting_input_since ? 1 : 0}:${s.archived_at ? 1 : 0}`; export function sessionChangeMatcher( keyOf: (s: Partial) => string = sessionStateKeyOf ): (msg: WSMessage) => boolean { const seen = new Map(); return (msg) => { if (msg.type !== "session_created" && msg.type !== "session_updated") return false; const s = msg.data as Partial | undefined; // A frame we cannot key is taken as a change: refetching is the safe side. if (!s || typeof s.id !== "string") return true; const key = keyOf(s); if (msg.type === "session_updated" && seen.get(s.id) === key) return false; seen.set(s.id, key); return true; }; }