mirror of
https://github.com/docmost/docmost.git
synced 2026-08-29 18:14:58 +08:00
smart scene sync
This commit is contained in:
@@ -42,6 +42,9 @@ export function useExcalidrawCollab(
|
|||||||
const collaboratorsRef = useRef<Map<string, Collaborator>>(new Map());
|
const collaboratorsRef = useRef<Map<string, Collaborator>>(new Map());
|
||||||
const [isCollaborating, setIsCollaborating] = useState(false);
|
const [isCollaborating, setIsCollaborating] = useState(false);
|
||||||
|
|
||||||
|
// Track broadcasted element versions for bandwidth optimization
|
||||||
|
const broadcastedElementVersions = useRef<Map<string, number>>(new Map());
|
||||||
|
|
||||||
const roomId = pageId ? `excalidraw-${pageId}` : null;
|
const roomId = pageId ? `excalidraw-${pageId}` : null;
|
||||||
const username = currentUser?.user?.name || "Anonymous";
|
const username = currentUser?.user?.name || "Anonymous";
|
||||||
|
|
||||||
@@ -77,32 +80,52 @@ export function useExcalidrawCollab(
|
|||||||
[socket, roomId, username, excalidrawAPI],
|
[socket, roomId, username, excalidrawAPI],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Broadcast scene changes
|
// Broadcast scene changes with bandwidth optimization
|
||||||
const broadcastScene = useMemo(
|
const broadcastScene = useCallback(
|
||||||
() =>
|
(elements: readonly ExcalidrawElement[], syncAll = false) => {
|
||||||
throttle((elements: readonly ExcalidrawElement[]) => {
|
if (!socket || !roomId || !isInitialized.current) {
|
||||||
if (!socket || !roomId || !isInitialized.current) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const sceneVersion = getSceneVersion(elements);
|
const sceneVersion = getSceneVersion(elements);
|
||||||
|
|
||||||
if (sceneVersion <= lastBroadcastedVersion.current) {
|
if (sceneVersion <= lastBroadcastedVersion.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: SceneUpdateMessage = {
|
// Filter to only send elements that changed since last broadcast
|
||||||
type: "SCENE_UPDATE",
|
const changedElements = elements.filter((element) => {
|
||||||
payload: { elements },
|
const lastVersion = broadcastedElementVersions.current.get(element.id);
|
||||||
};
|
return syncAll || lastVersion === undefined || element.version > lastVersion;
|
||||||
|
});
|
||||||
|
|
||||||
const json = JSON.stringify(data);
|
if (changedElements.length === 0) {
|
||||||
socket.emit("server-broadcast", [roomId, json, null]);
|
return;
|
||||||
lastBroadcastedVersion.current = sceneVersion;
|
}
|
||||||
}, 100),
|
|
||||||
|
const data: SceneUpdateMessage = {
|
||||||
|
type: "SCENE_UPDATE",
|
||||||
|
payload: { elements: changedElements },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update tracking map
|
||||||
|
for (const element of changedElements) {
|
||||||
|
broadcastedElementVersions.current.set(element.id, element.version);
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = JSON.stringify(data);
|
||||||
|
socket.emit("server-broadcast", [roomId, json, null]);
|
||||||
|
lastBroadcastedVersion.current = sceneVersion;
|
||||||
|
},
|
||||||
[socket, roomId],
|
[socket, roomId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Throttled version for onChange handler
|
||||||
|
const throttledBroadcastScene = useMemo(
|
||||||
|
() => throttle((elements: readonly ExcalidrawElement[]) => broadcastScene(elements, false), 100),
|
||||||
|
[broadcastScene],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle incoming broadcasts
|
// Handle incoming broadcasts
|
||||||
const handleClientBroadcast = useCallback(
|
const handleClientBroadcast = useCallback(
|
||||||
(jsonData: string, _iv: Uint8Array | null) => {
|
(jsonData: string, _iv: Uint8Array | null) => {
|
||||||
@@ -207,7 +230,8 @@ export function useExcalidrawCollab(
|
|||||||
socket.on("new-user", (socketId: string) => {
|
socket.on("new-user", (socketId: string) => {
|
||||||
console.log("New user joined:", socketId);
|
console.log("New user joined:", socketId);
|
||||||
if (excalidrawAPI) {
|
if (excalidrawAPI) {
|
||||||
broadcastScene(excalidrawAPI.getSceneElements());
|
// Send full scene to new user (syncAll = true)
|
||||||
|
broadcastScene(excalidrawAPI.getSceneElements(), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,6 +244,7 @@ export function useExcalidrawCollab(
|
|||||||
socket.off("new-user");
|
socket.off("new-user");
|
||||||
isInitialized.current = false;
|
isInitialized.current = false;
|
||||||
lastBroadcastedVersion.current = -1;
|
lastBroadcastedVersion.current = -1;
|
||||||
|
broadcastedElementVersions.current = new Map();
|
||||||
collaboratorsRef.current = new Map();
|
collaboratorsRef.current = new Map();
|
||||||
setIsCollaborating(false);
|
setIsCollaborating(false);
|
||||||
};
|
};
|
||||||
@@ -234,7 +259,7 @@ export function useExcalidrawCollab(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
broadcastScene,
|
broadcastScene: throttledBroadcastScene,
|
||||||
broadcastPointer,
|
broadcastPointer,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user