UI: fix the lobby slide on Telegram cold launch (correct the cause)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 45s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
The first attempt (the App.svelte `started` gate) targeted the first pane mount,
but the slide is a second render. On a Telegram cold launch the URL fragment is
Telegram's #tgWebAppData=... launch params, which the router parsed as notfound;
bootstrap's navigate('/') then corrected it to the lobby asynchronously, re-keying
the route pane (notfound -> lobby) and sliding the lobby in as if returning from a
screen. A reload was static because the hash was already #/.
Treat a Telegram launch fragment as the lobby root in the router, so the route is
correct from the first pane (no re-key, no slide). Extract the pure hash->Route
parsing into routeparse.ts so it unit-tests without a DOM, and revert the gate
(the first pane never slid — local transitions skip the initial mount, as clean
browser launches showed).
Tests: routeparse unit tests (incl. the tgWebApp fragment); an e2e that launches
with the fragment in the URL and asserts the lobby plus the normalised #/ hash.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
// Pure hash-path -> Route parsing for the router. Kept dependency-free and free of any
|
||||
// reactive state so it unit-tests in the node environment; router.svelte.ts wraps it with
|
||||
// the reactive route rune and navigation.
|
||||
|
||||
export type RouteName =
|
||||
| 'login'
|
||||
| 'lobby'
|
||||
| 'new'
|
||||
| 'game'
|
||||
| 'gameChat'
|
||||
| 'gameCheck'
|
||||
| 'profile'
|
||||
| 'settings'
|
||||
| 'about'
|
||||
| 'friends'
|
||||
| 'stats'
|
||||
| 'notfound';
|
||||
|
||||
export interface Route {
|
||||
name: RouteName;
|
||||
params: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse maps a location hash to a Route. An empty hash is the lobby root. A Telegram Mini
|
||||
* App cold launch appends its launch params to the URL fragment (#tgWebAppData=...&
|
||||
* tgWebAppVersion=...); those are launch metadata, not a route, so the fragment is treated
|
||||
* as the lobby root. Otherwise it would parse as notfound, and bootstrap's navigate('/')
|
||||
* would then re-key the route pane (notfound -> lobby), sliding the lobby in on launch as if
|
||||
* returning from another screen.
|
||||
*/
|
||||
export function parse(hash: string): Route {
|
||||
const raw = hash.replace(/^#/, '');
|
||||
if (raw === '' || raw.startsWith('tgWebApp')) return { name: 'lobby', params: {} };
|
||||
const path = raw.split('?')[0];
|
||||
const seg = path.split('/').filter(Boolean);
|
||||
if (seg.length === 0) return { name: 'lobby', params: {} };
|
||||
switch (seg[0]) {
|
||||
case 'login':
|
||||
return { name: 'login', params: {} };
|
||||
case 'new':
|
||||
return { name: 'new', params: {} };
|
||||
case 'game':
|
||||
if (!seg[1]) return { name: 'notfound', params: {} };
|
||||
if (seg[2] === 'chat') return { name: 'gameChat', params: { id: seg[1] } };
|
||||
if (seg[2] === 'check') return { name: 'gameCheck', params: { id: seg[1] } };
|
||||
return { name: 'game', params: { id: seg[1] } };
|
||||
case 'profile':
|
||||
return { name: 'profile', params: {} };
|
||||
case 'settings':
|
||||
return { name: 'settings', params: {} };
|
||||
case 'about':
|
||||
return { name: 'about', params: {} };
|
||||
case 'friends':
|
||||
return { name: 'friends', params: {} };
|
||||
case 'stats':
|
||||
return { name: 'stats', params: {} };
|
||||
default:
|
||||
return { name: 'notfound', params: {} };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user