Browse Source

expose resetScene and getSceneElementsIncludingDeleted && move excalidrawApp to excalidraw-app folder (#2272)

Co-authored-by: dwelle <luzar.david@gmail.com>
Aakansha Doshi 4 năm trước cách đây
mục cha
commit
e916d7f6f6
6 tập tin đã thay đổi với 191 bổ sung182 xóa
  1. 6 0
      src/components/App.tsx
  2. 108 0
      src/excalidraw-app/index.tsx
  3. 31 0
      src/excalidraw-app/pwa.ts
  4. 39 0
      src/excalidraw-app/sentry.ts
  5. 1 1
      src/global.d.ts
  6. 6 181
      src/index.tsx

+ 6 - 0
src/components/App.tsx

@@ -271,6 +271,10 @@ export type PointerDownState = Readonly<{
 export type ExcalidrawImperativeAPI =
   | {
       updateScene: InstanceType<typeof App>["updateScene"];
+      resetScene: InstanceType<typeof App>["resetScene"];
+      getSceneElementsIncludingDeleted: InstanceType<
+        typeof App
+      >["getSceneElementsIncludingDeleted"];
     }
   | undefined;
 
@@ -306,6 +310,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
     if (forwardedRef && "current" in forwardedRef) {
       forwardedRef.current = {
         updateScene: this.updateScene,
+        resetScene: this.resetScene,
+        getSceneElementsIncludingDeleted: this.getSceneElementsIncludingDeleted,
       };
     }
     this.scene = new Scene();

+ 108 - 0
src/excalidraw-app/index.tsx

@@ -0,0 +1,108 @@
+import React, { useState, useLayoutEffect, useEffect } from "react";
+
+import { LoadingMessage } from "../components/LoadingMessage";
+import { TopErrorBoundary } from "../components/TopErrorBoundary";
+import Excalidraw from "../excalidraw-embed/index";
+
+import {
+  importFromLocalStorage,
+  importUsernameFromLocalStorage,
+  saveToLocalStorage,
+  saveUsernameToLocalStorage,
+} from "../data/localStorage";
+
+import { debounce } from "../utils";
+
+import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "../time_constants";
+import { EVENT } from "../constants";
+
+import { ImportedDataState } from "../data/types";
+import { ExcalidrawElement } from "../element/types";
+import { AppState } from "../types";
+
+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();
+};
+
+export default function ExcalidrawApp() {
+  // dimensions
+  // ---------------------------------------------------------------------------
+
+  const [dimensions, setDimensions] = useState({
+    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);
+  }, []);
+
+  // initial state
+  // ---------------------------------------------------------------------------
+
+  const [initialState, setInitialState] = useState<{
+    data: ImportedDataState;
+    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={dimensions.width}
+        height={dimensions.height}
+        onChange={saveDebounced}
+        initialData={initialState.data}
+        user={initialState.user}
+        onUsernameChange={onUsernameChange}
+      />
+    </TopErrorBoundary>
+  );
+}

+ 31 - 0
src/excalidraw-app/pwa.ts

@@ -0,0 +1,31 @@
+import { register as registerServiceWorker } from "../serviceWorker";
+import { EVENT } from "../constants";
+
+// 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
+// go away once https://bugs.webkit.org/show_bug.cgi?id=183937 is fixed.
+if (
+  /\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent) &&
+  !matchMedia("(display-mode: standalone)").matches
+) {
+  import(/* webpackChunkName: "pwacompat" */ "pwacompat");
+}
+
+registerServiceWorker({
+  onUpdate: (registration) => {
+    const waitingServiceWorker = registration.waiting;
+    if (waitingServiceWorker) {
+      waitingServiceWorker.addEventListener(
+        EVENT.STATE_CHANGE,
+        (event: Event) => {
+          const target = event.target as ServiceWorker;
+          const state = target.state as ServiceWorkerState;
+          if (state === "activated") {
+            window.location.reload();
+          }
+        },
+      );
+      waitingServiceWorker.postMessage({ type: "SKIP_WAITING" });
+    }
+  },
+});

+ 39 - 0
src/excalidraw-app/sentry.ts

@@ -0,0 +1,39 @@
+import * as Sentry from "@sentry/browser";
+import * as SentryIntegrations from "@sentry/integrations";
+
+const SentryEnvHostnameMap: { [key: string]: string } = {
+  "excalidraw.com": "production",
+  "vercel.app": "staging",
+};
+
+const REACT_APP_DISABLE_SENTRY =
+  process.env.REACT_APP_DISABLE_SENTRY === "true";
+
+// Disable Sentry locally or inside the Docker to avoid noise/respect privacy
+const onlineEnv =
+  !REACT_APP_DISABLE_SENTRY &&
+  Object.keys(SentryEnvHostnameMap).find(
+    (item) => window.location.hostname.indexOf(item) >= 0,
+  );
+
+Sentry.init({
+  dsn: onlineEnv
+    ? "https://7bfc596a5bf945eda6b660d3015a5460@sentry.io/5179260"
+    : undefined,
+  environment: onlineEnv ? SentryEnvHostnameMap[onlineEnv] : undefined,
+  release: process.env.REACT_APP_GIT_SHA,
+  ignoreErrors: [
+    "undefined is not an object (evaluating 'window.__pad.performLoop')", // Only happens on Safari, but spams our servers. Doesn't break anything
+  ],
+  integrations: [
+    new SentryIntegrations.CaptureConsole({
+      levels: ["error"],
+    }),
+  ],
+  beforeSend(event) {
+    if (event.request?.url) {
+      event.request.url = event.request.url.replace(/#.*$/, "");
+    }
+    return event;
+  },
+});

+ 1 - 1
src/global.d.ts

@@ -10,7 +10,7 @@ interface Document {
 
 interface Window {
   ClipboardItem: any;
-  __EXCALIDRAW_SHA__: string;
+  __EXCALIDRAW_SHA__: string | undefined;
 }
 
 // https://github.com/facebook/create-react-app/blob/ddcb7d5/packages/react-scripts/lib/react-app.d.ts

+ 6 - 181
src/index.tsx

@@ -1,185 +1,10 @@
-import React, { useState, useLayoutEffect, useEffect } from "react";
+import React from "react";
 import ReactDOM from "react-dom";
-import * as Sentry from "@sentry/browser";
-import * as SentryIntegrations from "@sentry/integrations";
+import ExcalidrawApp from "./excalidraw-app";
 
-import { EVENT } from "./constants";
-import { TopErrorBoundary } from "./components/TopErrorBoundary";
-import Excalidraw from "./excalidraw-embed/index";
-import { register as registerServiceWorker } from "./serviceWorker";
+import "./excalidraw-app/pwa";
+import "./excalidraw-app/sentry";
 
-import { debounce } from "./utils";
-import {
-  importFromLocalStorage,
-  importUsernameFromLocalStorage,
-  saveUsernameToLocalStorage,
-  saveToLocalStorage,
-} from "./data/localStorage";
+window.__EXCALIDRAW_SHA__ = process.env.REACT_APP_GIT_SHA;
 
-import { SAVE_TO_LOCAL_STORAGE_TIMEOUT } from "./time_constants";
-import { ImportedDataState } 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
-// go away once https://bugs.webkit.org/show_bug.cgi?id=183937 is fixed.
-if (
-  /\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent) &&
-  !matchMedia("(display-mode: standalone)").matches
-) {
-  import(/* webpackChunkName: "pwacompat" */ "pwacompat");
-}
-
-const SentryEnvHostnameMap: { [key: string]: string } = {
-  "excalidraw.com": "production",
-  "vercel.app": "staging",
-};
-
-const REACT_APP_DISABLE_SENTRY =
-  process.env.REACT_APP_DISABLE_SENTRY === "true";
-const REACT_APP_GIT_SHA = process.env.REACT_APP_GIT_SHA as string;
-
-// Disable Sentry locally or inside the Docker to avoid noise/respect privacy
-const onlineEnv =
-  !REACT_APP_DISABLE_SENTRY &&
-  Object.keys(SentryEnvHostnameMap).find(
-    (item) => window.location.hostname.indexOf(item) >= 0,
-  );
-
-Sentry.init({
-  dsn: onlineEnv
-    ? "https://7bfc596a5bf945eda6b660d3015a5460@sentry.io/5179260"
-    : undefined,
-  environment: onlineEnv ? SentryEnvHostnameMap[onlineEnv] : undefined,
-  release: REACT_APP_GIT_SHA,
-  ignoreErrors: [
-    "undefined is not an object (evaluating 'window.__pad.performLoop')", // Only happens on Safari, but spams our servers. Doesn't break anything
-  ],
-  integrations: [
-    new SentryIntegrations.CaptureConsole({
-      levels: ["error"],
-    }),
-  ],
-  beforeSend(event) {
-    if (event.request?.url) {
-      event.request.url = event.request.url.replace(/#.*$/, "");
-    }
-    return event;
-  },
-});
-
-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,
-  });
-  useLayoutEffect(() => {
-    const onResize = () => {
-      setDimensions({
-        width: window.innerWidth,
-        height: window.innerHeight,
-      });
-    };
-
-    window.addEventListener("resize", onResize);
-
-    return () => window.removeEventListener("resize", onResize);
-  }, []);
-
-  // initial state
-  // ---------------------------------------------------------------------------
-
-  const [initialState, setInitialState] = useState<{
-    data: ImportedDataState;
-    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={dimensions.width}
-        height={dimensions.height}
-        offsetLeft={0}
-        offsetTop={0}
-        onChange={saveDebounced}
-        initialData={initialState.data}
-        user={initialState.user}
-        onUsernameChange={onUsernameChange}
-      />
-    </TopErrorBoundary>
-  );
-}
-
-const rootElement = document.getElementById("root");
-
-ReactDOM.render(<ExcalidrawApp />, rootElement);
-
-registerServiceWorker({
-  onUpdate: (registration) => {
-    const waitingServiceWorker = registration.waiting;
-    if (waitingServiceWorker) {
-      waitingServiceWorker.addEventListener(
-        EVENT.STATE_CHANGE,
-        (event: Event) => {
-          const target = event.target as ServiceWorker;
-          const state = target.state as ServiceWorkerState;
-          if (state === "activated") {
-            window.location.reload();
-          }
-        },
-      );
-      waitingServiceWorker.postMessage({ type: "SKIP_WAITING" });
-    }
-  },
-});
+ReactDOM.render(<ExcalidrawApp />, document.getElementById("root"));