release: v1.4.1 — Telegram nav (windowed, own back button, debug panel) #129

Merged
developer merged 7 commits from development into master 2026-06-23 13:27:31 +00:00
4 changed files with 43 additions and 19 deletions
Showing only changes of commit 6f00c2f41d - Show all commits
+9 -1
View File
@@ -4,7 +4,7 @@
import { app, bootstrap } from './lib/app.svelte';
import { navigate, router, type RouteName } from './lib/router.svelte';
import { t } from './lib/i18n/index.svelte';
import { insideTelegram, telegramBackButton } from './lib/telegram';
import { insideTelegram, telegramBackButton, telegramChromeDiag } from './lib/telegram';
import Toast from './components/Toast.svelte';
import Splash from './components/Splash.svelte';
import StaleInviteModal from './components/StaleInviteModal.svelte';
@@ -138,6 +138,14 @@
<Splash />
{/if}
<!-- TEMP DIAGNOSTIC overlay (Android nav debug) — REMOVE before merge -->
{#if app.ready && insideTelegram()}
{#key router.route.name + (router.route.params.id ?? '')}
<pre
style="position:fixed;left:0;right:0;bottom:0;z-index:9999;margin:0;padding:6px 8px;font:10px/1.35 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;background:rgba(0,0,0,0.85);color:#3f3;max-height:45vh;overflow:auto;pointer-events:none">{telegramChromeDiag()}</pre>
{/key}
{/if}
<style>
.splash {
height: 100%;
+5 -4
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { navigate } from '../lib/router.svelte';
import { insideTelegram } from '../lib/telegram';
import { insideTelegram, isTelegramAndroid } from '../lib/telegram';
import { connection } from '../lib/connection.svelte';
import { t } from '../lib/i18n/index.svelte';
import { app } from '../lib/app.svelte';
@@ -9,9 +9,10 @@
let { title, back, grow = false }: { title: string; back?: string; grow?: boolean } = $props();
// Inside Telegram the native header back button (App.svelte) is the back control, so
// the app's own chevron is hidden to avoid two back affordances.
const showBack = $derived(!!back && !insideTelegram());
// Inside Telegram the native header back button (App.svelte) is the back control, so the app's
// own chevron is hidden to avoid two back affordances — except on Android, where the native
// BackButton does not render in the (non-fullscreen) presentation, so the app shows its own.
const showBack = $derived(!!back && (!insideTelegram() || isTelegramAndroid()));
</script>
<header class="nav" class:grow>
+29 -7
View File
@@ -36,6 +36,7 @@ interface TelegramWebApp {
selectionChanged?: () => void;
};
BackButton?: {
isVisible?: boolean;
show?: () => void;
hide?: () => void;
onClick?: (cb: () => void) => void;
@@ -303,10 +304,22 @@ export function telegramHaptic(kind: Haptic): void {
* Android, on desktop, or on clients predating the method.
*/
export function telegramRequestFullscreen(): void {
if (webApp()?.platform === 'ios') webApp()?.requestFullscreen?.();
// TEMP (owner test): fullscreen fully disabled, incl. iOS, to confirm the Android "fullscreen
// look" is Telegram's own Mini App presentation, not our requestFullscreen (isFullscreen is
// already false on Android). Restore the iOS path after the test:
// if (webApp()?.platform === 'ios') webApp()?.requestFullscreen?.();
}
/** isTelegramAndroid reports whether the Mini App runs on a Telegram Android client (reported as
* 'android', or 'android_x' for Telegram X). The native header BackButton does not render in the
* Android (non-fullscreen) presentation, so back navigation there uses the app's own chevron. */
export function isTelegramAndroid(): boolean {
const p = webApp()?.platform;
return p === 'android' || p === 'android_x';
}
let backHandler: (() => void) | null = null;
let lastBackShow = false; // TEMP diag: the last telegramBackButton(show) request
/**
* telegramBackButton shows or hides Telegram's native header back button, wiring its
@@ -314,6 +327,7 @@ let backHandler: (() => void) | null = null;
* inside Telegram so only the native control shows.
*/
export function telegramBackButton(show: boolean, onClick?: () => void): void {
lastBackShow = show;
const b = webApp()?.BackButton;
if (!b) return;
if (backHandler) b.offClick?.(backHandler);
@@ -481,14 +495,22 @@ export function collectTelegramDiag(): TelegramDiag {
export function telegramChromeDiag(): string {
const w = webApp();
if (!w) return 'no telegram';
const ih = typeof window === 'undefined' ? 0 : window.innerHeight;
const sh = typeof screen === 'undefined' ? 0 : screen.height;
const win = typeof window === 'undefined' ? undefined : window;
const scr = typeof screen === 'undefined' ? undefined : screen;
const vv = win?.visualViewport ?? undefined;
const bar = typeof document === 'undefined' ? null : (document.querySelector('.bar')?.getBoundingClientRect() ?? null);
const sa = w.safeAreaInset;
const csa = w.contentSafeAreaInset;
const n = (v: number | undefined): string => (v === undefined ? '—' : String(Math.round(v)));
return [
`platform: ${w.platform ?? '—'} version: ${w.version ?? '—'}`,
`platform: ${w.platform ?? '—'} version: ${w.version ?? '—'} scheme: ${w.colorScheme ?? '—'}`,
`isFullscreen: ${w.isFullscreen} isExpanded: ${w.isExpanded}`,
`viewportH: ${w.viewportHeight ?? '—'} stableH: ${w.viewportStableHeight ?? '—'}`,
`innerH: ${ih} screenH: ${sh}`,
`contentSafeTop: ${w.contentSafeAreaInset?.top ?? '—'} safeTop: ${w.safeAreaInset?.top ?? '—'}`,
`inTG: ${insideTelegram()} backReq: ${lastBackShow} backPresent: ${!!w.BackButton} backVisible: ${w.BackButton?.isVisible}`,
`innerH: ${n(win?.innerHeight)} outerH: ${n(win?.outerHeight)} screenH: ${n(scr?.height)} availH: ${n(scr?.availHeight)}`,
`screenY: ${n(win?.screenY)} vv.offTop: ${n(vv?.offsetTop)} vv.h: ${n(vv?.height)}`,
`tgViewportH: ${n(w.viewportHeight)} stableH: ${n(w.viewportStableHeight)}`,
`safeArea T/B: ${n(sa?.top)}/${n(sa?.bottom)} contentSafe T/B: ${n(csa?.top)}/${n(csa?.bottom)}`,
`appHeader top/h: ${bar ? Math.round(bar.top) : '—'}/${bar ? Math.round(bar.height) : '—'}`,
].join('\n');
}
-7
View File
@@ -9,7 +9,6 @@
import { gateway } from '../lib/gateway';
import { navigate } from '../lib/router.svelte';
import { t } from '../lib/i18n/index.svelte';
import { insideTelegram, telegramChromeDiag } from '../lib/telegram'; // TEMP diag — remove before merge
import { resultBadge } from '../lib/result';
import { badgeKind } from '../lib/unread';
import { getLobby, setLobby } from '../lib/lobbycache';
@@ -224,12 +223,6 @@
<Screen title={app.profile?.displayName ?? t('app.title')}>
<div class="lobby">
<!-- TEMP DIAGNOSTIC (Android fullscreen debug) — REMOVE before merge -->
{#if insideTelegram()}
<pre
style="margin:0 0 8px;padding:8px;font:11px/1.4 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;background:var(--bg-elev);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text)"
>{telegramChromeDiag()}</pre>
{/if}
{#if invitations.length}
<section>
<h2>{t('lobby.invitations')}</h2>