@@ -240,9 +240,24 @@ function processDocUpdate(
240240}
241241
242242let isPolling = false ;
243+ let isUnloadPending = false ;
243244let pollInterval = POLLING_INTERVAL_IN_MS ;
244245let pageHideListenerRegistered = false ;
245246
247+ /**
248+ * Mark that a page unload has been requested. This fires on
249+ * `beforeunload` which happens before the browser aborts in-flight
250+ * fetches, allowing us to distinguish poll failures caused by
251+ * navigation from genuine server errors in the catch block.
252+ *
253+ * If the user cancels the unload (e.g. by dismissing a "Save Changes?" dialog),
254+ * the flag is reset at the start of the next poll cycle so that polling can
255+ * resume.
256+ */
257+ function handleBeforeUnload ( ) : void {
258+ isUnloadPending = true ;
259+ }
260+
246261/**
247262 * Send a disconnect signal for all registered rooms when the page is
248263 * being unloaded. Uses `sendBeacon` so the request survives navigation.
@@ -270,6 +285,11 @@ function poll(): void {
270285 return ;
271286 }
272287
288+ // Reset the unloading flag at the start of each poll cycle so
289+ // it doesn't permanently suppress disconnect after the user
290+ // cancels a beforeunload dialog.
291+ isUnloadPending = false ;
292+
273293 // Emit 'connecting' status.
274294 roomStates . forEach ( ( state ) => {
275295 state . onStatusChange ( { status : 'connecting' } ) ;
@@ -371,9 +391,14 @@ function poll(): void {
371391 ) ;
372392 }
373393
374- roomStates . forEach ( ( state ) => {
375- state . onStatusChange ( { status : 'disconnected' } ) ;
376- } ) ;
394+ // Don't report disconnected status when the request was aborted
395+ // due to page unload (e.g. during a refresh) to avoid briefly
396+ // flashing the disconnect dialog before the new page loads.
397+ if ( ! isUnloadPending ) {
398+ roomStates . forEach ( ( state ) => {
399+ state . onStatusChange ( { status : 'disconnected' } ) ;
400+ } ) ;
401+ }
377402 }
378403
379404 setTimeout ( poll , pollInterval ) ;
@@ -441,6 +466,7 @@ function registerRoom( {
441466 roomStates . set ( room , roomState ) ;
442467
443468 if ( ! pageHideListenerRegistered ) {
469+ window . addEventListener ( 'beforeunload' , handleBeforeUnload ) ;
444470 window . addEventListener ( 'pagehide' , handlePageHide ) ;
445471 pageHideListenerRegistered = true ;
446472 }
@@ -471,6 +497,7 @@ function unregisterRoom( room: string ): void {
471497 }
472498
473499 if ( roomStates . size === 0 && pageHideListenerRegistered ) {
500+ window . removeEventListener ( 'beforeunload' , handleBeforeUnload ) ;
474501 window . removeEventListener ( 'pagehide' , handlePageHide ) ;
475502 pageHideListenerRegistered = false ;
476503 }
0 commit comments