--- title: Telegram Mini App Flow tags: [flow, telegram, mini-app, auth, bilingual, RTL] related_models: ["[[User]]"] related_apis: ["POST /api/auth/telegram", "[[Auth API]]"] task: "5.4" --- > **Last updated:** 2026-06-03 > **Status:** IN PROGRESS — Task 5.4 (dependencies: 5.1 auth infra, 5.2 Telegram sign-in endpoint) > **Frontend branch:** `integrate-main-into-development` · v2.8.44 > **Entry point:** `src/sections/telegram/` · route `/telegram` # Telegram Mini App Flow End-to-end specification for the **Amaneh Telegram Mini App** — a fully self-contained marketplace shell surfaced inside Telegram's in-app browser via the WebApp SDK. Buyers and sellers can browse requests, create new escrow requests, review offer state, follow payments, and message each other without leaving Telegram. --- ## 1. Architecture Overview ``` Telegram Client └─ Mini App iframe (https://amn.gg/telegram) └─ TelegramMiniAppView ← shell orchestrator ├─ useTelegramLiveContext ← SDK probe + polling ├─ useTelegramLanguage ← EN / FA detection ├─ useTelegramAutoSignIn ← silent JWT exchange ├─ useTelegramMainButton ← native chrome sync ├─ useTelegramBackButton ← native chrome sync ├─ useTelegramHaptic ← haptic wrapper │ ├─ [state: loading] → TelegramLoadingState ├─ [state: unsupported] → TelegramUnsupportedState ├─ [state: unlinked] → TelegramUnlinkedState └─ [state: linked] ├─ TelegramHeader ├─ TelegramTabBar (Home / Requests / Chat / Account) │ ├─ TelegramHomeView ├─ TelegramRequestsView → TelegramRequestDetailView ├─ TelegramChatView → TelegramChatThreadView ├─ TelegramAccountView └─ [overlay] TelegramNewRequestView ``` The shell is a **single-page, no-router** design: all navigation (tabs, overlays, detail drilldowns) is pure React state in `TelegramMiniAppView`. `window.location.assign` is only used as a final escape hatch to the full web dashboard. --- ## 2. Launch Points | Entry | Mechanism | `startapp` context | |---|---|---| | Bot profile | User opens bot → taps "Open App" | none | | Menu button | Pinned button in any chat with the bot | none | | Inline button | Bot sends a card with an embedded button | `req_` | | Direct deep link | `https://t.me/AmanehBot/app?startapp=req_` | `req_` | | Web fallback | Browser at `/telegram` | none (unsupported state) | `startapp` / `tgWebAppStartParam` is read from either the WebApp SDK (`window.Telegram.WebApp`) or from URL query/hash params (for older Telegram clients that append them directly). --- ## 3. SDK Initialisation & Context Probe **File:** `src/utils/telegram-webapp.ts` · `getTelegramContext()` The function assembles a `TelegramContext` object from: 1. `window.Telegram.WebApp` — primary SDK surface (available when the app is opened inside Telegram). 2. URL query/hash fallback — `tgWebAppStartParam`, `tgWebAppData`, `tgWebAppVersion`, `tgWebAppPlatform` — used by older clients or during dev testing. **Fields extracted:** | Field | Source | Notes | |---|---|---| | `isMiniApp` | Any Telegram signal present | Drives unsupported vs unlinked state | | `initData` | `webApp.initData` or `tgWebAppData` URL param | HMAC-signed payload sent to `/api/auth/telegram` | | `initDataUnsafe` | `webApp.initDataUnsafe` | Client-side user identity (not trusted) | | `safeArea` | `contentSafeAreaInset` or `safe_area_insets` | Parsed to `{top, right, bottom, left}` in px | | `theme` | `webApp.themeParams` | Both camelCase and snake_case normalised | | `platform` | `webApp.platform` or URL param | e.g. `ios`, `android`, `tdesktop` | | `startParam` | `startapp` / `tgWebAppStartParam` / `start_param` | Deep-link context | | `isUnsupported` | `!webApp && Boolean(startParam)` | Partial signal — no SDK but has URL param | **Polling on mount** (`useTelegramLiveContext`): Telegram sometimes finishes injecting the WebApp object after the first React render. The hook re-probes at 0 ms, 100 ms, 500 ms, and 1000 ms after mount, and also re-probes on `hashchange` events (triggered by the native back-button on some platforms). --- ## 4. Shell State Machine `getTelegramStatus(context, hasWebAccount)` returns one of three states: ``` unsupported ─── !context.isMiniApp (opened in browser, not Telegram) unlinked ─────── isMiniApp && (!user || !telegramUser.id) (inside Telegram but no JWT session linked) linked ──────── isMiniApp && user && telegramUser.id (authenticated, full shell rendered) ``` State transitions occur on: - Auth session check completing (`loading → false`) - Telegram auto sign-in completing (`tgAuthLoading → false`) - Manual sign-in button tap (unlinked → linked) --- ## 5. Authentication Flow ### 5.1 Silent Auto Sign-In **Hook:** `useTelegramAutoSignIn` · **File:** `hooks/use-telegram-auto-sign-in.ts` On mount, if `context.isMiniApp && context.initData && !user`: 1. Exchange `initData` for a JWT by calling `signInWithTelegram({ initData })` → `POST /api/auth/telegram`. 2. On success, call `checkUserSession()` to refresh the auth context. 3. If the backend returns `isNewUser: true`, show `TelegramOnboardingSheet`. 4. A `useRef` deduplication guard (`attemptedInitDataRef`) prevents re-runs under React Strict Mode's double-effect behaviour. ### 5.2 Manual Sign-In (Unlinked State) When `initData` is present but auto sign-in failed (or hasn't run yet), `TelegramUnlinkedState` renders: - **Continue with Telegram** — calls the same `signIn()` function from `useTelegramAutoSignIn`. - **Sign in with email** — `window.location.assign(paths.auth.jwt.signIn)`. - **Create an account** — `window.location.assign(paths.auth.jwt.register)`. When `initData` is absent (accessed via a path that skips Telegram context), only the email/register buttons appear. ### 5.3 Backend Endpoint `POST /api/auth/telegram` — expects `{ initData: string }`. Backend verifies the HMAC using the Telegram bot token, extracts `user` from the payload, upserts a `User` record (`telegramId`, `telegramVerified: true`), and issues a JWT + refresh token. Returns `{ token, refreshToken, isNewUser }`. --- ## 6. Navigation Model All navigation is in-shell React state — no Next.js router is involved. ``` activeTab : 'home' | 'requests' | 'chat' | 'account' overlayScreen : 'new-request' | null openConversationId : string | null openRequestId : string | null ``` **Priority rendering** (first match wins): 1. `openConversationId` → `TelegramChatThreadView` 2. `openRequestId` → `TelegramRequestDetailView` 3. `overlayScreen === 'new-request'` → `TelegramNewRequestView` 4. `activeTab` → appropriate tab view **Back button** (Telegram native `BackButton`) dismisses in reverse priority order: chat thread → request detail → overlay → returns to `home` tab. `BackButton` visibility: shown whenever `state === 'linked'` and either an overlay/drilldown is active, or `activeTab !== 'home'`. `MainButton` visibility: hidden while any overlay is open. When visible: - **Linked** → "New Request" (opens `overlayScreen = 'new-request'`) - **Unlinked** → "Sign In" (navigates to the JWT sign-in page) Both chrome buttons are styled with the amaneh saffron palette (`color: #C2410C`, `text_color: #FFFFFF`) via `setParams` (WebApp SDK >= 6.1). --- ## 7. Supported Flows ### 7.1 Browse Requests (Requests Tab) - `TelegramRequestsView` fetches the user's purchase requests via `useTelegramMyRequests` (GET `/api/purchase-requests/my`). - Displays a skeleton loader, then a scrollable list of `TelegramRequestRow` items. - Each row shows: title, status chip, budget, creation date. - Tap → sets `openRequestId` → renders `TelegramRequestDetailView`. ### 7.2 Request Detail with Stepper - `TelegramRequestDetailView` fetches a single request via `useTelegramRequest`. - Renders `TelegramRequestStepper` — a visual timeline of the escrow status flow from `pending_payment` → `completed`. - `determineCurrentStepFromStatus` maps the current `status` to a step index. - Also renders: budget, description, creation date, category, urgency. - Dates formatted via `toLocaleDateString` with `fa-IR` locale for Persian. ### 7.3 Create New Request - `TelegramNewRequestView` is a full-screen overlay (not a routed page). - Form fields: title, description, category (fetched from `/api/categories`), budget min/max, urgency. - On submit: calls `createPurchaseRequest()` → POST `/api/purchase-requests`. - On success: closes overlay, switches `activeTab` to `'requests'`. - `MainButton` is hidden while the overlay is open (submit lives in the form itself). ### 7.4 Chat - `TelegramChatView` shows the user's active conversations via `useTelegramConversations`. - Tap a row → sets `openConversationId` → renders `TelegramChatThreadView`. - `TelegramChatThreadView` loads messages via `useTelegramChatThread`, renders `TelegramChatBubble` items, and includes `TelegramChatComposer` for sending. - Optimistic send: message appears immediately, confirmed/rolled back on API response. ### 7.5 Account - `TelegramAccountView` shows profile info (name, email, Telegram username, `telegramVerified` status), linked wallet (if any), and notification preferences. - Contains sign-out action and language toggle. --- ## 8. Bilingual Support (EN / FA) **Language detection priority** (`useTelegramLanguage`): 1. `localStorage` key `amn_tg_lang` — user's persisted manual selection. 2. `initDataUnsafe.user.language_code` — Telegram-reported language (`"fa"` or `"fa-IR"` → Persian). 3. Fallback → English. **Language toggle:** `TelegramLanguageToggle` in the header — two buttons `[ EN | فا ]`. On tap: haptic light + language switch + persist to `localStorage`. **RTL layout:** | Element | EN (LTR) | FA (RTL) | |---|---|---| | Root `dir` attribute | `ltr` | `rtl` | | Font family | IBM Plex Sans | Vazirmatn | | Arrow icons | `→` | `←` | | Text alignment | left | right (inherits from `dir`) | | Chip list wrap | left-to-right | right-to-left | Font size bumps for Persian: body 13 px → 14 px, labels 10 px → 11 px (Vazirmatn renders optically smaller). **Translation structure:** ```ts // src/sections/telegram/locales/en.ts + fa.ts const TR = { en: { loading, unsupported, unlinked, header, home, requests, chat, account, newRequest, tabs, main, onboarding, errors, displayName, dir }, fa: { /* same keys, Farsi strings, dir: 'rtl' */ }, }; ``` All JSX uses `t.
.` — no inline strings in components. --- ## 9. Design System **File:** `src/sections/telegram/constants.ts` · `src/sections/telegram/telegram-shell-css.ts` The Mini App has a distinct visual identity (cream/saffron Persian palette) that does not inherit from the main dashboard theme. All tokens are feature-scoped. **Palette:** `TG_PALETTE` | Token | Hex | Usage | |---|---|---| | `cream50` | `#FBF6EB` | Page background | | `ink900` | `#1C1410` | Primary text | | `ink600` | `#6B5D4E` | Secondary text / labels | | `saffron600` | `#C2410C` | Primary action, MainButton | | `saffron500` | `#D97757` | Hover states | | `pistachio700` | `#3D6B4F` | Success / released states | | `pomegranate700` | `#8E2424` | Error / disputed states | | `bgPage` | `#E7DFCB` | Shell outer background | **Fonts:** `TG_FONTS` — Source Serif 4 (headings), IBM Plex Sans (body LTR), Vazirmatn (body RTL), IBM Plex Mono (amounts/addresses). **CSS:** `buildTelegramShellCss()` injects a `