"use client"; import { t } from "./i18n"; import { useCallback, useEffect, useState, useSyncExternalStore } from "react"; import { fetchHealth, type Health } from "./client-api"; import { settingsStore } from "./settings"; /** * Health status for the current browser (re-checks whenever the API key * changes). Used by the settings dialog and the API-key gate. * * Every page mounts its own gate, and one probe can sit for seconds while * Ollama answers — so probes are deduplicated per key and cached briefly * instead of firing one per mount. The manual re-check always forces. */ const HEALTH_TTL_MS = 30_000; let cache: { key: string; at: number; value: Health } | null = null; let inflight: { key: string; promise: Promise } | null = null; function loadHealth(key: string, force: boolean): Promise { if (!force && cache && cache.key === key && Date.now() - cache.at < HEALTH_TTL_MS) { return Promise.resolve(cache.value); } if (!force && inflight && inflight.key === key) return inflight.promise; const promise = fetchHealth() .then((value) => { cache = { key, at: Date.now(), value }; return value; }) .finally(() => { if (inflight?.promise === promise) inflight = null; }); inflight = { key, promise }; return promise; } export function useHealth() { const apiKey = useSyncExternalStore( settingsStore.subscribe, () => settingsStore.getSnapshot().apiKey, () => "", ); const [health, setHealth] = useState(() => cache && cache.key === apiKey ? cache.value : null, ); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [checkedAt, setCheckedAt] = useState(() => cache && cache.key === apiKey ? cache.at : null, ); const run = useCallback(async (force: boolean) => { setLoading(true); setError(null); try { const key = settingsStore.getSnapshot().apiKey; setHealth(await loadHealth(key, force)); } catch (loadError) { setError(loadError instanceof Error ? loadError.message : t("api.unreachableShort")); } finally { // Even a failed check is a check: the panel can say when it last ran. setCheckedAt(Date.now()); setLoading(false); } }, []); useEffect(() => { void run(false); }, [run, apiKey]); /** The settings dialog's re-check button: always a fresh probe. */ const refresh = useCallback(() => run(true), [run]); return { health, loading, error, refresh, apiKey, checkedAt }; }