feat(06-05): capability guards — haptics + close protection + optional webapp members + RS10/BA9 gate bumps (GREEN)
- fireHaptic: the three D-08 cues (selectionChanged / impactOccurred medium / notificationOccurred success) via ?. chains; silent no-op without the API; header carries the no-haptic-on-reroute law
- engage/releaseCloseProtection: PX-03 layers gated BEFORE each call — isVersionAtLeast("7.7") body-swipe, ("6.2") native-close net; no-throw on capability-less clients (T-06-06)
- webapp.ts: HapticFeedback / vertical-swipes / closing-confirmation / isActive join as OPTIONAL members only — existing fakes untouched
- RS10 floor 16→24; BA9 input list += web/src/guidance (69507 / 204800 gz, printed)
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* close-protection.ts — PX-03's layered close protection: engaged on
|
||||
* guidance start, released at trip end (UI-SPEC §Close Protection).
|
||||
* ----------------------------------------------------------------------------
|
||||
* Layer 1: disableVerticalSwipes() (Bot API 7.7+) — the body-swipe minimize
|
||||
* path closes while guidance is active; re-enabled at trip end.
|
||||
* Layer 2: enableClosingConfirmation() (Bot API 6.2+) — the native close
|
||||
* dialog is the defense-in-depth NET behind the Rams confirm card (the
|
||||
* designed surface, D-09). The header swipe itself CANNOT be disabled —
|
||||
* official Telegram docs — which is exactly why the net exists.
|
||||
* Layer 3: the in-app abandon confirm card (banner.ts buildConfirmCard) —
|
||||
* every exit path lands there; BackButton-from-guidance means the same card.
|
||||
*
|
||||
* T-06-06 mitigation, verbatim: version gates BEFORE every call + `?.`
|
||||
* optional-member chains + no-throw behavior on capability-less clients
|
||||
* (RESEARCH Pitfall 5). A client without the capability is a silent no-op —
|
||||
* graceful degradation is the D-08 law applied to close protection.
|
||||
*/
|
||||
import type { TelegramWebAppLike } from "../telegram/webapp.ts";
|
||||
|
||||
export function engageCloseProtection(wa: TelegramWebAppLike | null): void {
|
||||
// layer 1 — Bot API 7.7+: gate BEFORE the call, then the ?. chain
|
||||
if (wa !== null && typeof wa.isVersionAtLeast === "function" && wa.isVersionAtLeast("7.7")) wa?.disableVerticalSwipes?.();
|
||||
// layer 2 — Bot API 6.2+: the native-close net, gated the same way
|
||||
if (wa !== null && typeof wa.isVersionAtLeast === "function" && wa.isVersionAtLeast("6.2")) wa?.enableClosingConfirmation?.();
|
||||
}
|
||||
|
||||
export function releaseCloseProtection(wa: TelegramWebAppLike | null): void {
|
||||
if (wa !== null && typeof wa.isVersionAtLeast === "function" && wa.isVersionAtLeast("7.7")) wa?.enableVerticalSwipes?.();
|
||||
if (wa !== null && typeof wa.isVersionAtLeast === "function" && wa.isVersionAtLeast("6.2")) wa?.disableClosingConfirmation?.();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* haptics.ts — the eyes-free channel (D-08 / UI-SPEC §Haptics).
|
||||
* ----------------------------------------------------------------------------
|
||||
* fireHaptic dispatches the three guidance cues through the capability-
|
||||
* guarded Telegram HapticFeedback API — the UI-SPEC's exact table:
|
||||
*
|
||||
* approach → HapticFeedback.selectionChanged() (within 300 m of the maneuver)
|
||||
* maneuver → HapticFeedback.impactOccurred("medium") (the step boundary)
|
||||
* arrive → HapticFeedback.notificationOccurred("success") (the trip-end
|
||||
* card's tactile twin, eyes-on-road)
|
||||
*
|
||||
* D-08 graceful degradation is the law: a WebApp without the API (older
|
||||
* client, plain browser, null) is a SILENT no-op — haptics never throw
|
||||
* (T-06-06: version variance across Bot API 6.1–8.0+ is the trust boundary;
|
||||
* the `?.` chains ARE the mitigation). Every cue is once-per-edge, armed by
|
||||
* the guidance state machine (06-02) and dispatched here.
|
||||
*
|
||||
* The no-haptic law (UI-SPEC): NO cue on reroute — a haptic would announce
|
||||
* blame, and the banner stays calm (D-04) — and none on confirm dialogs
|
||||
* (visual + thumb is enough). Firing is the caller's discipline; the module
|
||||
* exposes exactly the three legal cues so an illegal one cannot be typed.
|
||||
*/
|
||||
import type { TelegramWebAppLike } from "../telegram/webapp.ts";
|
||||
|
||||
export type HapticCue = "approach" | "maneuver" | "arrive";
|
||||
|
||||
export function fireHaptic(wa: TelegramWebAppLike | null, cue: HapticCue): void {
|
||||
if (cue === "approach") wa?.HapticFeedback?.selectionChanged();
|
||||
else if (cue === "maneuver") wa?.HapticFeedback?.impactOccurred("medium");
|
||||
else wa?.HapticFeedback?.notificationOccurred("success");
|
||||
}
|
||||
Reference in New Issue
Block a user