import { useCallback, useEffect, useRef } from "react"; import type { AppStore } from "@editor/store"; import { createProjectLifecycle } from "@operations/project/lifecycle"; import { projectFileName } from "@operations/project/format"; import { browserRecoveryStorage, downloadProjectFile, readProjectFile } from "@platform/browser/projectFiles"; export function useProjectLifecycle(store: AppStore) { const inputRef = useRef(null); const lifecycleRef = useRef | null>(null); useEffect(() => { const lifecycle = createProjectLifecycle({ store, storage: browserRecoveryStorage }); lifecycleRef.current = lifecycle; void lifecycle.recover(); return () => { lifecycle.dispose(); lifecycleRef.current = null; }; }, [store]); const save = useCallback(() => { const lifecycle = lifecycleRef.current; if (!lifecycle) return; const document = store.getState().document; downloadProjectFile(lifecycle.snapshot(), projectFileName(document.name)); void lifecycle.markSaved(); }, [store]); const openFilePicker = useCallback(() => inputRef.current?.click(), []); const openFile = useCallback(async (file: File) => { const lifecycle = lifecycleRef.current; if (!lifecycle) return; try { await lifecycle.open(await readProjectFile(file)); } catch (error) { window.alert(error instanceof Error ? error.message : "The project could not be opened."); } }, []); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.defaultPrevented || (!event.metaKey && !event.ctrlKey)) return; if (event.key.toLowerCase() === "s") { event.preventDefault(); save(); } else if (event.shiftKey && event.key.toLowerCase() === "o") { event.preventDefault(); openFilePicker(); } }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [openFilePicker, save]); const input = ( { const file = event.currentTarget.files?.[0]; event.currentTarget.value = ""; if (file) void openFile(file); }} /> ); return { input, save, openFilePicker }; }