Skip to content

Commit a459fbc

Browse files
committed
Enhance ExcalidrawWrapper component: implement broadcast throttling to limit updates, add flags to manage remote updates, and improve snapshot loading error handling with detailed logging.
1 parent d47764f commit a459fbc

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

excalidraw-app/src/components/ExcalidrawWrapper.tsx

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export function ExcalidrawWrapper({ serverConfig, onOpenSettings, onRoomIdChange
3434
const broadcastedElementVersions = useRef<Map<string, number>>(new Map());
3535
const autoSnapshotManager = useRef<AutoSnapshotManager | null>(null);
3636
const [snapshotStorage, setSnapshotStorage] = useState<typeof localStorageAPI | ServerStorage>(localStorageAPI);
37+
const lastBroadcastTime = useRef<number>(0);
38+
const broadcastThrottleMs = 50; // Throttle broadcasts to max 20 per second
39+
const isApplyingRemoteUpdate = useRef<boolean>(false);
3740

3841
useEffect(() => {
3942
const excalidrawAPI = new ExcalidrawAPI(serverConfig);
@@ -179,13 +182,19 @@ export function ExcalidrawWrapper({ serverConfig, onOpenSettings, onRoomIdChange
179182
appState
180183
);
181184

182-
// Update the scene version BEFORE updating scene to prevent re-broadcasting
183-
lastBroadcastedOrReceivedSceneVersion.current = getSceneVersion(reconciledElements);
185+
// Set flag to prevent re-broadcasting this update
186+
isApplyingRemoteUpdate.current = true;
184187

188+
// Update scene
185189
excalidrawRef.current.updateScene({
186190
elements: reconciledElements,
187191
});
188192

193+
// Clear flag after a short delay to allow onChange to process
194+
setTimeout(() => {
195+
isApplyingRemoteUpdate.current = false;
196+
}, 100);
197+
189198
console.log('Scene reconciled and updated');
190199
}
191200
});
@@ -312,12 +321,21 @@ export function ExcalidrawWrapper({ serverConfig, onOpenSettings, onRoomIdChange
312321

313322
// Broadcast changes if collaboration is enabled
314323
if (api?.isEnabled() && api.getCollaborationClient()?.isConnected()) {
324+
// Don't broadcast if we're applying a remote update
325+
if (isApplyingRemoteUpdate.current) {
326+
return;
327+
}
328+
315329
const currentVersion = getSceneVersion(elements);
330+
const now = Date.now();
316331

317-
// Only broadcast if this is a new version (not something we just received)
318-
if (currentVersion > lastBroadcastedOrReceivedSceneVersion.current) {
332+
// Only broadcast if this is a new version
333+
// AND we haven't broadcast too recently (throttle)
334+
if (currentVersion > lastBroadcastedOrReceivedSceneVersion.current &&
335+
(now - lastBroadcastTime.current) >= broadcastThrottleMs) {
319336
broadcastScene(api.getCollaborationClient(), elements, false);
320337
lastBroadcastedOrReceivedSceneVersion.current = currentVersion;
338+
lastBroadcastTime.current = now;
321339
}
322340
}
323341
};
@@ -365,18 +383,35 @@ export function ExcalidrawWrapper({ serverConfig, onOpenSettings, onRoomIdChange
365383
};
366384

367385
const handleLoadSnapshot = (snapshot: Snapshot) => {
368-
if (!excalidrawRef.current || !snapshot.data) return;
386+
if (!excalidrawRef.current) {
387+
alert('Excalidraw not ready');
388+
return;
389+
}
390+
391+
if (!snapshot.data) {
392+
console.error('Snapshot data is missing:', snapshot);
393+
alert('Snapshot data is missing');
394+
return;
395+
}
369396

370397
try {
398+
console.log('Loading snapshot:', snapshot.id);
371399
const sceneData = JSON.parse(snapshot.data);
400+
401+
if (!sceneData.elements || !Array.isArray(sceneData.elements)) {
402+
console.error('Invalid snapshot data structure:', sceneData);
403+
alert('Invalid snapshot data structure');
404+
return;
405+
}
406+
372407
excalidrawRef.current.updateScene({
373408
elements: sceneData.elements,
374-
appState: sceneData.appState,
409+
appState: sceneData.appState || {},
375410
});
376411
console.log('Snapshot loaded successfully');
377412
} catch (error) {
378413
console.error('Failed to load snapshot:', error);
379-
alert('Failed to load snapshot');
414+
alert(`Failed to load snapshot: ${error instanceof Error ? error.message : 'Unknown error'}`);
380415
}
381416
};
382417

excalidraw-app/src/lib/storage.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ export class LocalStorage {
7070
}
7171

7272
async loadSnapshot(id: string): Promise<Snapshot> {
73-
return invoke<Snapshot>('load_snapshot', { id });
73+
console.log(`Loading snapshot ${id} from local storage...`);
74+
const snapshot = await invoke<Snapshot>('load_snapshot', { id });
75+
console.log('Snapshot loaded from local storage:', snapshot);
76+
return snapshot;
7477
}
7578

7679
async deleteSnapshot(id: string): Promise<void> {
@@ -166,13 +169,18 @@ export class ServerStorage {
166169
}
167170

168171
async loadSnapshot(id: string): Promise<Snapshot> {
172+
console.log(`Loading snapshot ${id} from server...`);
169173
const response = await fetch(`${this.serverUrl}/api/snapshots/${id}`);
170174

171175
if (!response.ok) {
172-
throw new Error('Failed to load snapshot from server');
176+
const errorText = await response.text();
177+
console.error(`Server returned ${response.status}:`, errorText);
178+
throw new Error(`Failed to load snapshot from server (${response.status}): ${errorText}`);
173179
}
174180

175-
return response.json();
181+
const snapshot = await response.json();
182+
console.log('Snapshot loaded from server:', snapshot);
183+
return snapshot;
176184
}
177185

178186
async deleteSnapshot(id: string): Promise<void> {

0 commit comments

Comments
 (0)