Files
2026-10-23 22:37:55 +08:00

8.1 KiB
Raw Permalink Blame History

Phase 3: Mini App Shell + Geolocation - Context

Gathered: 2026-10-22 Status: Ready for planning

## Phase Boundary

The Telegram Mini App boots inside Telegram, validates its session through Phase 2's /api/session gate, acquires live location reliably across Android/iOS/Desktop/browser with a full fallback chain (Telegram-native → WebView → chat keyboard), and renders the user's position on the Rams basemap under Telegram light + dark themes. Every failure surface is deliberate (never blank); no coordinate persists anywhere. BackButton + safe-area groundwork land here; the observable PX-01/PX-03 behaviors are Phase 6's. Search/routes/guidance are later phases.

## Implementation Decisions

Mini App stack + repo layout

  • D-01: Frontend is TypeScript + Vite, no UI framework — MapLibre GL JS v6 bundled, interactivity via small focused modules (the mockups' pattern, productionized). This stack is the foundation Phases 4–6 build on.
  • D-02: App source lives in a web/ npm workspace beside theme/ and bot/; root package.json gains web:* delegate scripts. Theme is consumed as a build-time dependency (theme/dist/rams.css imported into the Vite build, not via CDN) — completing Phase 1's self-hosted/CDN-free decision for the app path.
  • D-03: Post-initData validation, the session token is held memory-only (module scope), never written to localStorage/sessionStorage/cookies. Revalidation happens on every fresh boot (Telegram revives webviews) and whenever the 900s TTL stalls mid-session. Research may stress-test this invariant across webview reload quirks, but the invariant itself is locked.

Location acquisition strategy

  • D-04: Primary chain: Telegram-native WebApp.getLocation() first (Android LocationManager with version-gated paths), fall back to navigator.geolocation inside the WebView, then the chat-keyboard "send location" fallback (bot-side; app consumes it). Research details the Android stale-fix and iOS WKWebView quirks — the CHAIN is locked, the per-link mechanics are research's.
  • D-05: Cadence is gated streaming: one-shot fixes during browsing/search; continuous watch only during guidance (Phase 6 builds the streaming state machine). Map recenters on each fix; the position marker always shows the freshest fix.
  • D-06: Denied/no-geolocation presents a deliberate Rams-styled denied state — explains why location matters, offers retry, points to Telegram's attach → Location path as the manual fallback (GEO-02). No silent banners.
  • D-07: Coordinates live in memory only while the app runs. Zero persistence across every storage API (localStorage, sessionStorage, cookies, IndexedDB, service-worker caches). This zero-persistence assertion is part of the phase's GEO-03 spot-check, stress-tested by research against every storage surface the app touches.

Shell UX + screens

  • D-08: Phase 3's screen surface is map-first and single: full-bleed Rams basemap, position dot, follow/recenter control, minimal status strip reflecting session/location state. Route lines, layer toggles, search furniture belong to later phases — nothing placeholder-shaped.
  • D-09: Boot choreography has staged deliberate states: validating session → acquiring location → live — each Rams-styled, none blank (extends the neutral-denied rule from PX-02).
  • D-10: Map survives theme flips Rams-constant (Phase 1 D-04 slots pay off); only shell surfaces re-theme via the Telegram theme params. Safe-area handling + BackButton wiring land here as infrastructure.

Dev/test harness

  • D-11: A Mock-Telegram harness for the daily loop: a local module injecting a fake window.Telegram.WebApp (dev-signed initData, BackButton events, theme params with light/dark switch, sample location streams) plus a runner page booting the real app off-Telegram in a plain browser. Real-device batteries are spent at the device-matrix exit gate, not during daily dev.
  • D-12: Local dev topology is wrangler dev on 8787 serving web/dist statics + /api/* + webhook with local KV simulation; remote staging used sparingly right before real-device verification sessions.

Worker static-asset wiring

  • D-13: Statics served by the Workers [assets] binding (wrangler config), from web/dist's build output; Worker fetch still owns /api/* + webhook first. Completes the one-deployable architecture from the roadmap.
  • D-14: Build inlines everything the app needs: CSS from theme, MapLibre, basemap style JSON (from theme/basemap/liberty-rams.json), self-hosted fonts copied at build time into web/dist. Tiles remain OpenFreeMap's endpoint at runtime; no CDN fallbacks.
  • D-15: Deployment stays one command (npm run deploy = build web → wrangler deploy with assets). Same ergonomics as Phase 2. Hashed filenames from Vite play naturally with Cloudflare's asset caching.

OpenCode's Discretion

  • Exact per-link mechanics of the location chain (Telegram API calls, permission bridging) — research's domain
  • Boot-state copy, exact mock initData signing mechanics, recenter-toggle visual details (within the Rams system)
  • assets config tuning (binding name, not_found_handling, asset prefixes)
  • How the reserved exit-gate Playwright matrix extends theme/scripts/screenshot.mjs
## Specific Ideas
  • The app should feel like the navigate_map_overview mockup distilled: calm, map-dominant, nothing decorative.
  • Allowed fallbacks compose as a chain, not a menu — user should perceive one honest app.
  • Mock harness borrow: dev initData signing already exists conceptually in bot/scripts (HTTP world), and the hand-rolled test runner pattern (theme) prevails.

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Phase scope + decisions

  • .planning/ROADMAP.md §Phase 3 — goal, 5 success criteria incl. device-matrix exit, research recommendation
  • .planning/REQUIREMENTS.md §GEO-01/02/03, PX-02 (this phase's requirements)
  • .planning/phases/02-bot-skeleton-initdata-security/02-CONTEXT.md — identity floor /api/session contract details (opaque token, 900s TTL, rotation)
  • .planning/phases/01-design-tokens-rams-foundation/01-CONTEXT.md — theme package contract (Rams-constant map, D-04 theme slots)

Codebase

  • theme/dist/rams.css, theme/src/components.css, theme/src/tokens.css — consumption surface
  • theme/basemap/liberty-rams.json — the map style to load
  • bot/src/webhook.ts, bot/src/kv/sessions.ts — the /api/session contract the web app calls
  • stitch_minimalist_route_navigator/navigate_map_overview/code.html — layout reference (read-only)
  • AGENTS.md — repo conventions

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • theme/dist/rams.css + components.css — the app imports these directly (build-time)
  • theme/basemap/liberty-rams.json + build.mjs — map style + derivation upstream of a loaded style object
  • theme/fonts/* self-hosted woff2 — copied at build time into web/dist
  • theme/scripts/screenshot.mjs — Playwright toolchain for exit-gate device-matrix extension
  • Phase 2's /api/session — the auth endpoint, deploy scripts + .dev.vars conventions

Established Patterns

  • npm workspaces with delegate scripts (theme/, bot/)
  • Hand-rolled node test runner (audit.test.mjs shape)
  • TDD discipline; zero-persistence privacy invariant (KV TTL-only)

Integration Points

  • web/dist → wrangler [assets] binding on the existing araharah-bot Worker
  • WebApp.getLocation() / navigator.geolocation → module that also feeds Phase 5's route snapping and Phase 6's guidance loop
  • BackButton jumps to full state graph in Phase 6

</code_context>

## Deferred Ideas
  • Scripted Playwright device-matrix spec (mock-based) — extend theme/scripts toolchain at the exit gate
  • Placeholder-shaped search drawer / route furniture — later phases by design
  • Real-device TestFlight/Play Beta channels — out of scope; device-matrix exit pass suffices for v1

Phase: 03-mini-app-shell-geolocation Context gathered: 2026-10-22