feat(offline): mid-session flight-mode reactivity [PR2] #203

Merged
developer merged 5 commits from feature/offline-flightmode into development 2026-07-06 17:31:17 +00:00
Owner

Offline mode — PR2 of the auto-detect (mid-session reactivity). Follows #202 (cold-start). Fixes your observation: the online/offline detect worked only at cold start; toggling flight mode in the app had no effect.

Behaviour

React to the network changing while the app is open, via passive online/offline events (no polling, ~no battery cost):

  • Interface lost (flight mode on) → enter offline mode for the session (auto).
  • Interface back (flight mode off) → if the offline was auto, verify the gateway is really reachable (interface up ≠ reachable) and return online; a deliberate offline (the toggle or the cold-start dialog) is left as the player's choice.

Pieces

  • offline.svelte: track auto — auto-detected vs deliberate (the distinction we designed).
  • connection.svelte: checkReachable is a pure one-shot now (the caller decides); the reachability watcher never probes in offline mode (events drive recovery — no background network offline).
  • transport.ts: the reachability probe is exempt from the kill switch — it IS the mechanism that decides whether to return online, fired only deliberately (the watcher is guarded off in offline; checkReachable runs it on an online event).
  • app.svelte.ts: initNetworkReactivity wires the events (web-only, skipped in the mock); called from bootstrap.

Tests

Online unaffected — the listeners skip the mock: e2e 196. check 0, unit 479, gate 113.3/114 (no bump). Mid-session reactivity is contour-verified (the mock e2e can't simulate a device network toggle) — please test: play online → flight mode on (→ offline UI) → off (→ back online); and in deliberate offline, toggle flight mode (should stay offline).

Note: Capacitor Network.addListener (native store app) is not wired here — the PWA target uses the window events, which cover the mobile home-screen PWA. Native can be a follow-up.

Offline mode — **PR2 of the auto-detect** (mid-session reactivity). Follows #202 (cold-start). Fixes your observation: the online/offline detect worked only at cold start; toggling flight mode in the app had no effect. ## Behaviour React to the network changing while the app is open, via **passive** `online`/`offline` events (no polling, ~no battery cost): - **Interface lost** (flight mode on) → enter offline mode **for the session** (auto). - **Interface back** (flight mode off) → if the offline was **auto**, verify the gateway is really reachable (interface up ≠ reachable) and **return online**; a **deliberate** offline (the toggle or the cold-start dialog) is left as the player's choice. ## Pieces - `offline.svelte`: track `auto` — auto-detected vs deliberate (the distinction we designed). - `connection.svelte`: `checkReachable` is a pure one-shot now (the caller decides); the reachability watcher never probes in offline mode (events drive recovery — no background network offline). - `transport.ts`: the reachability probe is **exempt from the kill switch** — it IS the mechanism that decides whether to return online, fired only deliberately (the watcher is guarded off in offline; `checkReachable` runs it on an `online` event). - `app.svelte.ts`: `initNetworkReactivity` wires the events (web-only, skipped in the mock); called from `bootstrap`. ## Tests Online unaffected — the listeners skip the mock: e2e 196. check 0, unit 479, gate 113.3/114 (no bump). Mid-session reactivity is **contour-verified** (the mock e2e can't simulate a device network toggle) — please test: play online → flight mode on (→ offline UI) → off (→ back online); and in deliberate offline, toggle flight mode (should stay offline). Note: Capacitor `Network.addListener` (native store app) is not wired here — the PWA target uses the `window` events, which cover the mobile home-screen PWA. Native can be a follow-up.
developer added 1 commit 2026-07-06 16:15:44 +00:00
feat(offline): mid-session flight-mode reactivity (auto-offline self-heals)
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 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m41s
e0d28733ff
React to the network changing while the app is open (e.g. the player toggling
flight mode), via passive online/offline events - no polling, no battery cost:
- interface lost -> enter offline mode for the session (auto);
- interface back -> if the offline was auto, verify the gateway is really
  reachable (an interface being up does not guarantee it) and return online; a
  deliberate offline (the toggle or the cold-start dialog) is left as-is.

- offline.svelte: track `auto` (auto-detected vs the player's deliberate choice).
- connection.svelte: checkReachable is now a pure one-shot (the caller decides);
  the reachability watcher never probes in offline mode (events drive recovery).
- transport.ts: the reachability probe is exempt from the kill switch - it IS
  the mechanism that decides whether to return online, fired only deliberately.
- app.svelte.ts: initNetworkReactivity wires the events (web-only, skipped in
  the mock); called from bootstrap.

Online unaffected (skipped in the mock e2e): e2e 196. Mid-session reactivity is
contour-verified.
developer added 1 commit 2026-07-06 16:35:53 +00:00
fix(offline): return online after flight-mode off; gate online-game actions offline
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 1m11s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m38s
fc8143758a
Two mid-session issues found on the contour:
- Auto-offline did not return to online after the network came back: a single
  reachability check right after the 'online' event failed (the interface is up
  before the gateway is actually reachable again). Retry a few times with backoff
  (tryReturnOnline) — that is what actually gets the app back online.
- An online game viewed while offline (flight mode on) still enabled its network
  actions, so they hit the kill switch and raised 'something went wrong' toasts.
  Gate them: netReady = isLocalGame || (connection.online && !offlineMode.active)
  — a local game stays fully usable; an online game's make/exchange/hint/resign
  disable while offline and re-enable when back. Also suppress the 'offline' code
  in handleError (a blocked call in offline mode is expected, not a toast).
developer added 1 commit 2026-07-06 16:50:11 +00:00
fix(offline): poll navigator.onLine to return online (the online event is unreliable)
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 1m24s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m41s
fffc6030ce
The auto-offline -> online return still did not fire: the retry hung on the
'online' event, which an installed PWA often does not deliver. Replace it with a
lightweight poll while in auto-offline that reads navigator.onLine (a reliable
live flag, unlike the event) and only hits the network for a reachability check
when the interface is actually up - so flight mode ON costs no radio, and flight
mode OFF is detected within ~4s and returns online. The 'online' event, when it
does fire, just kicks an immediate check. Runs only in auto-offline (deliberate
offline is the player's choice); wired for both the mid-session and cold-start
auto-offline paths.
developer added 1 commit 2026-07-06 17:03:34 +00:00
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
09c5a5e72e
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.
developer added 1 commit 2026-07-06 17:21:00 +00:00
fix(offline): set the auto flag in setOfflineMode; remove temp diagnostic
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 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m51s
3ad66f49c7
The auto-offline -> online return was dead because setOfflineMode never set the
'auto' flag: I added the state + getter but forgot the assignment, so it stayed
false. So scheduleRecovery bailed immediately (no poll ran) and the online event's
'if (active && auto)' was false. The contour diagnostic confirmed offline.auto
stayed false after an auto-offline. Add 'auto = on && !persist'.

Also remove the temporary network diagnostic panel (netdiag + the lobby strip) --
it did its job of pinpointing this.
owner approved these changes 2026-07-06 17:28:32 +00:00
Dismissed
owner approved these changes 2026-07-06 17:30:05 +00:00
developer merged commit 350013acd9 into development 2026-07-06 17:31:17 +00:00
developer deleted branch feature/offline-flightmode 2026-07-06 17:31:17 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: developer/scrabble-game#203