feat(07-01): grow TelegramWebAppLike with 8.0-optional fullscreen members + capability wrapper
- 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)
This commit is contained in:
@@ -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 */
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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 };
|
||||
|
||||
Reference in New Issue
Block a user