@@ -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
0 commit comments