72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
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<HTMLInputElement | null>(null);
|
|
const lifecycleRef = useRef<ReturnType<typeof createProjectLifecycle> | 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 = (
|
|
<input
|
|
ref={inputRef}
|
|
type="file"
|
|
accept=".json,.image-studio.json,application/json"
|
|
className="hidden"
|
|
onChange={(event) => {
|
|
const file = event.currentTarget.files?.[0];
|
|
event.currentTarget.value = "";
|
|
if (file) void openFile(file);
|
|
}}
|
|
/>
|
|
);
|
|
|
|
return { input, save, openFilePicker };
|
|
}
|