import { writable, get } from 'svelte/store' import { fetchDashboardSummary, fetchApprovals, type DashboardSummary } from '$lib/api' import { liveEvents, subscribeEvents, type OikosEvent } from './events' // Shared operational context: dashboard summary + pending approvals, // refreshed on a slow poll and eagerly on relevant SSE events. Ref-counted // so the poll only runs while something on screen displays it. export const summary = writable(null) let refs = 0 let pollTimer: ReturnType | null = null let unsubscribeSSE: (() => void) | null = null let unsubscribeStore: (() => void) | null = null let lastSeenEventId = 0 export async function refreshContext() { const [s] = await Promise.all([fetchDashboardSummary(), fetchApprovals('pending')]) if (s) summary.set(s) } function onEvent(ev: OikosEvent) { if (ev.id <= lastSeenEventId) return lastSeenEventId = ev.id if ( ev.type.startsWith('approval.') || ev.type.startsWith('signal.') || ev.type.startsWith('execution.') || ev.type === 'health.changed' ) { refreshContext() } } export function subscribeContext(): () => void { refs++ if (refs === 1) { refreshContext() pollTimer = setInterval(refreshContext, 30000) unsubscribeSSE = subscribeEvents() unsubscribeStore = liveEvents.subscribe((events) => { if (events[0]) onEvent(events[0]) }) } return () => { refs-- if (refs === 0) { if (pollTimer) clearInterval(pollTimer) pollTimer = null unsubscribeSSE?.() unsubscribeSSE = null unsubscribeStore?.() unsubscribeStore = null } } } export function openSignalCount(s: DashboardSummary | null): number { if (!s) return 0 return Object.values(s.signals_by_severity).reduce((a, b) => a + b, 0) }