nearly the entire application state is saved in URL
This commit is contained in:
parent
c7e661eb61
commit
49e701c7d7
7 changed files with 338 additions and 126 deletions
|
|
@ -3,9 +3,8 @@ import { HISTORY_RADIUS } from "./parameters";
|
|||
import { Dispatch, SetStateAction, useCallback, useEffect } from "react";
|
||||
import { EditHistory } from "./App";
|
||||
import { VisualEditorState } from "./VisualEditor/VisualEditor";
|
||||
import { emptyState } from "@/statecharts/concrete_syntax";
|
||||
|
||||
export function useEditor(editorState: VisualEditorState | null, setEditHistory: Dispatch<SetStateAction<EditHistory|null>>) {
|
||||
export function useEditor(setEditHistory: Dispatch<SetStateAction<EditHistory|null>>) {
|
||||
useEffect(() => {
|
||||
console.info("Welcome to StateBuddy!");
|
||||
() => {
|
||||
|
|
@ -13,64 +12,6 @@ export function useEditor(editorState: VisualEditorState | null, setEditHistory:
|
|||
}
|
||||
}, []);
|
||||
|
||||
// recover editor state from URL - we need an effect here because decompression is asynchronous
|
||||
useEffect(() => {
|
||||
console.log('recovering state...');
|
||||
const compressedState = window.location.hash.slice(1);
|
||||
if (compressedState.length === 0) {
|
||||
// empty URL hash
|
||||
console.log("no state to recover");
|
||||
setEditHistory(() => ({current: emptyState, history: [], future: []}));
|
||||
return;
|
||||
}
|
||||
let compressedBuffer;
|
||||
try {
|
||||
compressedBuffer = Uint8Array.fromBase64(compressedState); // may throw
|
||||
} catch (e) {
|
||||
// probably invalid base64
|
||||
console.error("failed to recover state:", e);
|
||||
setEditHistory(() => ({current: emptyState, history: [], future: []}));
|
||||
return;
|
||||
}
|
||||
const ds = new DecompressionStream("deflate");
|
||||
const writer = ds.writable.getWriter();
|
||||
writer.write(compressedBuffer).catch(() => {}); // any promise rejections will be detected when we try to read
|
||||
writer.close().catch(() => {});
|
||||
new Response(ds.readable).arrayBuffer()
|
||||
.then(decompressedBuffer => {
|
||||
const recoveredState = JSON.parse(new TextDecoder().decode(decompressedBuffer));
|
||||
setEditHistory(() => ({current: recoveredState, history: [], future: []}));
|
||||
})
|
||||
.catch(e => {
|
||||
// any other error: invalid JSON, or decompression failed.
|
||||
console.error("failed to recover state:", e);
|
||||
setEditHistory({current: emptyState, history: [], future: []});
|
||||
});
|
||||
}, []);
|
||||
|
||||
// save editor state in URL
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
if (editorState === null) {
|
||||
window.location.hash = "#";
|
||||
return;
|
||||
}
|
||||
const serializedState = JSON.stringify(editorState);
|
||||
const stateBuffer = new TextEncoder().encode(serializedState);
|
||||
const cs = new CompressionStream("deflate");
|
||||
const writer = cs.writable.getWriter();
|
||||
writer.write(stateBuffer);
|
||||
writer.close();
|
||||
// todo: cancel this promise handler when concurrently starting another compression job
|
||||
new Response(cs.readable).arrayBuffer().then(compressedStateBuffer => {
|
||||
const compressedStateString = new Uint8Array(compressedStateBuffer).toBase64();
|
||||
window.location.hash = "#"+compressedStateString;
|
||||
});
|
||||
}, 100);
|
||||
return () => clearTimeout(timeout);
|
||||
}, [editorState]);
|
||||
|
||||
|
||||
// append editor state to undo history
|
||||
const makeCheckPoint = useCallback(() => {
|
||||
setEditHistory(historyState => historyState && ({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue