Files
scrabble-game/ui/src/components/Header.svelte
T
Ilia Denisov 37070c3cb7
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 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m13s
feat(ui): drop Telegram fullscreen; own back chevron everywhere; hidden debug panel
Finalises the Telegram Mini App navigation work after on-device testing (Pixel
10 / Android 17 + iOS, fresh beta clients):

- Remove requestFullscreen entirely. Immersive fullscreen hid Telegram's native
  header (and its BackButton) and the Android system swipe-back minimised the
  app; the owner prefers the windowed full-size (expand) presentation, so the
  app never requests fullscreen on any platform now.
- The app's own back chevron (Header, showBack = !!back) drives back-navigation
  on every platform; the native Telegram BackButton is dropped — it does not
  render in the windowed Mini App (backVisible=false on iOS and Android), so
  relying on it lost back navigation (notably none on iOS).
- Replace the temporary always-on diagnostic overlay with a hidden debug panel
  (components/DebugPanel): ten quick taps on the header title open it; it shows a
  privacy-safe client diagnostic snapshot (app version, locale, online, userId,
  Telegram chrome / viewport / SDK state — no secrets, no IP) and shares it via
  the OS share sheet / clipboard; a tap anywhere except Share dismisses it.
- Drop the now-dead telegramRequestFullscreen / telegramBackButton /
  isTelegramAndroid helpers and the iOS-fullscreen unit test.

Telegram has no native non-modal notification API (only modal showPopup /
showAlert), so in-app toasts stay ours. Docs: UI_DESIGN.md.
2026-06-23 15:05:13 +02:00

159 lines
5.5 KiB
Svelte

<script lang="ts">
import { navigate } from '../lib/router.svelte';
import { connection } from '../lib/connection.svelte';
import { t } from '../lib/i18n/index.svelte';
import { app, openDebug } from '../lib/app.svelte';
import Spinner from './Spinner.svelte';
import AdBanner from './AdBanner.svelte';
let { title, back, grow = false }: { title: string; back?: string; grow?: boolean } = $props();
// The app always shows its own back chevron when there is a back target — on every platform, in
// and out of Telegram. The native Telegram BackButton is not used: it does not render reliably in
// the windowed Mini App (relying on it would lose back navigation there).
const showBack = $derived(!!back);
// Ten quick taps on the title open the hidden debug panel (components/DebugPanel) — a support aid.
let titleTaps = 0;
let lastTitleTap = 0;
function onTitleTap(): void {
const now = Date.now();
titleTaps = now - lastTitleTap < 400 ? titleTaps + 1 : 1;
lastTitleTap = now;
if (titleTaps >= 10) {
titleTaps = 0;
openDebug();
}
}
</script>
<header class="nav" class:grow>
<div class="bar">
{#if showBack}
<button class="icon back" onclick={() => back && navigate(back)} aria-label="Back">
<span class="chev"></span>
</button>
{:else}
<span class="spacer"></span>
{/if}
{#if connection.online}
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<h1 onclick={onTitleTap}>{title}</h1>
{:else}
<h1 class="connecting"><Spinner /> <span>{t('connection.connecting')}</span></h1>
{/if}
<!-- A right-hand spacer balances the back button so the title stays centred. -->
<span class="spacer"></span>
</div>
<!-- The ad banner lives inside the nav, directly under the title bar, so it sits in the
same place on every screen — and in the game (grown nav) the spare height falls below
it (banner under the title, board pinned to the bottom). -->
{#if app.profile?.banner && app.profile.banner.campaigns.length}
<AdBanner
campaigns={app.profile.banner.campaigns}
timings={app.profile.banner.timings}
reduceMotion={app.reduceMotion}
/>
{/if}
</header>
<style>
/* By default the nav bar is minimal and the content fills the screen. In the game
it grows (class `grow`) to push the board and controls to the bottom. */
.nav {
flex: 0 0 auto;
min-height: 52px;
background: var(--bg-elev);
border-bottom: 1px solid var(--border);
display: flex;
flex-direction: column;
user-select: none;
-webkit-user-select: none;
}
.nav.grow {
/* Grow into spare height (banner under the title, the board pinned to the bottom), but
never shrink: on a short viewport the banner keeps its height and the board's own
scroll (.stage) absorbs the squeeze, instead of the banner and the board splitting it. */
flex: 1 0 auto;
}
.bar {
display: flex;
align-items: center;
gap: var(--gap);
padding: 10px var(--pad);
}
h1 {
font-size: 1.05rem;
margin: 0;
flex: 1;
text-align: center;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* The "Connecting…" indicator replaces the title while offline: a spinner + muted label,
centred like the title so the bar does not shift. */
h1.connecting {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
color: var(--text-muted);
font-weight: 500;
}
.icon,
.spacer {
min-width: 40px;
height: 36px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.back {
background: none;
border: none;
color: var(--text);
border-radius: var(--radius-sm);
padding: 0 8px;
}
.back:hover {
background: var(--surface-2);
}
/* A thin, compact "<" drawn from two borders — lighter than a glyph. */
.chev {
width: 11px;
height: 11px;
border-left: 2.5px solid currentColor;
border-bottom: 2.5px solid currentColor;
transform: rotate(45deg);
margin-left: 3px;
}
/* Telegram fullscreen: TG's native nav occupies the band between the device notch
(--tg-safe-top) and --tg-content-top. Our header spans that full band (so the layout below
is unchanged) and centres the title within it, BELOW the notch — lining it up vertically
with Telegram's own back/menu controls, which sit in the band's corners. */
:global(html.tg-fullscreen) .bar {
/* The bar is sized by its content + padding — in Telegram the title is the only child (the
back chevron is hidden), so min-height (the nav-band height) doesn't bind. Without the
(removed) hamburger that content shrank and the bar sat flush under Telegram's native nav
band. So **padding-top** is the lever: it drops the title clear of the band — the notch
plus a **10px** gap (was 6). A fixed px (not rem/em) gap so the clearance from Telegram's
native controls stays constant if the user scales up the font (the title then grows
downward and the bar with it). (Owner-tunable: the 10px.) */
min-height: var(--tg-content-top);
box-sizing: border-box;
align-items: center;
justify-content: center;
padding-top: calc(var(--tg-safe-top) + 16px);
padding-bottom: 6px;
}
:global(html.tg-fullscreen) .spacer {
display: none;
}
:global(html.tg-fullscreen) h1 {
flex: 0 1 auto;
}
</style>