feat: move logic from index.tsx into Collab.tsx
This commit is contained in:
parent
aa91af8f7d
commit
e1ff9791f2
@ -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",
|
||||||
|
@ -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,32 +320,45 @@ class Collab extends PureComponent<Props, CollabState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pauseCollaboration = (callback?: () => void) => {
|
onPauseCollaborationChange = (state: PauseCollaborationState) => {
|
||||||
|
switch (state) {
|
||||||
|
case PauseCollaborationState.PAUSE: {
|
||||||
if (this.portal.socket) {
|
if (this.portal.socket) {
|
||||||
this.reportIdle();
|
|
||||||
this.portal.socket.disconnect();
|
this.portal.socket.disconnect();
|
||||||
this.portal.socketInitialized = false;
|
this.portal.socketInitialized = false;
|
||||||
this.setIsCollaborationPaused(true);
|
this.setIsCollaborationPaused(true);
|
||||||
|
|
||||||
if (callback) {
|
this.excalidrawAPI.updateScene({
|
||||||
callback();
|
appState: { viewModeEnabled: true },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
case PauseCollaborationState.RESUME: {
|
||||||
|
if (this.portal.socket && this.isPaused()) {
|
||||||
resumeCollaboration = (callback?: () => void) => {
|
|
||||||
if (this.portal.socket) {
|
|
||||||
this.reportActive();
|
|
||||||
this.portal.socket.connect();
|
this.portal.socket.connect();
|
||||||
this.portal.socketInitialized = true;
|
this.portal.socketInitialized = true;
|
||||||
this.portal.socket.emit(WS_SCENE_EVENT_TYPES.INIT);
|
this.portal.socket.emit(WS_SCENE_EVENT_TYPES.INIT);
|
||||||
this.portal.socket.once("client-broadcast", () => {
|
|
||||||
this.setIsCollaborationPaused(false);
|
this.excalidrawAPI.setToast({
|
||||||
if (callback) {
|
message: t("toast.reconnectRoomServer"),
|
||||||
callback();
|
duration: Infinity,
|
||||||
}
|
closable: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PauseCollaborationState.SYNC: {
|
||||||
|
if (this.isPaused()) {
|
||||||
|
this.setIsCollaborationPaused(false);
|
||||||
|
|
||||||
|
this.excalidrawAPI.updateScene({
|
||||||
|
appState: { viewModeEnabled: false },
|
||||||
|
});
|
||||||
|
this.excalidrawAPI.setToast(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
isPaused = () => appJotaiStore.get(isCollaborationPausedAtom)!;
|
isPaused = () => appJotaiStore.get(isCollaborationPausedAtom)!;
|
||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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?: (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user