feat(04-04): ONBD-03 home-screen hooks + the boot wiring + the ?dest= harness profile
- homescreen.ts: wireHomeScreen behind the BOTH-halves Bot API 8.0 guard; 'added' hides (no nag), 'unknown' (always iOS) shows the neutral prompt — indeterminate, never an error; homeScreenAdded hides; a refused addToHomeScreen is caught, the affordance stays; .btn-secondary in the existing strip — no new furniture; NO beforeinstallprompt/manifest/service worker (T-04-38) - main.ts: locale derivation moved above the bindings block; stage 13 reads BOTH destination carriers, seeds the memory-only holder, builds the destination/unavailable surface (null when no id — Phase 3 boot byte-identical, D-12); the live register shows the destination surface; the ARAHARAH-DEST state-token signal fires unconditionally - index.html: ?dest=<id> harness profile threaded to the mock's startParam; the block stays inside __ARAHARAH_HARNESS__ - HS1-HS9 green (54/54), web:typecheck green
This commit is contained in:
+53
-5
@@ -41,6 +41,9 @@ import { requestChatFallback } from "./location/chat-pin.ts";
|
||||
import type { Fix, LocationState } from "./location/types.ts";
|
||||
import { localeFromInitData, type Locale } from "./i18n/strings.ts";
|
||||
import { installMockWebApp } from "./mock/telegram-mock.ts";
|
||||
import { readDestinationId, resolveDestination, setDestination } from "./search/resolve-destination.ts";
|
||||
import { buildDestinationCard, buildDestinationUnavailableCard } from "./search/destination-card.ts";
|
||||
import { wireHomeScreen } from "./telegram/homescreen.ts";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -60,8 +63,15 @@ async function boot(): Promise<void> {
|
||||
// single-shell predicate for the no-dead-ends wire.
|
||||
const overlayPresent = (): boolean => false; // Phase 3: single shell state (UI-SPEC BackButton contract)
|
||||
|
||||
// 2 — Telegram bindings: theme slot switch + BackButton total wire.
|
||||
// 12 — the locale comes from the RAW initData string (the session POST
|
||||
// returns only {token} — the claim's language_code is not reachable
|
||||
// client-side). Derived BEFORE the Telegram-bindings block so the
|
||||
// home-screen wire (ONBD-03) receives it; the strip consumes it below
|
||||
// exactly where it always has.
|
||||
const wa = getWebApp();
|
||||
const locale: Locale = wa ? localeFromInitData(wa.initData) : "en";
|
||||
|
||||
// 2 — Telegram bindings: theme slot switch + BackButton total wire.
|
||||
if (wa) {
|
||||
watchTheme(wa);
|
||||
wireBackButton(wa, overlayPresent);
|
||||
@@ -89,11 +99,45 @@ async function boot(): Promise<void> {
|
||||
stack.className = "card-stack";
|
||||
mainEl.appendChild(stack);
|
||||
|
||||
// 12 — locale comes from the RAW initData string (the session POST returns
|
||||
// only {token} — the claim's language_code is not reachable client-side).
|
||||
const locale: Locale = wa ? localeFromInitData(wa.initData) : "en";
|
||||
// 12 — the strip consumes the locale derived above the bindings block.
|
||||
strip.setLocale(locale);
|
||||
|
||||
// 13 — the destination (ONBD-02). TWO carriers, ONE validation (mechanism
|
||||
// corrected per RESEARCH Pitfall 1): the URL's own query string first
|
||||
// (the inline-button path — a web_app button's WebAppInfo carries only
|
||||
// a `url`), then initData's start_param (the documented direct-link /
|
||||
// home-screen path). An unknown id yields the honest unavailable state
|
||||
// and the boot continues into the normal staged boot — never a blank
|
||||
// map. The id resolves against the webview's OWN build-time copy of
|
||||
// the shared gazetteer, and the result is held in MEMORY ONLY
|
||||
// (GEO-03/D-07) — seeded once here, no persistence anywhere.
|
||||
const destPayload = readDestinationId(window.location.search, wa?.initData ?? null);
|
||||
const destination = resolveDestination(destPayload.id);
|
||||
setDestination(destination);
|
||||
// The destination surface — the confirmation card when the id resolved, the
|
||||
// honest unavailable card when an id ARRIVED and resolved to nothing, and
|
||||
// null when no id arrived at all: the Phase 3 boot is then byte-identical
|
||||
// (D-12's startapp-less boot landing in the search-ready map is UNCHANGED).
|
||||
const destinationCard =
|
||||
destination !== null
|
||||
? buildDestinationCard(locale, destination)
|
||||
: destPayload.id !== null
|
||||
? buildDestinationUnavailableCard(locale)
|
||||
: null;
|
||||
|
||||
// ONBD-03 — the home-screen affordance behind the Bot API 8.0 capability
|
||||
// guard. Wired after the strip exists (it hosts the affordance) and after
|
||||
// the locale is known. Telegram-native methods only — no browser install
|
||||
// APIs exist in the webview (T-04-38).
|
||||
if (wa) wireHomeScreen(wa, locale);
|
||||
|
||||
// The destination capture signal — emitted UNCONDITIONALLY (outside any map
|
||||
// guard, the same shape as the ARAHARAH-LIVE token): a WebGL2-absent device
|
||||
// still resolves the race. A STATE TOKEN, never the id (D-15 hygiene: a
|
||||
// console line carries no place data); the capture leg pairs it with a DOM
|
||||
// check on the rendered card's text.
|
||||
console.log("ARAHARAH-DEST state=" + (destination ? "resolved" : "absent"));
|
||||
|
||||
const recenter = createRecenterControl(mainEl, locale, () => {
|
||||
if (lastFix !== null && map !== null) follow?.follow(lastFix);
|
||||
});
|
||||
@@ -196,7 +240,11 @@ async function boot(): Promise<void> {
|
||||
startGantry(gantryOf(bootCard2));
|
||||
break;
|
||||
case "live":
|
||||
showCard(null);
|
||||
// With a preloaded destination the destination surface IS the live
|
||||
// state's card (the visible confirmation); with an id that arrived
|
||||
// but resolved to nothing, the honest unavailable card holds the
|
||||
// register. No id → null → the stack hides exactly as Phase 3 left it.
|
||||
showCard(destinationCard);
|
||||
break;
|
||||
case "stale":
|
||||
showCard(lostCard, "bottom");
|
||||
|
||||
Reference in New Issue
Block a user