Переглянути джерело

Save scene in URL (#220)

Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
Timur Khazamov 5 роки тому
батько
коміт
db973c61e8
3 змінених файлів з 40 додано та 3 видалено
  1. 18 2
      src/index.tsx
  2. 19 0
      src/scene/data.ts
  3. 3 1
      src/scene/index.ts

+ 18 - 2
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<T extends any[]>(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(<App />, rootElement);
 const canvas = document.getElementById("canvas") as HTMLCanvasElement;

+ 19 - 0
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);
+}

+ 3 - 1
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";