|
@@ -22,7 +22,7 @@ import {
|
|
FIREBASE_STORAGE_PREFIXES,
|
|
FIREBASE_STORAGE_PREFIXES,
|
|
INITIAL_SCENE_UPDATE_TIMEOUT,
|
|
INITIAL_SCENE_UPDATE_TIMEOUT,
|
|
LOAD_IMAGES_TIMEOUT,
|
|
LOAD_IMAGES_TIMEOUT,
|
|
- SCENE,
|
|
|
|
|
|
+ WS_SCENE_EVENT_TYPES,
|
|
STORAGE_KEYS,
|
|
STORAGE_KEYS,
|
|
SYNC_FULL_SCENE_INTERVAL_MS,
|
|
SYNC_FULL_SCENE_INTERVAL_MS,
|
|
} from "../app_constants";
|
|
} from "../app_constants";
|
|
@@ -88,7 +88,7 @@ export interface CollabAPI {
|
|
onPointerUpdate: CollabInstance["onPointerUpdate"];
|
|
onPointerUpdate: CollabInstance["onPointerUpdate"];
|
|
initializeSocketClient: CollabInstance["initializeSocketClient"];
|
|
initializeSocketClient: CollabInstance["initializeSocketClient"];
|
|
onCollabButtonClick: CollabInstance["onCollabButtonClick"];
|
|
onCollabButtonClick: CollabInstance["onCollabButtonClick"];
|
|
- broadcastElements: CollabInstance["broadcastElements"];
|
|
|
|
|
|
+ syncElements: CollabInstance["syncElements"];
|
|
fetchImageFilesFromFirebase: CollabInstance["fetchImageFilesFromFirebase"];
|
|
fetchImageFilesFromFirebase: CollabInstance["fetchImageFilesFromFirebase"];
|
|
setUsername: (username: string) => void;
|
|
setUsername: (username: string) => void;
|
|
}
|
|
}
|
|
@@ -232,12 +232,20 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
});
|
|
});
|
|
|
|
|
|
saveCollabRoomToFirebase = async (
|
|
saveCollabRoomToFirebase = async (
|
|
- syncableElements: readonly ExcalidrawElement[] = this.getSyncableElements(
|
|
|
|
- this.excalidrawAPI.getSceneElementsIncludingDeleted(),
|
|
|
|
- ),
|
|
|
|
|
|
+ syncableElements: readonly ExcalidrawElement[],
|
|
) => {
|
|
) => {
|
|
try {
|
|
try {
|
|
- await saveToFirebase(this.portal, syncableElements);
|
|
|
|
|
|
+ const savedData = await saveToFirebase(
|
|
|
|
+ this.portal,
|
|
|
|
+ syncableElements,
|
|
|
|
+ this.excalidrawAPI.getAppState(),
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if (this.isCollaborating() && savedData && savedData.reconciledElements) {
|
|
|
|
+ this.handleRemoteSceneUpdate(
|
|
|
|
+ this.reconcileElements(savedData.reconciledElements),
|
|
|
|
+ );
|
|
|
|
+ }
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
console.error(error);
|
|
console.error(error);
|
|
}
|
|
}
|
|
@@ -250,9 +258,14 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
|
|
|
|
closePortal = () => {
|
|
closePortal = () => {
|
|
this.queueBroadcastAllElements.cancel();
|
|
this.queueBroadcastAllElements.cancel();
|
|
|
|
+ this.queueSaveToFirebase.cancel();
|
|
this.loadImageFiles.cancel();
|
|
this.loadImageFiles.cancel();
|
|
|
|
|
|
- this.saveCollabRoomToFirebase();
|
|
|
|
|
|
+ this.saveCollabRoomToFirebase(
|
|
|
|
+ this.getSyncableElements(
|
|
|
|
+ this.excalidrawAPI.getSceneElementsIncludingDeleted(),
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
if (window.confirm(t("alerts.collabStopOverridePrompt"))) {
|
|
if (window.confirm(t("alerts.collabStopOverridePrompt"))) {
|
|
// hack to ensure that we prefer we disregard any new browser state
|
|
// hack to ensure that we prefer we disregard any new browser state
|
|
// that could have been saved in other tabs while we were collaborating
|
|
// that could have been saved in other tabs while we were collaborating
|
|
@@ -400,10 +413,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
commitToHistory: true,
|
|
commitToHistory: true,
|
|
});
|
|
});
|
|
|
|
|
|
- this.broadcastElements(elements);
|
|
|
|
-
|
|
|
|
- const syncableElements = this.getSyncableElements(elements);
|
|
|
|
- this.saveCollabRoomToFirebase(syncableElements);
|
|
|
|
|
|
+ this.saveCollabRoomToFirebase(this.getSyncableElements(elements));
|
|
}
|
|
}
|
|
|
|
|
|
// fallback in case you're not alone in the room but still don't receive
|
|
// fallback in case you're not alone in the room but still don't receive
|
|
@@ -433,7 +443,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
switch (decryptedData.type) {
|
|
switch (decryptedData.type) {
|
|
case "INVALID_RESPONSE":
|
|
case "INVALID_RESPONSE":
|
|
return;
|
|
return;
|
|
- case SCENE.INIT: {
|
|
|
|
|
|
+ case WS_SCENE_EVENT_TYPES.INIT: {
|
|
if (!this.portal.socketInitialized) {
|
|
if (!this.portal.socketInitialized) {
|
|
this.initializeRoom({ fetchScene: false });
|
|
this.initializeRoom({ fetchScene: false });
|
|
const remoteElements = decryptedData.payload.elements;
|
|
const remoteElements = decryptedData.payload.elements;
|
|
@@ -449,7 +459,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- case SCENE.UPDATE:
|
|
|
|
|
|
+ case WS_SCENE_EVENT_TYPES.UPDATE:
|
|
this.handleRemoteSceneUpdate(
|
|
this.handleRemoteSceneUpdate(
|
|
this.reconcileElements(decryptedData.payload.elements),
|
|
this.reconcileElements(decryptedData.payload.elements),
|
|
);
|
|
);
|
|
@@ -711,15 +721,20 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
getSceneVersion(elements) >
|
|
getSceneVersion(elements) >
|
|
this.getLastBroadcastedOrReceivedSceneVersion()
|
|
this.getLastBroadcastedOrReceivedSceneVersion()
|
|
) {
|
|
) {
|
|
- this.portal.broadcastScene(SCENE.UPDATE, elements, false);
|
|
|
|
|
|
+ this.portal.broadcastScene(WS_SCENE_EVENT_TYPES.UPDATE, elements, false);
|
|
this.lastBroadcastedOrReceivedSceneVersion = getSceneVersion(elements);
|
|
this.lastBroadcastedOrReceivedSceneVersion = getSceneVersion(elements);
|
|
this.queueBroadcastAllElements();
|
|
this.queueBroadcastAllElements();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ syncElements = (elements: readonly ExcalidrawElement[]) => {
|
|
|
|
+ this.broadcastElements(elements);
|
|
|
|
+ this.queueSaveToFirebase();
|
|
|
|
+ };
|
|
|
|
+
|
|
queueBroadcastAllElements = throttle(() => {
|
|
queueBroadcastAllElements = throttle(() => {
|
|
this.portal.broadcastScene(
|
|
this.portal.broadcastScene(
|
|
- SCENE.UPDATE,
|
|
|
|
|
|
+ WS_SCENE_EVENT_TYPES.UPDATE,
|
|
this.excalidrawAPI.getSceneElementsIncludingDeleted(),
|
|
this.excalidrawAPI.getSceneElementsIncludingDeleted(),
|
|
true,
|
|
true,
|
|
);
|
|
);
|
|
@@ -731,6 +746,16 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
this.setLastBroadcastedOrReceivedSceneVersion(newVersion);
|
|
this.setLastBroadcastedOrReceivedSceneVersion(newVersion);
|
|
}, SYNC_FULL_SCENE_INTERVAL_MS);
|
|
}, SYNC_FULL_SCENE_INTERVAL_MS);
|
|
|
|
|
|
|
|
+ queueSaveToFirebase = throttle(() => {
|
|
|
|
+ if (this.portal.socketInitialized) {
|
|
|
|
+ this.saveCollabRoomToFirebase(
|
|
|
|
+ this.getSyncableElements(
|
|
|
|
+ this.excalidrawAPI.getSceneElementsIncludingDeleted(),
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }, SYNC_FULL_SCENE_INTERVAL_MS);
|
|
|
|
+
|
|
handleClose = () => {
|
|
handleClose = () => {
|
|
this.setState({ modalIsShown: false });
|
|
this.setState({ modalIsShown: false });
|
|
};
|
|
};
|
|
@@ -771,7 +796,7 @@ class CollabWrapper extends PureComponent<Props, CollabState> {
|
|
this.contextValue.onPointerUpdate = this.onPointerUpdate;
|
|
this.contextValue.onPointerUpdate = this.onPointerUpdate;
|
|
this.contextValue.initializeSocketClient = this.initializeSocketClient;
|
|
this.contextValue.initializeSocketClient = this.initializeSocketClient;
|
|
this.contextValue.onCollabButtonClick = this.onCollabButtonClick;
|
|
this.contextValue.onCollabButtonClick = this.onCollabButtonClick;
|
|
- this.contextValue.broadcastElements = this.broadcastElements;
|
|
|
|
|
|
+ this.contextValue.syncElements = this.syncElements;
|
|
this.contextValue.fetchImageFilesFromFirebase =
|
|
this.contextValue.fetchImageFilesFromFirebase =
|
|
this.fetchImageFilesFromFirebase;
|
|
this.fetchImageFilesFromFirebase;
|
|
this.contextValue.setUsername = this.setUsername;
|
|
this.contextValue.setUsername = this.setUsername;
|