Stage 17: UI defect fixes (russian variant, Telegram theme/nav/banner, reconnect, hint zoom, plaque, history, transitions, per-game cache)
- #6 align the UI variant id to the backend canonical 'russian_scrabble' (type, variants, Lobby, mock, tests) — fixes the New->Russian 400 - #11/#12 inside Telegram force the colour scheme from WebApp.colorScheme (over OS prefers-color-scheme, fixing the Telegram Desktop breakage) and hide the theme switcher - #14/#15 nav bar takes Telegram's bg; announcement banner gets a dedicated subtle --ad-bg accent token - #16 suppress the reconnect banner while backgrounded and silently reconnect the live stream on return to the foreground - #17 hint zoom scrolls to the placement's bounding box, not the top-left - #19/#20 players plaque: active seat raised with side shadows, others sunk; tap toggles history - #21/#23 history: scrollbar-gutter:stable (no word jitter); fixed-height drawer pins the bottom shadow to the board - #3 (UI) disable nudge on the player's own turn - #18a directional screen slide transitions (forward in from the right, back reveals the lobby) - #13 per-game in-memory cache: instant render on re-entry + background refresh - e2e: openGame waits for the slide transition to settle
This commit is contained in:
+56
-17
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { cubicOut } from 'svelte/easing';
|
||||
import { app, bootstrap } from './lib/app.svelte';
|
||||
import { router } from './lib/router.svelte';
|
||||
import { t } from './lib/i18n/index.svelte';
|
||||
@@ -17,28 +18,57 @@
|
||||
onMount(() => {
|
||||
void bootstrap();
|
||||
});
|
||||
|
||||
// Screen transitions: the lobby is the navigation root. Entering a screen from the
|
||||
// lobby slides it in from the right (forward); returning to the lobby slides the
|
||||
// screen out to the right and reveals the lobby (back). Transitions are local, so
|
||||
// they do not play on the initial mount, and collapse to nothing under reduce-motion.
|
||||
const dir = $derived(router.route.name === 'lobby' ? 'back' : 'forward');
|
||||
const enterSign = $derived(dir === 'forward' ? 1 : -1);
|
||||
const leaveSign = $derived(dir === 'forward' ? -1 : 1);
|
||||
const routeKey = $derived(router.route.name + (router.route.params.id ?? ''));
|
||||
const animMs = $derived(app.reduceMotion ? 0 : 260);
|
||||
|
||||
// slideX slides a pane horizontally by a full width. sign>0 enters from / exits to
|
||||
// the right; sign<0 the left. Percentage keeps it viewport-relative without reading
|
||||
// innerWidth, and the .router clips the off-screen pane.
|
||||
function slideX(_node: Element, { duration, sign }: { duration: number; sign: number }) {
|
||||
return {
|
||||
duration,
|
||||
easing: cubicOut,
|
||||
css: (tt: number) => `transform: translateX(${(1 - tt) * sign * 100}%)`,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !app.ready}
|
||||
<div class="splash">{t('common.loading')}</div>
|
||||
{:else if router.route.name === 'login'}
|
||||
<Login />
|
||||
{:else if router.route.name === 'new'}
|
||||
<NewGame />
|
||||
{:else if router.route.name === 'game'}
|
||||
<Game id={router.route.params.id} />
|
||||
{:else if router.route.name === 'profile'}
|
||||
<Profile />
|
||||
{:else if router.route.name === 'settings'}
|
||||
<Settings />
|
||||
{:else if router.route.name === 'about'}
|
||||
<About />
|
||||
{:else if router.route.name === 'friends'}
|
||||
<Friends />
|
||||
{:else if router.route.name === 'stats'}
|
||||
<Stats />
|
||||
{:else}
|
||||
<Lobby />
|
||||
<div class="router">
|
||||
{#key routeKey}
|
||||
<div class="pane" in:slideX={{ duration: animMs, sign: enterSign }} out:slideX={{ duration: animMs, sign: leaveSign }}>
|
||||
{#if router.route.name === 'login'}
|
||||
<Login />
|
||||
{:else if router.route.name === 'new'}
|
||||
<NewGame />
|
||||
{:else if router.route.name === 'game'}
|
||||
<Game id={router.route.params.id} />
|
||||
{:else if router.route.name === 'profile'}
|
||||
<Profile />
|
||||
{:else if router.route.name === 'settings'}
|
||||
<Settings />
|
||||
{:else if router.route.name === 'about'}
|
||||
<About />
|
||||
{:else if router.route.name === 'friends'}
|
||||
<Friends />
|
||||
{:else if router.route.name === 'stats'}
|
||||
<Stats />
|
||||
{:else}
|
||||
<Lobby />
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Toast />
|
||||
@@ -50,4 +80,13 @@
|
||||
place-items: center;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.router {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.pane {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user