瀏覽代碼

Pass Additional props remove localstorage related code for storing data and username from App.tsx to index.tsx (#2057)

Co-authored-by: dwelle <luzar.david@gmail.com>
Aakansha Doshi 4 年之前
父節點
當前提交
4718c31da5
共有 6 個文件被更改,包括 102 次插入54 次删除
  1. 5 31
      src/components/App.tsx
  2. 0 2
      src/components/Portal.tsx
  3. 9 7
      src/data/index.ts
  4. 3 3
      src/data/localStorage.ts
  5. 84 11
      src/index.tsx
  6. 1 0
      src/time_constants.ts

+ 5 - 31
src/components/App.tsx

@@ -44,7 +44,6 @@ import {
 } from "../scene";
 import {
   decryptAESGEM,
-  saveToLocalStorage,
   loadScene,
   loadFromBlob,
   SOCKET_SERVER,
@@ -146,11 +145,7 @@ import {
   isBindableElement,
 } from "../element/typeChecks";
 import { actionFinalize, actionDeleteSelected } from "../actions";
-import {
-  restoreUsernameFromLocalStorage,
-  saveUsernameToLocalStorage,
-  loadLibrary,
-} from "../data/localStorage";
+import { loadLibrary } from "../data/localStorage";
 
 import throttle from "lodash.throttle";
 import { LinearElementEditor } from "../element/linearElementEditor";
@@ -310,6 +305,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
       offsetLeft,
     } = this.state;
 
+    const { onUsernameChange } = this.props;
     const canvasScale = window.devicePixelRatio;
 
     const canvasWidth = canvasDOMWidth * canvasScale;
@@ -335,13 +331,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
           onRoomCreate={this.openPortal}
           onRoomDestroy={this.closePortal}
           onUsernameChange={(username) => {
-            if (this.props.onUsernameChange) {
-              this.props.onUsernameChange(username);
-            }
-            saveUsernameToLocalStorage(username);
-            this.setState({
-              username,
-            });
+            onUsernameChange && onUsernameChange(username);
+            this.setState({ username });
           }}
           onLockToggle={this.toggleLock}
           onInsertShape={(elements) =>
@@ -434,8 +425,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
   private onBlur = withBatchedUpdates(() => {
     isHoldingSpace = false;
     this.setState({ isBindingEnabled: true });
-    this.saveDebounced();
-    this.saveDebounced.flush();
   });
 
   private onUnload = () => {
@@ -510,7 +499,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
     let isCollaborationScene = !!getCollaborationLinkData(window.location.href);
     const isExternalScene = !!(id || jsonMatch || isCollaborationScene);
 
-    if (isExternalScene && !this.props.initialData) {
+    if (isExternalScene) {
       if (
         this.shouldForceLoadScene(scene) ||
         window.confirm(t("alerts.loadSceneOverridePrompt"))
@@ -840,7 +829,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
     if (this.state.scrolledOutside !== scrolledOutside) {
       this.setState({ scrolledOutside: scrolledOutside });
     }
-    this.saveDebounced();
 
     if (
       getDrawingVersion(this.scene.getElementsIncludingDeleted()) >
@@ -1433,16 +1421,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
     },
   );
 
-  restoreUserName() {
-    const username = restoreUsernameFromLocalStorage();
-
-    if (username !== null) {
-      this.setState({
-        username,
-      });
-    }
-  }
-
   // Input handling
 
   private onKeyDown = withBatchedUpdates((event: KeyboardEvent) => {
@@ -3679,10 +3657,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
     this.setState({ shouldCacheIgnoreZoom: false });
   }, 300);
 
-  private saveDebounced = debounce(() => {
-    saveToLocalStorage(this.scene.getElementsIncludingDeleted(), this.state);
-  }, 300);
-
   private getCanvasOffsets() {
     if (this.excalidrawRef?.current) {
       const parentElement = this.excalidrawRef.current.parentElement;

+ 0 - 2
src/components/Portal.tsx

@@ -24,8 +24,6 @@ class Portal {
     this.socket.on("init-room", () => {
       if (this.socket) {
         this.socket.emit("join-room", this.roomID);
-
-        this.app.restoreUserName();
       }
     });
     this.socket.on("new-user", async (_socketId: string) => {

+ 9 - 7
src/data/index.ts

@@ -18,12 +18,10 @@ import { serializeAsJSON } from "./json";
 
 import { ExportType } from "../scene/types";
 import { restore } from "./restore";
-import { restoreFromLocalStorage } from "./localStorage";
 import { DataState } from "./types";
 
 export { loadFromBlob } from "./blob";
 export { saveAsJSON, loadFromJSON } from "./json";
-export { saveToLocalStorage } from "./localStorage";
 
 const BACKEND_GET = process.env.REACT_APP_BACKEND_V1_GET_URL;
 
@@ -233,7 +231,7 @@ export const exportToBackend = async (
   }
 };
 
-export const importFromBackend = async (
+const importFromBackend = async (
   id: string | null,
   privateKey?: string | null,
 ) => {
@@ -246,7 +244,7 @@ export const importFromBackend = async (
     );
     if (!response.ok) {
       window.alert(t("alerts.importBackendFailed"));
-      return restore(elements, appState);
+      return { elements, appState };
     }
     let data;
     if (privateKey) {
@@ -277,7 +275,7 @@ export const importFromBackend = async (
     window.alert(t("alerts.importBackendFailed"));
     console.error(error);
   } finally {
-    return restore(elements, appState);
+    return { elements, appState };
   }
 };
 
@@ -374,9 +372,13 @@ export const loadScene = async (
   if (id != null) {
     // the private key is used to decrypt the content from the server, take
     // extra care not to leak it
-    data = await importFromBackend(id, privateKey);
+    const { elements, appState } = await importFromBackend(id, privateKey);
+    data = restore(elements, appState);
   } else {
-    data = initialData || restoreFromLocalStorage();
+    data = restore(
+      initialData?.elements ?? [],
+      initialData?.appState ?? getDefaultAppState(),
+    );
   }
 
   return {

+ 3 - 3
src/data/localStorage.ts

@@ -62,7 +62,7 @@ export const saveUsernameToLocalStorage = (username: string) => {
   }
 };
 
-export const restoreUsernameFromLocalStorage = (): string | null => {
+export const importUsernameFromLocalStorage = (): string | null => {
   try {
     const data = localStorage.getItem(LOCAL_STORAGE_KEY_COLLAB);
     if (data) {
@@ -95,7 +95,7 @@ export const saveToLocalStorage = (
   }
 };
 
-export const restoreFromLocalStorage = () => {
+export const importFromLocalStorage = () => {
   let savedElements = null;
   let savedState = null;
 
@@ -131,5 +131,5 @@ export const restoreFromLocalStorage = () => {
       // Do nothing because appState is already null
     }
   }
-  return restore(elements, appState);
+  return { elements, appState };
 };

+ 84 - 11
src/index.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useLayoutEffect } from "react";
+import React, { useState, useLayoutEffect, useEffect } from "react";
 import ReactDOM from "react-dom";
 import * as Sentry from "@sentry/browser";
 import * as SentryIntegrations from "@sentry/integrations";
@@ -9,6 +9,19 @@ import Excalidraw from "./excalidraw-embed/index";
 import { register as registerServiceWorker } from "./serviceWorker";
 
 import { loadFromBlob } from "./data";
+import { debounce } from "./utils";
+import {
+  importFromLocalStorage,
+  importUsernameFromLocalStorage,
+  saveUsernameToLocalStorage,
+  saveToLocalStorage,
+} from "./data/localStorage";
+
+import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "./time_constants";
+import { DataState } from "./data/types";
+import { LoadingMessage } from "./components/LoadingMessage";
+import { ExcalidrawElement } from "./element/types";
+import { AppState } from "./types";
 
 // On Apple mobile devices add the proprietary app icon and splashscreen markup.
 // No one should have to do this manually, and eventually this annoyance will
@@ -60,29 +73,89 @@ Sentry.init({
 
 window.__EXCALIDRAW_SHA__ = REACT_APP_GIT_SHA;
 
+const saveDebounced = debounce(
+  (elements: readonly ExcalidrawElement[], state: AppState) => {
+    saveToLocalStorage(elements, state);
+  },
+  SAVE_TO_LOCAL_STORAGE_TIMEOUT,
+);
+
+const onUsernameChange = (username: string) => {
+  saveUsernameToLocalStorage(username);
+};
+
+const onBlur = () => {
+  saveDebounced.flush();
+};
+
 function ExcalidrawApp() {
+  // dimensions
+  // ---------------------------------------------------------------------------
+
   const [dimensions, setDimensions] = useState({
     width: window.innerWidth,
     height: window.innerHeight,
   });
-
-  const onResize = () => {
-    setDimensions({
-      width: window.innerWidth,
-      height: window.innerHeight,
-    });
-  };
-
   useLayoutEffect(() => {
+    const onResize = () => {
+      setDimensions({
+        width: window.innerWidth,
+        height: window.innerHeight,
+      });
+    };
+
     window.addEventListener("resize", onResize);
 
     return () => window.removeEventListener("resize", onResize);
   }, []);
 
-  const { width, height } = dimensions;
+  // initial state
+  // ---------------------------------------------------------------------------
+
+  const [initialState, setInitialState] = useState<{
+    data: DataState;
+    user: {
+      name: string | null;
+    };
+  } | null>(null);
+
+  useEffect(() => {
+    setInitialState({
+      data: importFromLocalStorage(),
+      user: {
+        name: importUsernameFromLocalStorage(),
+      },
+    });
+  }, []);
+
+  // blur/unload
+  // ---------------------------------------------------------------------------
+
+  useEffect(() => {
+    window.addEventListener(EVENT.UNLOAD, onBlur, false);
+    window.addEventListener(EVENT.BLUR, onBlur, false);
+    return () => {
+      window.removeEventListener(EVENT.UNLOAD, onBlur, false);
+      window.removeEventListener(EVENT.BLUR, onBlur, false);
+    };
+  }, []);
+
+  // ---------------------------------------------------------------------------
+
+  if (!initialState) {
+    return <LoadingMessage />;
+  }
+
   return (
     <TopErrorBoundary>
-      <Excalidraw width={width} height={height} />
+      <Excalidraw
+        width={dimensions.width}
+        height={dimensions.height}
+        onChange={saveDebounced}
+        initialData={initialState.data}
+        user={initialState.user}
+        onUsernameChange={onUsernameChange}
+      />
     </TopErrorBoundary>
   );
 }

+ 1 - 0
src/time_constants.ts

@@ -3,3 +3,4 @@ export const TAP_TWICE_TIMEOUT = 300;
 export const INITIAL_SCENE_UPDATE_TIMEOUT = 5000;
 export const SYNC_FULL_SCENE_INTERVAL_MS = 20000;
 export const TOUCH_CTX_MENU_TIMEOUT = 500;
+export const SAVE_TO_LOCAL_STORAGE_TIMEOUT = 300;