diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-10-17 18:37:35 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-10-17 18:37:35 +0200 |
| commit | 8125df6d8149368ed8a84c9db6c5e37bfccb5a15 (patch) | |
| tree | 6eba4f399c7a9b6e785f4d28bc197fb31923d0a3 /packages/meshbay-hub/src/meshbay_hub/static/transport.js | |
| parent | d5ba9552e27bac71168178f73a4c977a4d36dc1c (diff) | |
| download | meshbay-8125df6d8149368ed8a84c9db6c5e37bfccb5a15.tar.gz | |
fix(spa): a reconnect refused the local network says so
Each failed attempt reaches the group page, which shows the same message
and buttons as at opening while the reconnect goes on. Try now gives up
an attempt still waiting on ICE and starts the next at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/transport.js | 43 |
1 files changed, 42 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js index 0b796cab..11088adc 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js @@ -747,6 +747,9 @@ class MeshBayTransport { // and a single setter meant the second to arrive silently replaced // the first, then cleared it on the way out. this._reconnectListeners = new Set(); + // Told of each attempt that failed, with its error: a reconnect refused + // the local network tries again for ever and says nothing otherwise. + this._reconnectFailureListeners = new Set(); this._onNeedToken = null; // Which device key THIS connection has identified itself to the node with. // Empty means "not identified": nothing can be sealed, so nothing can be @@ -762,6 +765,9 @@ class MeshBayTransport { // connect() like the diagnostic listener above it — this one has to // survive every reconnect attempt, not restart with each one. this._reconnectWakeResolve = null; + // Set by retryNow for the attempt it gave up: the next waits for nothing. + this._retryAtOnce = false; + this._abandonAttempt = null; this._onVisibilityWake = () => { if (document.visibilityState === 'visible') this._wakeReconnect(); }; @@ -823,6 +829,21 @@ class MeshBayTransport { } } + /** + * Tries again now: the person has just changed what made it fail. An + * attempt under way set up its connection before that change (a permission + * granted), and Chrome keeps refusing the addresses it dropped then until + * ICE gives up, half a minute: it is given up here, and the next starts at + * once. + */ + retryNow() { + if (this._inReconnectAttempt && this._abandonAttempt) { + this._retryAtOnce = true; + this._abandonAttempt(); + } + this._wakeReconnect(); + } + get connected() { return this._connected; } /** True while _reconnectLoop is running, backoff included. */ get reconnecting() { return !!this._reconnectPromise; } @@ -864,6 +885,17 @@ class MeshBayTransport { } /** + * Told of each automatic reconnect attempt that failed, with its error, while + * the reconnect goes on. A phone that leaves mobile data for the node's Wi-Fi + * without the local network permission is refused on every attempt + * (`reason` local_network_blocked), and only the page can say why. + */ + addReconnectFailureListener(fn) { + this._reconnectFailureListeners.add(fn); + return () => this._reconnectFailureListeners.delete(fn); + } + + /** * Told whenever this connection's device identity changes — including to * *nothing*, which is the case that mattered. * @@ -1007,6 +1039,8 @@ class MeshBayTransport { // DataChannel closed" on the console. Every failed reconnect attempt // printed one, which is noise in exactly the log a freeze gets read from. channelReady.catch(() => { /* the awaiter below reports it */ }); + // retryNow; a no-op once the channel is open. + this._abandonAttempt = () => channelReject(new Error('Tried again at once')); this._channel.onmessage = (event) => this._onMessage(event.data); const channel = this._channel; @@ -1698,7 +1732,9 @@ class MeshBayTransport { this._reconnectAttempts = 0; while (!this._closed) { this._reconnectAttempts += 1; - const delayMs = Math.min(30000, 1000 * 2 ** (this._reconnectAttempts - 1)); + const delayMs = this._retryAtOnce ? 0 + : Math.min(30000, 1000 * 2 ** (this._reconnectAttempts - 1)); + this._retryAtOnce = false; trace('reconnect_wait', { attempt: this._reconnectAttempts, delay_ms: delayMs }); // Interruptible: _wakeReconnect (fired on visibilitychange → visible) // resolves this immediately instead of waiting out the rest of a @@ -1752,6 +1788,11 @@ class MeshBayTransport { }); console.warn('[MeshBay] Reconnect attempt', this._reconnectAttempts, 'failed:', e.message); + for (const fn of [...this._reconnectFailureListeners]) { + try { fn(e); } catch (err) { + console.error('[MeshBay] reconnect failure listener threw:', err); + } + } // Loop again with a longer backoff — closing over `args`/`token` // freshly next time, in case the token was the actual problem. } |