---
title: Telegram Mini App Flow
tags: [flow, telegram, mini-app, auth, bilingual, RTL, shop, cart]
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.94
> **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, shop seller templates, manage a cart, 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
            ├─ useTelegramCart          ← shared localStorage cart
            │
            ├─ [state: loading]         → TelegramLoadingState
            ├─ [state: unsupported]     → TelegramUnsupportedState
            ├─ [state: unlinked]        → TelegramUnlinkedState
            └─ [state: linked]
                 ├─ TelegramHeader
                 ├─ TelegramTabBar (Home / Shop / Requests / Chat / Account)
                 │
                 ├─ TelegramHomeView
                 ├─ TelegramShopView → TelegramSellerShopView
                 ├─ TelegramRequestsView → TelegramRequestDetailView
                 ├─ TelegramChatView → TelegramChatThreadView
                 ├─ TelegramAccountView
                 └─ [overlay] TelegramNewRequestView
                 └─ [overlay] TelegramNotificationsView
                 └─ [overlay] TelegramCartView
```

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_<requestId>` |
| Direct deep link | `https://t.me/AmanehBot/app?startapp=req_<id>` | `req_<requestId>` |
| 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' | 'shop' | 'requests' | 'chat' | 'account'
overlayScreen       : 'new-request' | 'notifications' | 'cart' | null
openConversationId  : string | null
openRequestId       : string | null
openSellerId        : string | null
```

**Priority rendering** (first match wins):

1. `openConversationId` → `TelegramChatThreadView`
2. `openRequestId` → `TelegramRequestDetailView`
3. `openSellerId` → `TelegramSellerShopView`
4. `overlayScreen === 'cart'` → `TelegramCartView`
5. `overlayScreen === 'notifications'` → `TelegramNotificationsView`
6. `overlayScreen === 'new-request'` → `TelegramNewRequestView`
7. `activeTab` → appropriate tab view

**Back button** (Telegram native `BackButton`) dismisses in reverse priority order: chat thread → request detail → seller shop → 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. Tab Structure

The shell has **five bottom tabs** rendered by `TelegramTabBar`:

| Tab | Icon | View | Purpose |
|---|---|---|---|
| Home | house | `TelegramHomeView` | Welcome banner, quick-action cards, escrow-state chips |
| Shop | storefront | `TelegramShopView` | Sellers list; drill into seller store; add templates to cart |
| Requests | list | `TelegramRequestsView` | User's escrow requests with status stepper |
| Chat | speech bubble | `TelegramChatView` | Conversation list + support entry |
| Account | person | `TelegramAccountView` | Profile, preferences, links to web dashboard sections |

`handleTabSelect` clears all overlays and drill-down IDs before switching tab.

---

## 8. Supported Flows

### 8.1 Home Tab

`TelegramHomeView` is the landing screen shown on first open. It contains:
- **Welcome banner** (`TelegramWelcomeBanner`): escrow account summary, primary CTA.
- **Quick-action cards** (`TelegramQuickActions`): shortcuts to Requests, Payments, Chat.
- **Escrow state chips** (`TelegramEscrowStateChips`): legend of status values visible in the platform.

### 8.2 Shop Tab — Sellers List

**`TelegramShopView`** (`telegram-shop-view.tsx`):
- Fetches all sellers via `useTelegramShops()` → SWR wrapping `getTemplateSellers()` → `GET /api/request-templates/sellers`.
- Renders `TelegramShopRow` per seller: avatar, name, rating, template count, sales count.
- Shows a floating cart badge button in the header when `totalItems > 0`; tap opens `overlayScreen = 'cart'`.
- Tap a seller row → sets `openSellerId` → navigates to `TelegramSellerShopView`.

### 8.3 Shop Tab — Seller Store

**`TelegramSellerShopView`** (`telegram-seller-shop-view.tsx`):
- Fetches seller + active templates via `useTelegramSellerShop(sellerId)` → `GET /api/request-templates/sellers/:id`.
- Dark header: seller avatar, name, rating, template count, description.
- Each template card shows: image, title, 2-line description, budget range, usage count.
- **Two actions per template:**
  - **Add to cart / Remove from cart** — toggles item in `useTelegramCart` (localStorage, no API). Button is filled blue when not in cart, outline when added.
  - **Order this template** — `<a href>` to `/dashboard/request/from-template?shareableLink=...`. Exits the Mini App to the web dashboard (single-template direct order, bypasses cart).
- Floating "Cart · N templates" sticky button at bottom when `totalItems > 0`; tap calls `onOpenCart()`.

### 8.4 Shopping Cart Overlay

**`TelegramCartView`** (`telegram-cart-view.tsx`):
- Rendered as `overlayScreen = 'cart'`; dismissed by Telegram BackButton.
- Lists each cart item: image, name, seller name, USDT price × quantity, +/− quantity controls, remove button.
- Subtotal/total in USDT, locale-formatted (`fa-IR` for Persian, `en-US` for English); amounts always `dir="ltr"`.
- **"Continue to payment"** — plain `<a href={paths.shops.checkout}>` link; exits Mini App to web checkout.

**Cart storage (`useTelegramCart`):**

- Reads/writes `localStorage` key **`app-request-template-checkout`** — the same key the web `RequestTemplateCheckoutProvider` reads. This is the cart handoff mechanism: the cart built in Telegram IS the cart the web checkout page hydrates.
- Dispatches a custom `tg-cart-changed` DOM event on every write; listens on both that event and the native `storage` event so all open tabs stay in sync.
- Operations: `addTemplate(template, seller)`, `removeItem(itemId)`, `changeQuantity(itemId, qty)`, `isInCart(templateId)`.
- No API calls — cart is purely client-side until checkout.
- Cart item model: `id`, `templateId`, `name`, `description`, `price` (from `template.budget.min`), `quantity`, `image`, `sellerId`, `sellerName`, `category`, `shareableLink`, `deliveryInfo`, `maxUsage`, `usageCount`, `remainingCapacity`.

### 8.5 Web Checkout Handoff

Destination: `/dashboard/shops/checkout` — `RequestTemplateCheckoutView` wrapped by `RequestTemplateCheckoutProvider`.

The provider reads the shared `localStorage` key and hydrates the TMA cart. The checkout is a 3-step stepper:

| Step | Component | Description |
|---|---|---|
| 0 (Cart review) | `RequestTemplateCheckoutCart` | Item list, quantities, remove, totals, discount/shipping |
| 1 (Address) | `RequestTemplateCheckoutBillingAddress` | Physical address or online delivery email |
| 2 (Payment) | `RequestTemplateCheckoutPayment` | Wallet payment + socket confirmation |
| Complete | `RequestTemplateCheckoutOrderComplete` | Confirmation dialog, cart reset |

Payment execution calls `convertTemplatesToRequests()` to create escrow records, then awaits a `template-checkout-payment-confirmed` socket event. A guard checks `createdRequestIds` is non-empty before advancing (prevents stray global socket events from triggering premature completion). Stock validation clamps or removes items exceeding `remainingCapacity` before payment.

### 8.6 Browse Requests (Requests Tab)

- `TelegramRequestsView` fetches the user's purchase requests via `useTelegramMyRequests` (GET `/api/requests`).
- 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`.

### 8.7 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.

### 8.8 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).

### 8.9 Chat Tab

- `TelegramChatView` shows the user's active conversations via `useTelegramConversations`.
- Includes a Support row that calls `createSupportChat()` → `POST /api/chat/support`, then opens `TelegramChatThreadView` with the returned conversation ID.
- Tap a conversation 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.
- Real-time updates via Socket.IO events; SWR is mutated on `new-notification` and `unread-count-update` events.

### 8.10 Account Tab

**`TelegramAccountView`** (`telegram-account-view.tsx`):

The account tab has four sections. All user data is passed as props from the shell (loaded via `useAuthContext()` — no fetch on mount).

**Profile header:**
- Avatar (from `user.profile.avatar`, falls back to initials), full name, Telegram `@username`, role chip (buyer / seller / admin / resolver / guard).
- Verification chips: "Telegram Verified" (if `user.telegramVerified`) and "Email Verified" (if `user.isEmailVerified`).

**Preferences section:**
- Language toggle (FA / EN, in-shell via `TelegramLanguageToggle`).
- General Settings → `/dashboard/account` (web, labeled "Opens in the web dashboard").
- Wallet → truncated address (`0x1234…abcd`) or "not connected" → `/dashboard/account/wallet` (web).
- Notifications → opens `TelegramNotificationsView` overlay in-shell.
- Addresses → `/dashboard/account/address` (web).
- Passkey → `/dashboard/account/passkey` (web).

**Help section:**
- Support → `createSupportChat()` → opens `TelegramChatThreadView` in-shell.
- Terms & Conditions → placeholder, "coming soon".

**Session section:**
- Sign Out → `TelegramBottomSheet` confirmation dialog → `authSignOut()` + `window.location.assign(paths.auth.jwt.signIn)`.

### 8.11 Notifications Overlay

- `TelegramNotificationsView` is rendered as `overlayScreen = 'notifications'`.
- Fetches via `useTelegramNotifications` → `getNotifications(userId, 1, 50)` → `GET /api/notifications?userId=...&page=1&limit=50`.
- Real-time updates: Socket.IO events `new-notification`, `unread-count-update` trigger SWR mutate.
- "Mark all read" calls `markAllNotificationsAsRead(userId)` → `PATCH /api/notifications/mark-all-read`.

---

## 9. API Calls

| Action | Hook / call | Backend endpoint |
|---|---|---|
| Auto sign-in | `useTelegramAutoSignIn` → `signInWithTelegram({initData})` | `POST /api/auth/telegram` |
| Sellers list | `useTelegramShops` → `getTemplateSellers()` | `GET /api/request-templates/sellers` |
| Seller + templates | `useTelegramSellerShop` → `getSellerWithTemplates(id)` | `GET /api/request-templates/sellers/:id` |
| Marketplace sellers | `useTelegramSellers` → `getSellers()` | `GET /api/marketplace/sellers` |
| My requests | `useTelegramMyRequests` | `GET /api/requests` |
| Single request | `useTelegramRequest` | `GET /api/purchase-requests/:id` |
| Create request | shell → `createPurchaseRequest()` | `POST /api/purchase-requests` |
| Conversations | `useTelegramConversations` | `GET /api/chat/conversations` |
| Chat thread | `useTelegramChatThread` | `GET /api/chat/:id` + Socket.IO real-time |
| Support chat | `createSupportChat()` | `POST /api/chat/support` |
| Notifications | `useTelegramNotifications` | `GET /api/notifications?userId=...&page=1&limit=50` |
| Mark all read | `markAllNotificationsAsRead(userId)` | `PATCH /api/notifications/mark-all-read` |
| Auth sign-out | `authSignOut()` | JWT sign-out endpoint |

Cart operations (add/remove/quantity) are **pure localStorage** — no API calls until web checkout.

---

## 10. Bilingual Support (EN / FA)

**Language detection priority** (`useTelegramLanguage`):

1. `?lang=` URL query param — dev preview override.
2. `localStorage` key `amn_tg_lang` — user's persisted manual selection.
3. `initDataUnsafe.user.language_code` — Telegram-reported language (`"fa"` or `"fa-IR"` → Persian).
4. 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 |
| Amounts | always `dir="ltr"` | always `dir="ltr"` |

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, shop, requests,
        chat, account, newRequest, tabs, main, onboarding, errors, displayName, dir },
  fa: { /* same keys, Farsi strings, dir: 'rtl' */ },
};
```

All JSX uses `t.<section>.<key>` — no inline strings in components.

---

## 11. 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 `<style>` tag at shell root with all class utilities (`.tg-chip`, `.tg-shell`, `.tg-tab-bar`, `.tg-header`, etc.). Theme CSS variables (`--cream-50`, `--ink-900`, etc.) are set on `.tg-shell` root.

**Safe area:** `getTelegramSafeAreaStyle(safeArea)` maps the Telegram-reported safe area insets to CSS padding using `max(${px}px, env(safe-area-inset-*))` to handle both Telegram-native and iOS/Android safe areas.

---

## 12. Telegram SDK Usage Patterns

### 12.1 Safe-Area Inset

```ts
// TelegramContext.safeArea = { top, right, bottom, left } (px)
// Source: webApp.contentSafeAreaInset || webApp.safe_area_insets
// Normalised to number via parseNumber() — rejects non-finite strings
const topInset = (context.safeArea?.top ?? 0) as number;
```

All views receive `topInset` / `bottomInset` props and add them as explicit `paddingTop` / `paddingBottom` to avoid content being obscured by the Telegram chrome.

### 12.2 Haptic Feedback

```ts
// useTelegramHaptic(webApp) → haptic('light' | 'medium')
webApp?.HapticFeedback?.impactOccurred?.(type)
```

Used on: tab switches (light), new-request CTA (medium), language toggle (light), back button (light). All calls are wrapped in try/catch — the API may be absent on older clients.

### 12.3 Back Button

```ts
useTelegramBackButton({ webApp, isVisible, onClick })
// Calls webApp.BackButton.show() / hide() and registers onClick handler
// Cleanup: offClick() on unmount / visibility change
```

### 12.4 Main Button

```ts
useTelegramMainButton({ webApp, isReady, text, onClick })
// Calls webApp.MainButton.show() / hide(), setText(), setParams()
// Saffron palette: color: '#C2410C', text_color: '#FFFFFF'
// setParams requires WebApp >= 6.1; silent fallback for older clients
```

### 12.5 Theme Integration

Telegram's `themeParams` is normalised (both camelCase and snake_case accepted) and injected as CSS custom properties on the shell root (`--telegram-shell-bg`, `--telegram-shell-text`, etc.). The amaneh palette overrides these for the Mini App's own UI, but components can reference them for adaptive behaviours.

---

## 13. Edge Cases

| Scenario | Detection | Handling |
|---|---|---|
| Opened in browser (not Telegram) | `context.isMiniApp === false` | `TelegramUnsupportedState` — shows "Open in Telegram" badge, web dashboard link |
| Partial Telegram signal (URL params but no SDK) | `!webApp && Boolean(startParam)` → `isUnsupported: true` | Same unsupported state |
| Telegram SDK injected late | `useTelegramLiveContext` polls at 0/100/500/1000 ms | Re-probes until SDK is ready; seed context bypasses polling |
| `initData` absent (no auth data) | `!context.initData` in unlinked state | Sign-in button triggers error string `t.errors.no_init_data`; email/create buttons remain available |
| Auto sign-in replay (React Strict Mode) | `attemptedInitDataRef.current === context.initData` | Deduplication ref — second effect is a no-op |
| Backend sign-in failure | Catch block in `useTelegramAutoSignIn` | Error string displayed in `TelegramUnlinkedState`; retry via "Continue with Telegram" |
| New user first login | `result.isNewUser === true` | `TelegramOnboardingSheet` shown over the shell; dismissed to account settings or "Later" |
| Expired session inside Mini App | Auth context `user === null` after session check | Shell falls back to `unlinked` state |
| Old Telegram client (< 6.1) | `setParams` throws | Try/catch silences it; button shows without saffron colour |
| RTL + keyboard overlap | Viewport shrinks on soft keyboard open | `flex: 1` + `overflowY: auto` on content area; bottom safe-area inset on tab bar |
| Persian locale date formatting | `lang === 'fa'` | `toLocaleDateString('fa-IR', ...)` in `formatDate` helper |
| Cart cross-tab sync | Multiple tabs / Mini App + web | `tg-cart-changed` DOM event + `storage` event both trigger re-render |
| Template at capacity | `remainingCapacity === 0` at checkout | Stock validation clamps/removes over-capacity items before payment |
| Stray global socket on checkout | `template-checkout-payment-confirmed` fires unexpectedly | Guard checks `createdRequestIds.length > 0` before advancing to completion step |

---

## 14. File Map

```
src/
  app/telegram/page.tsx                        # Next.js route (thin shell, no auth guard)
  utils/telegram-webapp.ts                     # SDK probe, context types, shell style helpers
  sections/telegram/
    constants.ts                               # TG_PALETTE, TG_FONTS, TG_EASE, status maps
    telegram-shell-css.ts                      # buildTelegramShellCss() — inlined CSS blob
    index.ts                                   # barrel
    locales/
      types.ts                                 # TelegramDict, TelegramLang, TelegramTabId
      en.ts                                    # English strings
      fa.ts                                    # Persian strings
      index.ts                                 # getTelegramDict(lang)
    hooks/
      use-telegram-live-context.ts             # SDK polling
      use-telegram-language.ts                 # EN/FA detection + ?lang= + localStorage persist
      use-telegram-auto-sign-in.ts             # initData → JWT exchange
      use-telegram-main-button.ts              # MainButton lifecycle
      use-telegram-back-button.ts              # BackButton lifecycle
      use-telegram-haptic.ts                   # HapticFeedback wrapper
      use-telegram-cart.ts                     # localStorage cart (shared with web checkout)
      use-telegram-shops.ts                    # GET /api/request-templates/sellers
      use-telegram-seller-shop.ts              # GET /api/request-templates/sellers/:id
      use-telegram-sellers.ts                  # GET /api/marketplace/sellers
      use-telegram-my-requests.ts              # GET /api/requests
      use-telegram-request.ts                  # GET /api/purchase-requests/:id
      use-telegram-conversations.ts            # Chat conversation list
      use-telegram-chat-thread.ts              # Chat thread + optimistic send
      use-telegram-notifications.ts            # GET /api/notifications
      index.ts
    view/
      telegram-mini-app-view.tsx               # Shell orchestrator (all state lives here)
      telegram-home-view.tsx                   # Home tab
      telegram-shop-view.tsx                   # Shop tab — sellers list
      telegram-seller-shop-view.tsx            # Seller store drill-down + cart actions
      telegram-cart-view.tsx                   # Cart overlay
      telegram-requests-view.tsx               # Requests list tab
      telegram-request-detail-view.tsx         # Request drilldown + stepper
      telegram-new-request-view.tsx            # New request overlay form
      telegram-chat-view.tsx                   # Chat conversation list tab
      telegram-chat-thread-view.tsx            # Chat thread drilldown
      telegram-account-view.tsx                # Account + preferences + sign-out tab
      telegram-notifications-view.tsx          # Notifications overlay
      index.ts
    components/
      telegram-header.tsx                      # AMN logo + subtitle + language toggle
      telegram-tab-bar.tsx                     # Bottom tab bar (5 tabs)
      telegram-welcome-banner.tsx              # Home: escrow account banner + CTA
      telegram-quick-actions.tsx               # Home: action cards (Requests / Payments / Chat)
      telegram-escrow-state-chips.tsx          # Home: status chip legend
      telegram-shop-row.tsx                    # Shop: seller list row
      telegram-request-row.tsx                 # Requests: list row
      telegram-request-stepper.tsx             # Detail: visual escrow timeline
      telegram-list-row.tsx                    # Generic list row primitive
      telegram-list-skeleton.tsx               # Skeleton loader for lists
      telegram-chat-row.tsx                    # Chat: conversation list row
      telegram-chat-bubble.tsx                 # Chat: message bubble
      telegram-chat-composer.tsx               # Chat: message input
      telegram-loading-state.tsx               # Loading spinner state
      telegram-unlinked-state.tsx              # Unlinked / sign-in prompt state
      telegram-unsupported-state.tsx           # Not-in-Telegram fallback state
      telegram-onboarding-sheet.tsx            # New-user onboarding bottom sheet
      telegram-empty-state.tsx                 # Generic empty list state
      telegram-language-toggle.tsx             # EN | FA header toggle
      telegram-bottom-sheet.tsx                # Generic bottom sheet primitive
      telegram-form-field.tsx                  # Form field + input style helper
      telegram-seal-mark.tsx                   # SealMark logo component
      telegram-icons.tsx                       # Telegram-scoped icon set
      index.ts
```

---

## 15. Current Implementation Status (v2.8.94)

| Area | Status | Notes |
|---|---|---|
| Shell + state machine | Done | `TelegramMiniAppView` — all states wired |
| SDK probe + live context | Done | Polling + hashchange listener |
| Auto sign-in | Done | Deduped initData exchange |
| Manual sign-in (unlinked) | Done | Email + create account fallbacks |
| Bilingual EN/FA | Done | Full string inventory, RTL layout, Vazirmatn font |
| Language toggle | Done | Header toggle + localStorage persist |
| `?lang=` dev preview param | Done | URL param override added to `useTelegramLanguage` |
| Home tab | Done | Banner + quick actions + state chips |
| Shop tab — sellers list | Done | API-backed with skeleton + empty states, cart badge |
| Shop tab — seller store | Done | Templates list, add/remove cart, direct order link |
| Shopping cart (localStorage) | Done | Shared key with web checkout; cross-tab sync |
| Cart overlay | Done | Quantity controls, remove, total, checkout link |
| Web checkout handoff | Done | localStorage handoff; stock guard; socket guard |
| Requests list | Done | API-backed with skeleton + empty states |
| Request detail + stepper | Done | Status timeline, budget, dates with fa-IR locale |
| New request form | Done | In-shell overlay, category fetch, validation |
| Chat list | Done | API-backed conversation list + support row |
| Chat thread | Done | Messages + optimistic send + Socket.IO real-time |
| Account tab | Done | Profile, preferences, help, web-dashboard links, sign-out |
| Notifications overlay | Done | API-backed; Socket.IO real-time; mark-all-read |
| Telegram chrome (MainButton / BackButton) | Done | Saffron palette, lifecycle hooks |
| Haptic feedback | Done | All tap interactions |
| Safe area insets | Done | Normalised from SDK + CSS env() fallback |
| Deep link `startapp` context | Partial | Parsed but not yet used to auto-navigate to a request |
| Bilingual onboarding sheet | Done | Shown on `isNewUser` flag |
| Unsupported / browser fallback | Done | Web dashboard link |

### Open Items

- `startapp` deep link routing: if `context.startParam` matches `req_<id>`, auto-open `TelegramRequestDetailView` on first render.
- Backend room-scoped Socket.IO for real-time chat updates (global socket event broadcast was fixed client-side in v2.8.4; server-side scoping is a follow-up).

---

## 16. Related Documents

- [[PRD - Telegram Mini App Bilingual (EN + FA)]] — bilingual string inventory and RTL layout spec
- [[PRD - Telegram Phone Number Authentication]] — phone-number auth as a future sign-in path
- [[Authentication Flow]] — JWT lifecycle shared with the Mini App auth
- [[Purchase Request Flow]] — escrow state machine surfaced in the stepper
- [[Chat Flow]] — real-time messaging that the Mini App embeds
- [[Request Template Checkout]] — web checkout flow that the Mini App cart hands off to
