chore(offline): TEMPORARY network diagnostic panel (remove before release)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m44s

A fixed strip in the lobby showing live navigator.onLine / offline.active /
offline.auto / connection.online plus a timestamped event log (offline/online
events, poll ticks, checkReachable results, mode changes) - to pinpoint where the
auto-offline -> online transition breaks on the contour. A 1s heartbeat logs
navigator.onLine flips even if the events never fire. Skipped in the mock; to be
reverted with the fix.
This commit is contained in:
Ilia Denisov
2026-07-06 19:03:33 +02:00
parent fffc6030ce
commit 09c5a5e72e
3 changed files with 61 additions and 3 deletions
+13 -3
View File
@@ -57,6 +57,7 @@ import {
import type { CoachSeries } from './coachmark';
import { connection, reportOffline, reportOnline, resetConnection, checkReachable } from './connection.svelte';
import { offlineMode, setOfflineMode } from './offline.svelte';
import { netlog } from './netdiag.svelte'; // TEMP diagnostic
import { shouldBootOffline } from './offline';
import { isConnectionCode } from './retry';
import { clearGameCache, getCachedGame, setCachedGame } from './gamecache';
@@ -603,9 +604,15 @@ export function scheduleRecovery(delayMs: number): void {
recoveryTimer = null;
if (!offlineMode.active || !offlineMode.auto) return;
const interfaceUp = typeof navigator === 'undefined' || navigator.onLine;
if (interfaceUp && (await checkReachable(BOOT_REACHABILITY_TIMEOUT_MS))) {
if (offlineMode.active && offlineMode.auto) setOfflineMode(false);
return;
netlog(`poll tick: navOnline=${interfaceUp}`); // TEMP diagnostic
if (interfaceUp) {
const reachable = await checkReachable(BOOT_REACHABILITY_TIMEOUT_MS);
netlog(`checkReachable=${reachable}`); // TEMP diagnostic
if (reachable && offlineMode.active && offlineMode.auto) {
setOfflineMode(false);
netlog('-> RETURNED ONLINE'); // TEMP diagnostic
return;
}
}
scheduleRecovery(4000);
}, delayMs);
@@ -614,14 +621,17 @@ export function scheduleRecovery(delayMs: number): void {
export function initNetworkReactivity(): void {
if (typeof window === 'undefined' || import.meta.env.MODE === 'mock') return;
window.addEventListener('offline', () => {
netlog(`offline EVENT (active=${offlineMode.active})`); // TEMP diagnostic
if (!offlineMode.active) {
setOfflineMode(true, false); // auto (session)
netlog('-> auto-offline; poll started'); // TEMP diagnostic
scheduleRecovery(4000); // poll for the network to return
}
});
// The online event, when it fires, just kicks an immediate reachability check; the poll above is
// the reliable fallback for platforms where it does not fire.
window.addEventListener('online', () => {
netlog(`online EVENT (active=${offlineMode.active} auto=${offlineMode.auto})`); // TEMP diagnostic
if (offlineMode.active && offlineMode.auto) scheduleRecovery(0);
});
}
+38
View File
@@ -0,0 +1,38 @@
// TEMPORARY network diagnostic — REMOVE before release. A live view of the online/offline signals so
// we can pinpoint where the auto-offline -> online transition breaks. Rendered by a panel in
// Lobby.svelte. Skipped in the mock build.
let entries = $state<string[]>([]);
let navOnline = $state(typeof navigator !== 'undefined' ? navigator.onLine : true);
export const netdiag = {
/** entries is the newest-first ring buffer of timestamped diagnostic lines. */
get entries(): string[] {
return entries;
},
/** navOnline is the live navigator.onLine value (the property itself is not reactive). */
get navOnline(): boolean {
return navOnline;
},
};
/** netlog appends a timestamped diagnostic line (kept to the last 24). */
export function netlog(msg: string): void {
const t = new Date().toLocaleTimeString();
entries = [`${t} ${msg}`, ...entries].slice(0, 24);
if (typeof navigator !== 'undefined') navOnline = navigator.onLine;
}
// A 1 s heartbeat keeps navigator.onLine live in the panel and logs when it flips — so a flight-mode
// toggle is visible even if the online/offline events never fire (the exact thing we are chasing).
if (typeof window !== 'undefined' && import.meta.env.MODE !== 'mock') {
let last = typeof navigator !== 'undefined' ? navigator.onLine : true;
setInterval(() => {
const now = typeof navigator !== 'undefined' ? navigator.onLine : true;
navOnline = now;
if (now !== last) {
last = now;
netlog(`navigator.onLine -> ${now}`);
}
}, 1000);
}