fix(06-08): device-gate round 1 — destination card gains ROUTE forward action; document title mirrors Telegram header (arah² → Routing to <name>); capture teardown race guard
This commit is contained in:
+30
-2
@@ -39,7 +39,7 @@ import { getSession, isSessionStale } from "./session/store.ts";
|
||||
import { acquireFix, isFixStale, fixAgeSeconds } from "./location/chain.ts";
|
||||
import { requestChatFallback } from "./location/chat-pin.ts";
|
||||
import type { Fix, LocationState } from "./location/types.ts";
|
||||
import { localeFromInitData, type Locale } from "./i18n/strings.ts";
|
||||
import { localeFromInitData, strings, 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";
|
||||
@@ -201,7 +201,7 @@ async function boot(): Promise<void> {
|
||||
// (D-12's startapp-less boot landing in the search-ready map is UNCHANGED).
|
||||
const destinationCard =
|
||||
destination !== null
|
||||
? buildDestinationCard(locale, destination)
|
||||
? buildDestinationCard(locale, destination, { withRouteAction: true })
|
||||
: destPayload.id !== null
|
||||
? buildDestinationUnavailableCard(locale)
|
||||
: null;
|
||||
@@ -219,6 +219,18 @@ async function boot(): Promise<void> {
|
||||
// check on the rendered card's text.
|
||||
console.log("ARAHARAH-DEST state=" + (destination ? "resolved" : "absent"));
|
||||
|
||||
// The document title mirrors the Telegram header (device-gate feedback,
|
||||
// 2026-10-25): a resolved destination re-titles the app "arah² → Routing to
|
||||
// <name>" (en/ms via the strings module — the NAME is public data, no
|
||||
// coordinates, D-12/D-15); the root state restores the bare brand. The
|
||||
// title element always exists (index.html's <title> is the base).
|
||||
const titleEl = document.querySelector("title");
|
||||
if (titleEl !== null) {
|
||||
titleEl.textContent = destination !== null
|
||||
? strings[locale].titleRoute.replace("{name}", destination.name)
|
||||
: "araharah";
|
||||
}
|
||||
|
||||
const recenter = createRecenterControl(mainEl, locale, () => {
|
||||
if (lastFix !== null && map !== null) {
|
||||
if (routeController !== null && routeController.guidancePhase === "driving") {
|
||||
@@ -490,6 +502,22 @@ async function boot(): Promise<void> {
|
||||
void tryResume(resumeId, routeController);
|
||||
}
|
||||
|
||||
// Device-gate fix (2026-10-24): the destination card's ROUTE button — the
|
||||
// forward action SEARCH AGAIN's destination state was missing (auto-consult
|
||||
// fires exactly once per boot; a user walking back was stranded). Re-consults
|
||||
// with the latest fix + the resolved destination; no fix yet = the honest
|
||||
// boot continues (the chain is already running or will render its own state).
|
||||
const routeButton = destinationCard?.querySelector('[data-role="route-to-destination"]');
|
||||
if (routeButton instanceof HTMLButtonElement && routeController !== null && destination !== null) {
|
||||
const controller = routeController;
|
||||
const dest = destination;
|
||||
routeButton.addEventListener("click", () => {
|
||||
const fix = lastFix;
|
||||
if (fix === null) return; // no position yet — the chain's own surfaces own this state
|
||||
void controller.consult(fix, dest);
|
||||
});
|
||||
}
|
||||
|
||||
/** GET the ?g= envelope and resume: rebuild the model from the frozen
|
||||
* snapshot (decode + cumulative + destination fields), hand the RAW
|
||||
* anchors to the controller — it owns the projection. A failed,
|
||||
|
||||
Reference in New Issue
Block a user