import { computed, onMounted, onUnmounted, readonly, shallowRef } from "vue"; import type { DesktopRuntimeSnapshot } from "../types"; const RUNTIME_POLL_INTERVAL_MS = 15_000; const snapshot = shallowRef(null); const loading = shallowRef(false); const error = shallowRef(null); let intervalHandle: ReturnType | null = null; let subscribers = 0; let visibilityListenerBound = false; let runtimeInvalidationUnsubscribe: (() => void) | null = null; function isPageVisible(): boolean { return typeof document === "undefined" || document.visibilityState === "visible"; } async function refreshRuntimeSnapshot() { loading.value = true; error.value = null; try { if (!window.desktopBridge?.app?.runtimeSnapshot) { snapshot.value = null; error.value = "desktop runtime bridge unavailable"; return; } snapshot.value = await window.desktopBridge.app.runtimeSnapshot(); } catch (err) { error.value = err instanceof Error ? err.message : "runtime snapshot unavailable"; } finally { loading.value = false; } } async function refreshAccounts() { if (!window.desktopBridge?.app?.refreshRuntimeAccounts) { await refreshRuntimeSnapshot(); return; } loading.value = true; error.value = null; try { await window.desktopBridge.app.refreshRuntimeAccounts(); snapshot.value = await window.desktopBridge.app.runtimeSnapshot(); } catch (err) { error.value = err instanceof Error ? err.message : "refresh accounts failed"; } finally { loading.value = false; } } function stopPolling() { if (!intervalHandle) { return; } clearInterval(intervalHandle); intervalHandle = null; } function syncPollingState() { if (subscribers === 0 || !isPageVisible()) { stopPolling(); return; } if (intervalHandle) { return; } intervalHandle = setInterval(() => { void refreshRuntimeSnapshot(); }, RUNTIME_POLL_INTERVAL_MS); } function handleVisibilityChange() { if (isPageVisible()) { void refreshRuntimeSnapshot(); } syncPollingState(); } function bindVisibilityListener() { if (visibilityListenerBound || typeof document === "undefined") { return; } document.addEventListener("visibilitychange", handleVisibilityChange); visibilityListenerBound = true; } function bindRuntimeInvalidationListener() { if (runtimeInvalidationUnsubscribe || !window.desktopBridge?.app?.onRuntimeInvalidated) { return; } runtimeInvalidationUnsubscribe = window.desktopBridge.app.onRuntimeInvalidated(() => { if (isPageVisible()) { void refreshRuntimeSnapshot(); } }); } function unbindVisibilityListener() { if (!visibilityListenerBound || typeof document === "undefined") { return; } document.removeEventListener("visibilitychange", handleVisibilityChange); visibilityListenerBound = false; } function unbindRuntimeInvalidationListener() { runtimeInvalidationUnsubscribe?.(); runtimeInvalidationUnsubscribe = null; } export function useDesktopRuntime() { onMounted(() => { subscribers += 1; bindVisibilityListener(); bindRuntimeInvalidationListener(); if (!snapshot.value && !loading.value) { void refreshRuntimeSnapshot(); } syncPollingState(); }); onUnmounted(() => { subscribers = Math.max(0, subscribers - 1); if (subscribers === 0) { stopPolling(); unbindVisibilityListener(); unbindRuntimeInvalidationListener(); return; } syncPollingState(); }); return { snapshot: readonly(snapshot), loading: readonly(loading), error: readonly(error), refresh: refreshRuntimeSnapshot, refreshAccounts, hasSnapshot: computed(() => snapshot.value !== null), }; }