From d43a740eb613ce38d7fb2aa53467a0c498a04c61 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Mon, 6 Jul 2026 19:41:09 +0200 Subject: [PATCH] docs(offline): document connectivity auto-detect + transport kill switch The offline auto-detect shipped in #202 (cold-start no-connection dialog / auto offline) and #203 (mid-session flight-mode reactivity) but neither PR baked the docs. Add the user story to FUNCTIONAL.md (+ _ru mirror) and the mechanism to the ARCHITECTURE offline section: the auto vs deliberate distinction, the cold-start navigator.onLine / bounded reachability-probe decision, the mid-session window online/offline events backed by a navigator.onLine poll (the events are unreliable on some platforms, notably iOS PWAs), and the reachability probe being the one call exempt from the offline kill switch. --- docs/ARCHITECTURE.md | 14 +++++++++++++- docs/FUNCTIONAL.md | 9 +++++++++ docs/FUNCTIONAL_ru.md | 9 +++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c7d8d85..6695360 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1263,7 +1263,19 @@ the ad-banner slot. A **cold launch already in offline mode** boots from the per profile (the profile is saved on every online adopt/refresh) — `bootstrap` skips the session adoption and profile fetch that would otherwise hang with no network, and lands straight in the offline lobby; without a cached profile (an install that was never online) it drops the sticky flag -and boots online instead. The gateway registers the `.webmanifest` MIME type +and boots online instead. Beyond the sticky toggle the app **auto-detects connectivity**: the +reactive flag carries an *auto* bit, so a self-entered offline (no network) is transient +(session-only, never persisted) and self-heals to online when the network returns, while a deliberate +one (the toggle, or the cold-start dialog's *Switch*) persists. At cold start `navigator.onLine === +false` enters offline for the session; otherwise a single bounded reachability probe (a `profile.get`, +~3 s) decides — success boots online, a timeout on an eligible install raises a *No connection* dialog +(*Switch* = sticky offline, *Wait for network* = boot online with the reachability watcher retrying). +Mid-session the `window` `online`/`offline` events drive it — `offline` auto-enters, `online` +re-verifies reachability before returning — backed by a `navigator.onLine` poll because those events +are unreliable on some platforms (notably iOS PWAs). Offline is a real **transport kill switch** +(every gateway call is refused, so no traffic leaks); the reachability probe is the one call exempt +from it (it is the return-to-online mechanism), and the transient reachability watcher is suppressed +while offline. The gateway registers the `.webmanifest` MIME type in-process (the distroless image has no `/etc/mime.types`). Hash-named `/assets/*` are served `immutable` (a relaunch is a cache hit, not a re-download); the HTML shells are `no-cache` so a new deploy is picked up — both containers apply the same caching. An diff --git a/docs/FUNCTIONAL.md b/docs/FUNCTIONAL.md index ec05603..cb32d02 100644 --- a/docs/FUNCTIONAL.md +++ b/docs/FUNCTIONAL.md @@ -268,6 +268,15 @@ the app quietly preloads the dictionaries for the player's enabled variants whil (once installed) launches from a precached shell even with no network. The mode is device-scoped and sticky across launches. +The app also **enters offline mode on its own** when it cannot reach the network. On a cold launch +with no connection it switches to offline for that session; when the device is online but the gateway +stays silent for a few seconds it asks first — *No connection. Switch to offline mode?*, with +**Switch** (go offline) or **Wait for network** (keep retrying online). While the app is open, losing +the network — airplane mode — switches to offline automatically, and restoring it returns online by +itself. A self-entered offline is temporary: it reverts to online the moment the network is back, and +the next launch re-checks. A **deliberate** offline — the Settings toggle, or **Switch** in that +dialog — is the player's choice: it is kept, and never undone automatically. + ### Social: friends, block, chat, nudge Become friends in two ways: redeem a **one-time code** the other player issues (six digits, valid for twelve hours), or send a **request to someone you have played diff --git a/docs/FUNCTIONAL_ru.md b/docs/FUNCTIONAL_ru.md index 32d198a..659fad5 100644 --- a/docs/FUNCTIONAL_ru.md +++ b/docs/FUNCTIONAL_ru.md @@ -273,6 +273,15 @@ e-mail) либо ввод фразы. Активные игры форфейтя запускается из прекешированного шелла даже без сети. Режим привязан к устройству и сохраняется между запусками. +Приложение также **само включает офлайн-режим**, когда не может достучаться до сети. При холодном +запуске без связи оно переходит в офлайн на текущую сессию; когда устройство онлайн, но шлюз молчит +несколько секунд, оно сперва спрашивает — *Нет связи. Включить офлайн-режим?*, с выбором **Включить** +(уйти в офлайн) или **Ждать сеть** (продолжить попытки онлайн). Пока приложение открыто, потеря сети — +режим полёта — переключает в офлайн автоматически, а её восстановление само возвращает онлайн. +Самостоятельно включённый офлайн временный: он возвращается в онлайн, как только сеть появилась, и +следующий запуск проверяет заново. **Осознанный** офлайн — тумблер в Настройках или **Включить** в том +диалоге — это выбор игрока: он сохраняется и никогда не отменяется автоматически. + ### Социальное: друзья, блок, чат, nudge Подружиться можно двумя способами: погасить **одноразовый код**, который выпускает другой игрок (шесть цифр, действует двенадцать часов), либо отправить **заявку