51 lines
3.0 KiB
TypeScript
51 lines
3.0 KiB
TypeScript
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<EnvironmentStatus | null>(null);
|
|
const [pending, setPending] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
const refresh = useCallback(async () => {
|
|
setLoading(true);
|
|
try {setValue(await request<EnvironmentStatus>("/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<EnvironmentRun>).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"};
|
|
}
|