import { useCallback, useEffect, useRef, useState } from "react"; import type { EnvironmentSettings, UploadedEnvironmentMedia } from "@nodedc/ui-core"; import { APIError, request } from "./api"; export function defaultPresentation(): EnvironmentSettings { return { revision: 0, pages: { home: { headerLabel: "Mission Core Node", eyebrow: "NODEDC / MISSION CORE NODE", title: "Mission Core Node", description: "Подключение устройств, запись и просмотр данных на бортовом компьютере.", primaryWorkspaceId: "sensors", secondaryWorkspaceId: "environment", background: { enabled: false, imageDurationSeconds: 10, items: [] }, } } }; } export function usePresentation(authorized: boolean) { const [settings, setSettings] = useState(defaultPresentation); const [state, setState] = useState<"loading" | "ready" | "saving" | "error">("loading"); const [error, setError] = useState(null); const epoch = useRef(0); const refresh = useCallback(async () => { if (!authorized) return; const generation = ++epoch.current; setState("loading"); try { const next = await request("/api/presentation/settings"); if (generation !== epoch.current) return; if (next.schema !== "missioncore.node.presentation/v1" || !next.pages.home || Object.keys(next.pages).length !== 1) throw new Error("Версия оформления главной не поддерживается."); setSettings(next); setError(null); setState("ready"); } catch (reason) { if (generation !== epoch.current) return; setError(reason instanceof Error ? reason.message : "Не удалось загрузить оформление главной."); setState("error"); } }, [authorized]); useEffect(() => { void refresh(); return () => { epoch.current += 1; }; }, [refresh]); const save = useCallback(async (draft: EnvironmentSettings) => { setState("saving"); setError(null); try { const next = await request("/api/presentation/settings", "PUT", draft); setSettings(next); setState("ready"); return next; } catch (reason) { const message = reason instanceof Error ? reason.message : "Не удалось сохранить оформление главной."; setError(message); setState("error"); throw new Error(message); } }, []); const upload = useCallback(async (surfaceId: string, itemId: string, file: File): Promise => { if (surfaceId !== "home") throw new Error("Оформление доступно только для главной страницы."); const response = await fetch(`/api/presentation/media/home/${encodeURIComponent(itemId)}`, { method: "PUT", credentials: "same-origin", signal: AbortSignal.timeout(300000), headers: { "Content-Type": file.type || "application/octet-stream", "X-NODEDC-File-Name": encodeURIComponent(file.name) }, body: file, }); if (!response.ok) { const body = await response.json().catch(() => ({})); throw new APIError(body.error ?? "Не удалось загрузить фон главной страницы.", response.status); } return response.json(); }, []); return { settings, state, error, refresh, save, upload }; }