feat(offline): implicit net-state model, two-tier version gate, unified lobby
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m14s
CI / conformance (pull_request) Successful in 12s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Failing after 22s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m14s
CI / conformance (pull_request) Successful in 12s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Failing after 22s
Land the offline-model redesign (ANDROID_PLAN.md O1-O7): replace the explicit offline toggle with a single detected net-state machine, unify the lobby, and add a two-tier client-version gate. Contour-safe: both version vars empty => dormant, and the wire change is additive, so web/pwa/vk/tg behaviour is unchanged unless the gate is deliberately configured. O1 pure net-state reducer (test-first). O2 reactive store + event wiring (connection/offline become thin shims; +@capacitor/network). O3 remove the offline toggle + migrate the pref. O4 two-tier gate: hard update_required degrades to an offline Update/Play-offline notice (not terminal); soft GATEWAY_RECOMMENDED_CLIENT_VERSION -> X-Update-Recommended nudge. O5 unified lobby (device-local + greyed-from-cache server games; self-set identity; closes G-step-0). O6 create flows (with-friends online/offline segment + offline dict guard). O7 docs (ARCHITECTURE, FUNCTIONAL +_ru, TESTING, deploy/README). Also disable the manual android-build CI workflow for now (rename to .disabled); the Android APK release comes later. Tests: gateway go green, svelte-check 0/0, vitest 617, e2e 122/122 (chromium + webkit).
This commit is contained in:
+65
-15
@@ -1,26 +1,76 @@
|
||||
// Global "client too old" signal. `active` latches true the first time the gateway answers a
|
||||
// user-initiated online call with the update_required sentinel — the HTTP-header version gate the
|
||||
// edge checks before decoding the payload (docs/ARCHITECTURE.md §2). It is terminal: unlike the
|
||||
// maintenance signal there is no self-clearing poll, because an installed build cannot become
|
||||
// compatible without an actual update. A non-dismissable overlay (UpdateOverlay.svelte) then covers
|
||||
// the app; its one action sends the user to the store (native) or reloads (web). Offline play never
|
||||
// trips it — the network kill switch refuses the call before it leaves the device.
|
||||
// The client-version gate's two client signals (docs/ARCHITECTURE.md §2).
|
||||
//
|
||||
// HARD tier ("client too old"): the gateway refuses a foreground call with the update_required
|
||||
// sentinel (the Execute result_code, the Subscribe FailedPrecondition). The transport reports it here,
|
||||
// which drives the net-state machine into offlineVersionLocked; the notice (UpdateOverlay.svelte) reads
|
||||
// netState.versionLocked and offers "Update" (store / reload) or "Play offline" (dismiss → stay
|
||||
// offline). Offline play never trips it — the kill switch refuses the call before it leaves the device,
|
||||
// and the background guest reconcile swallows it (stays a local guest, no notice).
|
||||
//
|
||||
// SOFT tier ("update available"): a served response carries the X-Update-Recommended header, which the
|
||||
// transport reports here. It drives the machine (versionRecommended), whose setNudge effect — fired
|
||||
// only from online — latches the dismissible nudge (UpdateNudge.svelte). The hard notice supersedes the
|
||||
// nudge naturally: the nudge shows only while online, and version-locked is an offline state.
|
||||
|
||||
import { emit } from './netstate.svelte';
|
||||
import { clientChannel } from './channel';
|
||||
|
||||
/** UPDATE_REQUIRED is the stable sentinel the gateway returns (the Execute result_code, and the
|
||||
* GatewayError code the Subscribe FailedPrecondition maps to) for a client too old to be served. */
|
||||
export const UPDATE_REQUIRED = 'update_required';
|
||||
|
||||
let required = $state(false);
|
||||
/** openUpdate sends the user to the fix, shared by the hard-tier notice and the soft-tier nudge: the
|
||||
* store listing on a native build (VITE_RUSTORE_URL, handed to the OS via '_system'), or a plain
|
||||
* reload on the web (which fetches the current client). */
|
||||
export function openUpdate(): void {
|
||||
const ch = clientChannel();
|
||||
if (ch === 'android' || ch === 'ios') {
|
||||
const url = import.meta.env.VITE_RUSTORE_URL;
|
||||
if (url) window.open(url, '_system');
|
||||
} else {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
export const updateRequired = {
|
||||
/** active is true once a foreground online call has been refused as too old. Terminal. */
|
||||
/** reportUpdateRequired drives the net-state machine into offlineVersionLocked (the hard tier). The
|
||||
* transport calls it only for a foreground update_required — the background reconcile swallows it and
|
||||
* stays offline. The notice reads netState.versionLocked; there is no separate latch. */
|
||||
export function reportUpdateRequired(): void {
|
||||
emit('versionRejected');
|
||||
}
|
||||
|
||||
// The soft "update available" nudge. `active` latches when the machine accepts a versionRecommended
|
||||
// from online (via the setNudge effect → latchUpdateNudge); `dismissed` hides the banner for the
|
||||
// session once the user closes it. A fresh launch re-evaluates the version, so both reset on reload.
|
||||
let recommended = $state(false);
|
||||
let nudgeDismissed = $state(false);
|
||||
|
||||
export const updateRecommended = {
|
||||
/** active is true once the gateway has signalled an available (non-blocking) update. */
|
||||
get active(): boolean {
|
||||
return required;
|
||||
return recommended;
|
||||
},
|
||||
/** dismissed is true once the user has closed the nudge for this session. */
|
||||
get dismissed(): boolean {
|
||||
return nudgeDismissed;
|
||||
},
|
||||
};
|
||||
|
||||
/** reportUpdateRequired latches the terminal "update required" overlay. The transport calls it when
|
||||
* a foreground call returns the update_required sentinel; idempotent. */
|
||||
export function reportUpdateRequired(): void {
|
||||
required = true;
|
||||
/** latchUpdateNudge raises the soft-nudge latch. It is wired to the machine's setNudge effect
|
||||
* (registerNetNudge), which fires only from online, so the nudge is never raised while offline or
|
||||
* version-locked. */
|
||||
export function latchUpdateNudge(): void {
|
||||
recommended = true;
|
||||
}
|
||||
|
||||
/** dismissUpdateNudge hides the nudge for the session (a fresh launch re-evaluates the version). */
|
||||
export function dismissUpdateNudge(): void {
|
||||
nudgeDismissed = true;
|
||||
}
|
||||
|
||||
/** reportUpdateRecommended signals an available update (the soft tier). The transport calls it when a
|
||||
* served response carries the X-Update-Recommended header; it drives the machine (versionRecommended),
|
||||
* which latches the nudge from online. */
|
||||
export function reportUpdateRecommended(): void {
|
||||
emit('versionRecommended');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user