diff --git a/src/index.tsx b/src/index.tsx index edb1110f..6abafda0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -25,6 +25,8 @@ import { exportAsPNG, restoreFromLocalStorage, saveToLocalStorage, + restoreFromURL, + saveToURL, hasBackground, hasStroke, getElementAtPosition, @@ -122,7 +124,8 @@ class App extends React.Component<{}, AppState> { document.addEventListener("keydown", this.onKeyDown, false); window.addEventListener("resize", this.onResize, false); - const savedState = restoreFromLocalStorage(elements); + const savedState = + restoreFromURL(elements) || restoreFromLocalStorage(elements); if (savedState) { this.setState(savedState); } @@ -997,6 +1000,11 @@ class App extends React.Component<{}, AppState> { })); }; + private saveDebounced = debounce(() => { + saveToLocalStorage(elements, this.state); + saveToURL(elements, this.state); + }, 300); + private addElementsFromPaste = (paste: string, x?: number, y?: number) => { let parsedElements; try { @@ -1037,7 +1045,7 @@ class App extends React.Component<{}, AppState> { scrollY: this.state.scrollY, viewBackgroundColor: this.state.viewBackgroundColor }); - saveToLocalStorage(elements, this.state); + this.saveDebounced(); if (history.isRecording()) { history.pushEntry(history.generateCurrentEntry(elements)); history.clearRedoStack(); @@ -1046,6 +1054,14 @@ class App extends React.Component<{}, AppState> { } } +function debounce(fn: (...args: T) => void, timeout: number) { + let handle = 0; + return (...args: T) => { + clearTimeout(handle); + handle = window.setTimeout(() => fn(...args), timeout); + }; +} + const rootElement = document.getElementById("root"); ReactDOM.render(, rootElement); const canvas = document.getElementById("canvas") as HTMLCanvasElement; diff --git a/src/scene/data.ts b/src/scene/data.ts index f9458f35..3dad1f75 100644 --- a/src/scene/data.ts +++ b/src/scene/data.ts @@ -181,3 +181,22 @@ export function saveToLocalStorage( localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(elements)); localStorage.setItem(LOCAL_STORAGE_KEY_STATE, JSON.stringify(state)); } + +export function restoreFromURL(elements: ExcalidrawElement[]) { + try { + const [savedElements, savedState] = document.location.hash + .slice(1) + .split(":") + .map(atob); + return restore(elements, savedElements, savedState); + } catch (ex) { + return null; + } +} + +export function saveToURL(elements: ExcalidrawElement[], state: AppState) { + const hash = [JSON.stringify(elements), JSON.stringify(state)] + .map(btoa) + .join(":"); + document.location.replace("#" + hash); +} diff --git a/src/scene/index.ts b/src/scene/index.ts index 8e96a4a3..2d7dc9e1 100644 --- a/src/scene/index.ts +++ b/src/scene/index.ts @@ -13,7 +13,9 @@ export { loadFromJSON, saveAsJSON, restoreFromLocalStorage, - saveToLocalStorage + saveToLocalStorage, + restoreFromURL, + saveToURL } from "./data"; export { hasBackground, hasStroke, getElementAtPosition } from "./comparisons"; export { createScene } from "./createScene";