2d1fadb50c
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Successful in 1m12s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m54s
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, the wire change is additive, so web/pwa/vk/tg behaviour is unchanged unless the gate is deliberately configured. O1 net-state reducer (test-first). O2 store + wiring (connection/offline 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. Telegram/VK stay online-only (contour review): the offline model is channel-gated via offlineCapable() (native + plain web; false in the mini-apps). offlineMode.active is hard-gated on it, so the whole model (blue chrome, unified/greyed lobby, transport kill switch, device-local vs_ai/hotseat create) stays inert in Telegram/VK regardless of the detected net state (closing a version-lock path that could have flipped them offline); and New Game's with-friends hides the online/offline segment there, leaving the remote invite alone. ARCHITECTURE already declared "Telegram/VK are exempt" - this makes the code match. Deploy/CI: wire the version gate through the deploy (GATEWAY_MIN_CLIENT_VERSION + GATEWAY_RECOMMENDED_CLIENT_VERSION as plain unprefixed vars via compose + ci.yaml + prod-deploy.yaml + write-prod-env.sh + .env.example) so the gate is live when set (test-contour commit-hash version fails open; empty => dormant). Disable the manual android-build CI workflow for now (rename .disabled). Bump the app bundle budget 127->130 KB for the added always-loaded wiring. Fix the UI Docker stage (gateway/Dockerfile): install --ignore-scripts so the Alpine/musl SPA build no longer tries to native-build sharp (a local android:assets tool, unused in the image); esbuild's binary is an optional dep so Vite still builds. Tests: gateway go green (two-tier gate over a real HTTP handler); docker build of the landing + gateway targets green locally; compose --no-interpolate confirms the gateway env; two new telegram.spec.ts e2e (offline signal keeps the chrome online + quick-match opponent choice visible => server enqueue; with-friends shows no pass-and-play), RED-verified; svelte-check 0/0, vitest 617, e2e 248 (chromium + webkit), build + bundle-size 127.8/130.
37 lines
1.6 KiB
TypeScript
37 lines
1.6 KiB
TypeScript
// Pure helpers for offline-mode support, kept out of the reactive module (offline.svelte.ts) so they
|
|
// unit-test in the node env. Offline became implicit — the net-state machine detects connectivity, and
|
|
// there is no deliberate toggle — so only the background dict-preload eligibility check and a one-time
|
|
// cleanup of the retired deliberate-offline preference remain here.
|
|
|
|
const STORAGE_KEY = 'scrabble.offlineMode';
|
|
|
|
/**
|
|
* clearOfflinePref removes the retired deliberate-offline preference key. The offline model is implicit
|
|
* now — the app boots online and the machine detects offline — so a persisted flag from a pre-redesign
|
|
* install is orphaned data that is never read again; boot clears it once (best-effort) so nobody is left
|
|
* with a dead key.
|
|
*/
|
|
export function clearOfflinePref(): void {
|
|
try {
|
|
if (typeof localStorage !== 'undefined') localStorage.removeItem(STORAGE_KEY);
|
|
} catch {
|
|
/* best-effort — a storage failure just leaves the orphaned key, which is never read anyway */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* offlinePreloadEligible reports whether a background dictionary preload should run in this context: an
|
|
* installed standalone web PWA (not a Telegram/VK mini-app, not a plain browser tab) with a confirmed
|
|
* email, currently online. Elsewhere the preload is wasted bandwidth — the context has no offline play
|
|
* to prepare for — so kickDictPreload skips it.
|
|
*/
|
|
export function offlinePreloadEligible(opts: {
|
|
hasEmail: boolean;
|
|
standalone: boolean;
|
|
inTelegram: boolean;
|
|
inVK: boolean;
|
|
online: boolean;
|
|
}): boolean {
|
|
return opts.hasEmail && opts.standalone && !opts.inTelegram && !opts.inVK && opts.online;
|
|
}
|