ソースを参照

feat: move logic from index.tsx into Collab.tsx

Arnošt Pleskot 2 年 前
コミット
e1ff9791f2

+ 1 - 2
src/excalidraw-app/app_constants.ts

@@ -7,13 +7,12 @@ export const SYNC_FULL_SCENE_INTERVAL_MS = 20000;
 export const SYNC_BROWSER_TABS_TIMEOUT = 50;
 export const SYNC_BROWSER_TABS_TIMEOUT = 50;
 export const CURSOR_SYNC_TIMEOUT = 33; // ~30fps
 export const CURSOR_SYNC_TIMEOUT = 33; // ~30fps
 export const DELETED_ELEMENT_TIMEOUT = 24 * 60 * 60 * 1000; // 1 day
 export const DELETED_ELEMENT_TIMEOUT = 24 * 60 * 60 * 1000; // 1 day
+export const PAUSE_COLLABORATION_TIMEOUT = 30000;
 
 
 export const FILE_UPLOAD_MAX_BYTES = 3 * 1024 * 1024; // 3 MiB
 export const FILE_UPLOAD_MAX_BYTES = 3 * 1024 * 1024; // 3 MiB
 // 1 year (https://stackoverflow.com/a/25201898/927631)
 // 1 year (https://stackoverflow.com/a/25201898/927631)
 export const FILE_CACHE_MAX_AGE_SEC = 31536000;
 export const FILE_CACHE_MAX_AGE_SEC = 31536000;
 
 
-export const PAUSE_COLLABORATION_TIMEOUT = 30000;
-
 export const WS_EVENTS = {
 export const WS_EVENTS = {
   SERVER_VOLATILE: "server-volatile-broadcast",
   SERVER_VOLATILE: "server-volatile-broadcast",
   SERVER: "server-broadcast",
   SERVER: "server-broadcast",

+ 54 - 28
src/excalidraw-app/collab/Collab.tsx

@@ -1,6 +1,6 @@
 import throttle from "lodash.throttle";
 import throttle from "lodash.throttle";
 import { PureComponent } from "react";
 import { PureComponent } from "react";
-import { ExcalidrawImperativeAPI } from "../../types";
+import { ExcalidrawImperativeAPI, PauseCollaborationState } from "../../types";
 import { ErrorDialog } from "../../components/ErrorDialog";
 import { ErrorDialog } from "../../components/ErrorDialog";
 import { APP_NAME, ENV, EVENT } from "../../constants";
 import { APP_NAME, ENV, EVENT } from "../../constants";
 import { ImportedDataState } from "../../data/types";
 import { ImportedDataState } from "../../data/types";
@@ -24,6 +24,7 @@ import {
   FIREBASE_STORAGE_PREFIXES,
   FIREBASE_STORAGE_PREFIXES,
   INITIAL_SCENE_UPDATE_TIMEOUT,
   INITIAL_SCENE_UPDATE_TIMEOUT,
   LOAD_IMAGES_TIMEOUT,
   LOAD_IMAGES_TIMEOUT,
+  PAUSE_COLLABORATION_TIMEOUT,
   WS_SCENE_EVENT_TYPES,
   WS_SCENE_EVENT_TYPES,
   SYNC_FULL_SCENE_INTERVAL_MS,
   SYNC_FULL_SCENE_INTERVAL_MS,
 } from "../app_constants";
 } from "../app_constants";
@@ -92,8 +93,6 @@ export interface CollabAPI {
   onPointerUpdate: CollabInstance["onPointerUpdate"];
   onPointerUpdate: CollabInstance["onPointerUpdate"];
   startCollaboration: CollabInstance["startCollaboration"];
   startCollaboration: CollabInstance["startCollaboration"];
   stopCollaboration: CollabInstance["stopCollaboration"];
   stopCollaboration: CollabInstance["stopCollaboration"];
-  pauseCollaboration: CollabInstance["pauseCollaboration"];
-  resumeCollaboration: CollabInstance["resumeCollaboration"];
   syncElements: CollabInstance["syncElements"];
   syncElements: CollabInstance["syncElements"];
   fetchImageFilesFromFirebase: CollabInstance["fetchImageFilesFromFirebase"];
   fetchImageFilesFromFirebase: CollabInstance["fetchImageFilesFromFirebase"];
   setUsername: (username: string) => void;
   setUsername: (username: string) => void;
@@ -112,6 +111,7 @@ class Collab extends PureComponent<Props, CollabState> {
   excalidrawAPI: Props["excalidrawAPI"];
   excalidrawAPI: Props["excalidrawAPI"];
   activeIntervalId: number | null;
   activeIntervalId: number | null;
   idleTimeoutId: number | null;
   idleTimeoutId: number | null;
+  pauseTimeoutId: number | null;
 
 
   private socketInitializationTimer?: number;
   private socketInitializationTimer?: number;
   private lastBroadcastedOrReceivedSceneVersion: number = -1;
   private lastBroadcastedOrReceivedSceneVersion: number = -1;
@@ -153,6 +153,7 @@ class Collab extends PureComponent<Props, CollabState> {
     this.excalidrawAPI = props.excalidrawAPI;
     this.excalidrawAPI = props.excalidrawAPI;
     this.activeIntervalId = null;
     this.activeIntervalId = null;
     this.idleTimeoutId = null;
     this.idleTimeoutId = null;
+    this.pauseTimeoutId = null;
   }
   }
 
 
   componentDidMount() {
   componentDidMount() {
@@ -171,8 +172,6 @@ class Collab extends PureComponent<Props, CollabState> {
       fetchImageFilesFromFirebase: this.fetchImageFilesFromFirebase,
       fetchImageFilesFromFirebase: this.fetchImageFilesFromFirebase,
       stopCollaboration: this.stopCollaboration,
       stopCollaboration: this.stopCollaboration,
       setUsername: this.setUsername,
       setUsername: this.setUsername,
-      pauseCollaboration: this.pauseCollaboration,
-      resumeCollaboration: this.resumeCollaboration,
       isPaused: this.isPaused,
       isPaused: this.isPaused,
     };
     };
 
 
@@ -214,6 +213,10 @@ class Collab extends PureComponent<Props, CollabState> {
       window.clearTimeout(this.idleTimeoutId);
       window.clearTimeout(this.idleTimeoutId);
       this.idleTimeoutId = null;
       this.idleTimeoutId = null;
     }
     }
+    if (this.pauseTimeoutId) {
+      window.clearTimeout(this.pauseTimeoutId);
+      this.pauseTimeoutId = null;
+    }
   }
   }
 
 
   isCollaborating = () => appJotaiStore.get(isCollaboratingAtom)!;
   isCollaborating = () => appJotaiStore.get(isCollaboratingAtom)!;
@@ -317,31 +320,44 @@ class Collab extends PureComponent<Props, CollabState> {
     }
     }
   };
   };
 
 
-  pauseCollaboration = (callback?: () => void) => {
-    if (this.portal.socket) {
-      this.reportIdle();
-      this.portal.socket.disconnect();
-      this.portal.socketInitialized = false;
-      this.setIsCollaborationPaused(true);
-
-      if (callback) {
-        callback();
+  onPauseCollaborationChange = (state: PauseCollaborationState) => {
+    switch (state) {
+      case PauseCollaborationState.PAUSE: {
+        if (this.portal.socket) {
+          this.portal.socket.disconnect();
+          this.portal.socketInitialized = false;
+          this.setIsCollaborationPaused(true);
+
+          this.excalidrawAPI.updateScene({
+            appState: { viewModeEnabled: true },
+          });
+        }
+        break;
       }
       }
-    }
-  };
-
-  resumeCollaboration = (callback?: () => void) => {
-    if (this.portal.socket) {
-      this.reportActive();
-      this.portal.socket.connect();
-      this.portal.socketInitialized = true;
-      this.portal.socket.emit(WS_SCENE_EVENT_TYPES.INIT);
-      this.portal.socket.once("client-broadcast", () => {
-        this.setIsCollaborationPaused(false);
-        if (callback) {
-          callback();
+      case PauseCollaborationState.RESUME: {
+        if (this.portal.socket && this.isPaused()) {
+          this.portal.socket.connect();
+          this.portal.socketInitialized = true;
+          this.portal.socket.emit(WS_SCENE_EVENT_TYPES.INIT);
+
+          this.excalidrawAPI.setToast({
+            message: t("toast.reconnectRoomServer"),
+            duration: Infinity,
+            closable: false,
+          });
         }
         }
-      });
+        break;
+      }
+      case PauseCollaborationState.SYNC: {
+        if (this.isPaused()) {
+          this.setIsCollaborationPaused(false);
+
+          this.excalidrawAPI.updateScene({
+            appState: { viewModeEnabled: false },
+          });
+          this.excalidrawAPI.setToast(null);
+        }
+      }
     }
     }
   };
   };
 
 
@@ -550,6 +566,7 @@ class Collab extends PureComponent<Props, CollabState> {
             this.handleRemoteSceneUpdate(
             this.handleRemoteSceneUpdate(
               this.reconcileElements(decryptedData.payload.elements),
               this.reconcileElements(decryptedData.payload.elements),
             );
             );
+            this.onPauseCollaborationChange(PauseCollaborationState.SYNC);
             break;
             break;
           case "MOUSE_LOCATION": {
           case "MOUSE_LOCATION": {
             const { pointer, button, username, selectedElementIds } =
             const { pointer, button, username, selectedElementIds } =
@@ -737,6 +754,10 @@ class Collab extends PureComponent<Props, CollabState> {
         window.clearInterval(this.activeIntervalId);
         window.clearInterval(this.activeIntervalId);
         this.activeIntervalId = null;
         this.activeIntervalId = null;
       }
       }
+      this.pauseTimeoutId = window.setTimeout(
+        () => this.onPauseCollaborationChange(PauseCollaborationState.PAUSE),
+        PAUSE_COLLABORATION_TIMEOUT,
+      );
       this.onIdleStateChange(UserIdleState.AWAY);
       this.onIdleStateChange(UserIdleState.AWAY);
     } else {
     } else {
       this.idleTimeoutId = window.setTimeout(this.reportIdle, IDLE_THRESHOLD);
       this.idleTimeoutId = window.setTimeout(this.reportIdle, IDLE_THRESHOLD);
@@ -745,6 +766,11 @@ class Collab extends PureComponent<Props, CollabState> {
         ACTIVE_THRESHOLD,
         ACTIVE_THRESHOLD,
       );
       );
       this.onIdleStateChange(UserIdleState.ACTIVE);
       this.onIdleStateChange(UserIdleState.ACTIVE);
+      if (this.pauseTimeoutId) {
+        window.clearTimeout(this.pauseTimeoutId);
+        this.onPauseCollaborationChange(PauseCollaborationState.RESUME);
+        this.pauseTimeoutId = null;
+      }
     }
     }
   };
   };
 
 

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

@@ -46,7 +46,6 @@ import {
 } from "../utils";
 } from "../utils";
 import {
 import {
   FIREBASE_STORAGE_PREFIXES,
   FIREBASE_STORAGE_PREFIXES,
-  PAUSE_COLLABORATION_TIMEOUT,
   STORAGE_KEYS,
   STORAGE_KEYS,
   SYNC_BROWSER_TABS_TIMEOUT,
   SYNC_BROWSER_TABS_TIMEOUT,
 } from "./app_constants";
 } from "./app_constants";
@@ -294,10 +293,6 @@ const ExcalidrawWrapper = () => {
     getInitialLibraryItems: getLibraryItemsFromStorage,
     getInitialLibraryItems: getLibraryItemsFromStorage,
   });
   });
 
 
-  const pauseCollaborationTimeoutRef = useRef<ReturnType<
-    typeof setTimeout
-  > | null>(null);
-
   useEffect(() => {
   useEffect(() => {
     if (!excalidrawAPI || (!isCollabDisabled && !collabAPI)) {
     if (!excalidrawAPI || (!isCollabDisabled && !collabAPI)) {
       return;
       return;
@@ -477,47 +472,6 @@ const ExcalidrawWrapper = () => {
       ) {
       ) {
         syncData();
         syncData();
       }
       }
-
-      if (event.type === EVENT.VISIBILITY_CHANGE) {
-        switch (true) {
-          // user switches to another tab
-          case document.hidden && collabAPI.isCollaborating():
-            if (!pauseCollaborationTimeoutRef.current) {
-              pauseCollaborationTimeoutRef.current = setTimeout(() => {
-                collabAPI.pauseCollaboration(() =>
-                  excalidrawAPI.updateScene({
-                    appState: { viewModeEnabled: true },
-                  }),
-                );
-              }, PAUSE_COLLABORATION_TIMEOUT);
-            }
-            break;
-
-          // user returns to the tab with Excalidraw
-          case !document.hidden && collabAPI.isPaused():
-            excalidrawAPI.setToast({
-              message: t("toast.reconnectRoomServer"),
-              duration: Infinity,
-              closable: true,
-            });
-
-            collabAPI.resumeCollaboration(() => {
-              excalidrawAPI.updateScene({
-                appState: { viewModeEnabled: false },
-              });
-              excalidrawAPI.setToast(null);
-            });
-            break;
-
-          // user returns and timeout hasn't fired yet
-          case !document.hidden && Boolean(pauseCollaborationTimeoutRef):
-            if (pauseCollaborationTimeoutRef.current) {
-              clearTimeout(pauseCollaborationTimeoutRef.current);
-              pauseCollaborationTimeoutRef.current = null;
-            }
-            break;
-        }
-      }
     };
     };
 
 
     window.addEventListener(EVENT.HASHCHANGE, onHashChange, false);
     window.addEventListener(EVENT.HASHCHANGE, onHashChange, false);

+ 6 - 0
src/types.ts

@@ -376,6 +376,12 @@ export enum UserIdleState {
   IDLE = "idle",
   IDLE = "idle",
 }
 }
 
 
+export enum PauseCollaborationState {
+  PAUSE = "pause",
+  RESUME = "resume",
+  SYNC = "sync",
+}
+
 export type ExportOpts = {
 export type ExportOpts = {
   saveFileToDisk?: boolean;
   saveFileToDisk?: boolean;
   onExportToBackend?: (
   onExportToBackend?: (