import { useCallback, useEffect, useState } from "react"; import { desktopAction, request } from "./api"; export interface EnvironmentStep { id: string; state: string; detail: string } export interface EnvironmentRun { schema: string; profile_revision: string; run_id: string; state: string; updated_at: number; steps: EnvironmentStep[] } export interface EnvironmentStatus { available: boolean; profile: {revision: string; steps:{id:string;label:string;description:string}[]}; run: EnvironmentRun | null } export function useEnvironment(authorized: boolean, failure: (error: unknown) => void) { const [value, setValue] = useState(null); const [pending, setPending] = useState(false); const [loading, setLoading] = useState(false); const refresh = useCallback(async () => { setLoading(true); try {setValue(await request("/api/environment"));} catch(error) {failure(error);} finally {setLoading(false);} },[failure]); // Native progress survives a service restart and never depends on a stale // HTTP session. Reopening an existing run uses the read-only status API. useEffect(() => { function progress(event:Event) { const run=(event as CustomEvent).detail; if(run?.schema==="missioncore.node.environment/v1") setValue(current=>current?{...current,available:true,run}:current); } function complete(event:Event) { const result=(event as CustomEvent<{ok:boolean;error?:string;reloading?:boolean}>).detail; if(!result?.reloading) setPending(false); if(result?.error) failure(new Error(result.error)); // The native launcher renews login after service changes. No immediate // request with the expired cookie is made here. } const sessionReady=()=>setPending(false); window.addEventListener("mission-core-session-ready",sessionReady); window.addEventListener("mission-core-environment-progress",progress); window.addEventListener("mission-core-environment-result",complete); return()=>{window.removeEventListener("mission-core-session-ready",sessionReady);window.removeEventListener("mission-core-environment-progress",progress);window.removeEventListener("mission-core-environment-result",complete);}; },[failure]); useEffect(()=>{if(authorized&&!pending) void refresh();},[authorized,pending,refresh]); useEffect(()=>{ if(!authorized||pending||value?.run?.state!=="running") return; const timer=setTimeout(()=>void refresh(),2000);return()=>clearTimeout(timer); },[authorized,pending,value,refresh]); function start() { if(pending||value?.run?.state==="running") return; if(!desktopAction("configure-system")) {failure(new Error("Откройте установленное приложение Node для настройки окружения."));return;} setValue(current=>current?{...current,run:null}:current); setPending(true); } return {value,pending,loading,refresh,start,running:pending||value?.run?.state==="running"}; }