a8f78c7880
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).
105 lines
3.1 KiB
Svelte
105 lines
3.1 KiB
Svelte
<script lang="ts">
|
|
// The "update required" notice (the hard version tier). Shown when the gateway has refused a
|
|
// foreground call as too old, driving the net-state machine into offlineVersionLocked. Unlike the
|
|
// old terminal cover it is dismissable: "Update" takes the user to the fix (the store on native,
|
|
// a reload on the web), while "Play offline" dismisses the notice and leaves the app in the offline
|
|
// lobby (the version lock is an offline state, sticky until an actual update on the next launch).
|
|
// Mirrors MaintenanceOverlay.svelte's look.
|
|
import { netState } from '../lib/netstate.svelte';
|
|
import { openUpdate } from '../lib/update.svelte';
|
|
import { t } from '../lib/i18n/index.svelte';
|
|
|
|
// Dismissed for the session once the user chooses "Play offline". The lock is sticky until a fresh
|
|
// launch, so the notice does not reappear this session; the app stays in the offline lobby.
|
|
let dismissed = $state(false);
|
|
</script>
|
|
|
|
{#if netState.versionLocked && !dismissed}
|
|
<div class="scrim" role="alertdialog" aria-modal="true" aria-labelledby="update-title" aria-describedby="update-body">
|
|
<div class="card">
|
|
<div class="tile" aria-hidden="true">Э</div>
|
|
<h1 id="update-title">{t('update.title')}</h1>
|
|
<p id="update-body">{t('update.body')}</p>
|
|
<div class="acts">
|
|
<button type="button" class="primary" onclick={openUpdate}>{t('update.action')}</button>
|
|
<button type="button" onclick={() => (dismissed = true)}>{t('update.playOffline')}</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.scrim {
|
|
position: fixed;
|
|
inset: 0;
|
|
/* Above the game (z 60) and toasts (z 50); below the dev DebugPanel (z 10000). */
|
|
z-index: 100;
|
|
background: rgba(0, 0, 0, 0.55);
|
|
display: grid;
|
|
place-items: center;
|
|
padding: 24px;
|
|
box-sizing: border-box;
|
|
}
|
|
.card {
|
|
max-width: 22rem;
|
|
text-align: center;
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--shadow);
|
|
padding: 2rem 1.5rem;
|
|
}
|
|
/* Mirrors a placed board tile (as Splash.svelte / MaintenanceOverlay.svelte do). */
|
|
.tile {
|
|
display: inline-grid;
|
|
place-items: center;
|
|
width: 3.5rem;
|
|
height: 3.5rem;
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
border-radius: 4px;
|
|
background: var(--tile-bg);
|
|
color: var(--tile-text);
|
|
box-shadow:
|
|
inset 0 -2px 0 var(--tile-edge),
|
|
2px 0 3px -1px rgba(0, 0, 0, 0.4);
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
h1 {
|
|
font-size: 1.25rem;
|
|
margin: 0 0 0.5rem;
|
|
}
|
|
p {
|
|
margin: 0 0 1.5rem;
|
|
color: var(--text-muted);
|
|
line-height: 1.5;
|
|
}
|
|
.acts {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.6rem;
|
|
}
|
|
button {
|
|
font: inherit;
|
|
padding: 0.55rem 1.4rem;
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
background: transparent;
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
}
|
|
button:hover {
|
|
border-color: var(--accent);
|
|
color: var(--accent);
|
|
}
|
|
.primary {
|
|
background: var(--accent);
|
|
color: var(--accent-text);
|
|
border-color: var(--accent);
|
|
}
|
|
.primary:hover {
|
|
color: var(--accent-text);
|
|
opacity: 0.9;
|
|
}
|
|
</style>
|