From 6a0ad06c119800bdf51c6b50b4807f2ee4ad69cb Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 30 Mar 2026 19:48:45 -0700 Subject: [PATCH] fix: sync collab overlay connection state with runtime WS status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The overlay was blindly setting 'connected' on runtime ready without checking actual WebSocket state, and never subscribed to connect/disconnect events — causing stale "Reconnecting…" badges. Co-Authored-By: Claude Opus 4.6 --- shared/components/rstack-collab-overlay.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/shared/components/rstack-collab-overlay.ts b/shared/components/rstack-collab-overlay.ts index 2b4c9dd..572dae7 100644 --- a/shared/components/rstack-collab-overlay.ts +++ b/shared/components/rstack-collab-overlay.ts @@ -47,6 +47,8 @@ export class RStackCollabOverlay extends HTMLElement { #unsubAwareness: (() => void) | null = null; #unsubPresence: (() => void) | null = null; #unsubLeave: (() => void) | null = null; + #unsubConnect: (() => void) | null = null; + #unsubDisconnect: (() => void) | null = null; #mouseMoveTimer: ReturnType | null = null; #lastCursor = { x: 0, y: 0 }; #gcInterval: ReturnType | null = null; @@ -112,6 +114,8 @@ export class RStackCollabOverlay extends HTMLElement { this.#unsubAwareness?.(); this.#unsubPresence?.(); this.#unsubLeave?.(); + this.#unsubConnect?.(); + this.#unsubDisconnect?.(); this.#stopMouseTracking(); this.#stopFocusTracking(); } @@ -189,7 +193,14 @@ export class RStackCollabOverlay extends HTMLElement { this.#localPeerId = runtime.peerId; // Assign a deterministic color from peer ID this.#localColor = this.#colorForPeer(this.#localPeerId!); - this.#connState = 'connected'; + // Set initial state from actual runtime connection status + this.#connState = runtime.isOnline ? 'connected' : 'offline'; + + // Track ongoing connection state changes + this.#unsubConnect = runtime.onConnect(() => this.setConnState('connected')); + this.#unsubDisconnect = runtime.onDisconnect(() => { + this.setConnState(navigator.onLine ? 'reconnecting' : 'offline'); + }); // Load space members for offline display this.#loadSpaceMembers(); @@ -517,7 +528,7 @@ export class RStackCollabOverlay extends HTMLElement { if (this.#connState === 'offline' || this.#connState === 'reconnecting' || this.#connState === 'connecting') { const msg = this.#connState === 'offline' ? '\u26a0 You\u2019re offline. Changes are saved locally and will resync when you reconnect.' - : 'Reconnecting to server\u2026'; + : this.#connState === 'connecting' ? 'Connecting to server\u2026' : 'Reconnecting to server\u2026'; fragments.push(`
${this.#escHtml(msg)}
`); } @@ -669,12 +680,13 @@ export class RStackCollabOverlay extends HTMLElement { } if (this.#connState === 'reconnecting' || this.#connState === 'connecting') { + const label = this.#connState === 'connecting' ? 'Connecting\u2026' : 'Reconnecting\u2026'; badge.innerHTML = ` - Reconnecting\u2026 + ${label} `; badge.classList.add('visible'); - badge.title = 'Reconnecting to server\u2026'; + badge.title = label; return; }