From af0c0e9de2dc04bdf4b887adce078e410b1d152d Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Sun, 25 Oct 2026 03:42:37 +0800 Subject: [PATCH] feat(07-01): grow TelegramWebAppLike with 8.0-optional fullscreen members + capability wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TelegramWebAppLike gains requestFullscreen/exitFullscreen/isFullscreen/lockOrientation/unlockOrientation as OPTIONAL members only (no existing member reshaped) - new web/src/telegram/fullscreen.ts: requestGuidanceFullscreen/exitGuidanceFullscreen/watchFullscreenEvents behind the BOTH-halves guard (method presence AND isVersionAtLeast("8.0")) - every platform call in try/catch — a refused request is a supported degradation, never an error surface (T-07-06) - no DOM Fullscreen API, no generic orientation lock, zero logging (T-07-10, T-07-09) --- web/src/telegram/fullscreen.ts | 101 +++++++++++++++++++++++++++++++++ web/src/telegram/webapp.ts | 19 ++++++- 2 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 web/src/telegram/fullscreen.ts diff --git a/web/src/telegram/fullscreen.ts b/web/src/telegram/fullscreen.ts new file mode 100644 index 0000000..3d25835 --- /dev/null +++ b/web/src/telegram/fullscreen.ts @@ -0,0 +1,101 @@ +/* + * fullscreen.ts — PX-04 (D-03): the Telegram-native fullscreen capability + * layer, guarded by BOTH halves of the Bot API 8.0 capability check. + * ----------------------------------------------------------------------------- + * The affordance is `Telegram.WebApp.requestFullscreen()` / `exitFullscreen()` + * ONLY — Bot API 8.0+ methods on the narrow `TelegramWebAppLike` contract + * (the same phase-scoped law as homescreen.ts: guard on the optional member, + * a total-function handler, no new UI furniture). + * + * The capability guard is BOTH halves: the method may be absent on older + * clients, AND the platform documents the API as Bot API 8.0+ — a no-op on + * an unsupported client either way (the homescreen.ts precedent). + * + * A refused request is a SUPPORTED DEGRADATION, never an error surface: every + * platform call rides inside a try/catch, and the boolean returns are + * best-effort signals the caller deliberately ignores. The orientation lock + * locks the CURRENT orientation only — never force landscape — and the DOM + * Fullscreen API plus the generic screen-level orientation lock are + * hard-banned: the Telegram-native path is the + * only primary path (T-07-10, pinned by the FS9 source-scan ban). + * + * D-14 category-only law: this path logs NOTHING — no coordinates, no user + * ids, no route payloads. A `fullscreenFailed` is a category event. + */ +import type { TelegramWebAppLike } from "./webapp.ts"; + +/** + * Best-effort request for the Mini App's native fullscreen at the + * guidance-start edge. Returns true only when the platform call did not + * throw; false is the honest fallback on null WebApp, absent method, an + * old client, or a refusal — never an error surface. + */ +export function requestGuidanceFullscreen(wa: TelegramWebAppLike | null): boolean { + if (wa === null) return false; + if (typeof wa.requestFullscreen !== "function") return false; + if (typeof wa.isVersionAtLeast !== "function" || !wa.isVersionAtLeast("8.0")) return false; + try { + wa.requestFullscreen(); + try { + // Telegram's lock pins the CURRENT orientation — never force landscape, + // and never the DOM's screen-level orientation lock (T-07-10). + wa.lockOrientation?.(); + } catch { + /* best-effort: an orientation refusal is not a guidance failure */ + } + return true; + } catch { + // the platform refused (FS4) — a supported degradation, never an error + return false; + } +} + +/** + * The mirror of requestGuidanceFullscreen: releases fullscreen and the + * orientation lock at the trip-end / walk-back teardown edges, same + * both-halves guard, same try/catch law. + */ +export function exitGuidanceFullscreen(wa: TelegramWebAppLike | null): boolean { + if (wa === null) return false; + if (typeof wa.exitFullscreen !== "function") return false; + if (typeof wa.isVersionAtLeast !== "function" || !wa.isVersionAtLeast("8.0")) return false; + try { + wa.exitFullscreen?.(); + try { + wa.unlockOrientation?.(); + } catch { + /* best-effort: the lock release is not a guidance failure */ + } + return true; + } catch { + return false; + } +} + +/** + * Register the fullscreen observability listeners (Bot API 8.0+ clients + * fire `fullscreenChanged` / `fullscreenFailed`). The onChange callback is + * wrapped in try/catch so a throwing client callback can never break the + * guidance pump; the events are observability, never correctness, and this + * path logs NOTHING (the D-14 category-only law, T-07-09). + */ +export function watchFullscreenEvents( + wa: TelegramWebAppLike | null, + onChange: (isFullscreen: boolean) => void, +): void { + if (wa === null || typeof wa.onEvent !== "function") return; + wa.onEvent("fullscreenChanged", () => { + try { + onChange(true); + } catch { + /* a throwing observer must never break the guidance pump */ + } + }); + wa.onEvent("fullscreenFailed", () => { + try { + onChange(false); + } catch { + /* a throwing observer must never break the guidance pump */ + } + }); +} diff --git a/web/src/telegram/webapp.ts b/web/src/telegram/webapp.ts index b338328..13f1ae5 100644 --- a/web/src/telegram/webapp.ts +++ b/web/src/telegram/webapp.ts @@ -23,8 +23,12 @@ * `enableClosingConfirmation`/`disableClosingConfirmation` (6.2+) and * `isActive` (8.0+) — the contract stays the contract, no existing member * changes shape, every test fake and the harness mock satisfy exactly - * these members. `@types/telegram-web-app` remains deliberately - * NOT a dependency. + * these members. Phase 7 (PX-04/D-03) adds the native-fullscreen members as + * OPTIONAL only: requestFullscreen/exitFullscreen/isFullscreen/ + * lockOrientation/unlockOrientation (8.0+) — the contract stays the contract, + * no existing member changes shape, every test fake and the harness mock + * satisfy exactly these members. `@types/telegram-web-app` remains + * deliberately NOT a dependency. */ export type HomeScreenStatus = | "unsupported" // the feature is not supported here @@ -71,6 +75,17 @@ export interface TelegramWebAppLike { enableClosingConfirmation?: () => void; /** Bot API 6.2+ — release the native close net (at trip end). */ disableClosingConfirmation?: () => void; + /** Bot API 8.0+ — request the Mini App's native fullscreen. Optional: + * absent on older clients; the 8.0 guard runs before every call. */ + requestFullscreen?(): void; + /** Bot API 8.0+ — leave the Mini App's native fullscreen. Optional. */ + exitFullscreen?(): void; + /** Bot API 8.0+ — whether the Mini App is currently fullscreen. Optional. */ + isFullscreen?: boolean; + /** Bot API 8.0+ — lock the CURRENT orientation (never forces landscape). */ + lockOrientation?(): void; + /** Bot API 8.0+ — release the orientation lock. */ + unlockOrientation?: () => void; onEvent?(event: string, cb: () => void): void; offEvent?(event: string, cb: () => void): void; BackButton?: { show(): void; hide(): void; onClick(cb: () => void): void; offClick(cb: () => void): void };