--- title: Telegram Mini App Flow tags: [flow, telegram, mini-app, auth, bilingual, RTL, shop, cart, payment] related_models: ["[[User]]"] related_apis: ["POST /api/auth/telegram", "[[Auth API]]"] task: "5.4" --- > **Last updated:** 2026-06-08 > **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. > **Two separate Mini Apps exist on this platform.** This document covers the **main marketplace Mini App** (`amn.gg/telegram`) built inside the primary Next.js frontend. For the AI-assisted request-creation Mini App, see [[amanat-assist]]. --- ## 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 (disabled) ├─ useTelegramBackButton ← native chrome sync ├─ useTelegramHaptic ← haptic wrapper ├─ useTelegramCart ← shared localStorage cart ├─ useTelegramNotifications ← unread badge count │ ├─ [state: loading] → TelegramLoadingState ├─ [state: unsupported] → TelegramUnsupportedState ├─ [state: unlinked] → TelegramUnlinkedState └─ [state: linked] ├─ TelegramHeader ├─ TelegramTabBar (Home / Shop / Requests / Chat / Account) │ ├─ [drilldown] TelegramPaymentView ← highest priority ├─ [drilldown] TelegramChatThreadView ├─ [drilldown] TelegramRequestDetailView ├─ [drilldown] TelegramTemplateDetailView ├─ [drilldown] TelegramSellerShopView │ ├─ [overlay] TelegramPointsView ├─ [overlay] TelegramSettingsView ├─ [overlay] TelegramAddressesView ├─ [overlay] TelegramCartView ├─ [overlay] TelegramCheckoutView ├─ [overlay] TelegramNotificationsView ├─ [overlay] TelegramNewRequestView │ ├─ TelegramHomeView ├─ TelegramShopView → TelegramSellerShopView ├─ TelegramRequestsView → TelegramRequestDetailView ├─ TelegramChatView → TelegramChatThreadView └─ TelegramAccountView ``` 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 used only as a final escape hatch to external URLs. `openTelegramExternalLink` is used for deep links into the web dashboard, which opens inside Telegram's WebView or an external browser depending on the Telegram client. --- ## 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. amanat-assist vs Main Mini App Two distinct Telegram Mini Apps exist for this platform: | Property | Main Mini App (this doc) | amanat-assist | |---|---|---| | URL | `amn.gg/telegram` | `assist.amn.gg` | | Bot | AmanehBot | AmanehBot (same) | | Codebase | `frontend/` (Next.js, `src/sections/telegram/`) | `/amanat-assist` (React + Vite, separate repo) | | Purpose | Full marketplace shell: browse, buy, sell, chat, manage account | Conversational LLM wizard to create one purchase request | | LLM | None | Mistral → DeepSeek fallback (via `amanat-llm-proxy` on port 3001) | | Backend access | Direct calls to `api.amn.gg` | Proxied through `amanat-llm-proxy` which holds the LLM API keys | | Auth | Telegram `initData` → `POST /api/auth/telegram` | Same endpoint; also supports web redirect via `?access_token=` | | Deep links between apps | Main Mini App has "New Request" overlay with an "Open Assist" CTA that navigates `window.location.href` to `assist.amn.gg?access_token=...` | Assist submits the finished request then the user returns to the main app | | Status | In production | Live at `assist.amn.gg` v1.1.0 | **Hand-off from main app to assist:** `handleOpenAssist()` in `TelegramMiniAppView` constructs a URL to `https://assist.amn.gg` with `access_token`, `user_json`, `theme`, and `source=miniapp` query params. `window.location.href` is used (not `openLink`) to keep the navigation inside Telegram's WebView rather than opening Safari on iOS. --- ## 4. 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). --- ## 5. 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) --- ## 6. Authentication Flow ### 6.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. ### 6.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. ### 6.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 }`. Registered at `authRoutes.ts` line 24: `router.post("/telegram", ctrl.telegramAuth.bind(ctrl))` — public route, no auth middleware. ### 6.4 Session Linking (Telegram ↔ Amaneh Account) The `POST /api/auth/telegram` endpoint both creates and links accounts: - **New Telegram user, no existing Amanat account:** a new `User` is created with `telegramId` set; `isNewUser: true` is returned and the onboarding sheet is shown. - **Existing Amanat account with the same `telegramId`:** the existing user is returned; session continues. - **Existing Amanat account that has never used Telegram:** `telegramId` and `telegramVerified: true` are written onto the existing record (matched by Telegram user id). After the JWT is issued the standard `checkUserSession()` re-hydrates the React auth context. The Mini App shell reads `user.telegramVerified` and `user.isEmailVerified` from this context to render verification chips in the Account tab. --- ## 7. 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' | 'checkout' | 'points' | 'settings' | 'addresses' | null openConversationId : string | null openRequestId : string | null openPaymentRequestId : string | null ← payment drilldown (highest priority) paymentCheckoutFlow : boolean ← true when reached from shop checkout openSellerId : string | null openTemplate : { template, seller } | null ``` **Priority rendering** (first match wins): 1. `openPaymentRequestId` → `TelegramPaymentView` ← new, highest priority 2. `openConversationId` → `TelegramChatThreadView` 3. `openRequestId` → `TelegramRequestDetailView` 4. `openTemplate` → `TelegramTemplateDetailView` ← new 5. `openSellerId` → `TelegramSellerShopView` 6. `overlayScreen === 'points'` → `TelegramPointsView` ← new 7. `overlayScreen === 'settings'` → `TelegramSettingsView` ← new 8. `overlayScreen === 'addresses'` → `TelegramAddressesView` ← new 9. `overlayScreen === 'cart'` → `TelegramCartView` 10. `overlayScreen === 'checkout'` → `TelegramCheckoutView` ← new (replaces web handoff) 11. `overlayScreen === 'notifications'` → `TelegramNotificationsView` 12. `overlayScreen === 'new-request'` → `TelegramNewRequestView` 13. `activeTab` → appropriate tab view **Back button** (Telegram native `BackButton`) dismisses in reverse priority order: - Payment drilldown → if `paymentCheckoutFlow`, steps back to cart; otherwise clears payment state. - Chat thread → clears `openConversationId`. - Request detail → clears `openRequestId`. - Template detail → clears `openTemplate`. - Seller shop → clears `openSellerId`. - Overlay (`checkout` steps back to `cart`) → clears `overlayScreen`. - Non-home tab → returns to `home`. `BackButton` visibility: shown whenever `state === 'linked'` and either an overlay/drilldown is active, or `activeTab !== 'home'`. `MainButton` visibility: **intentionally disabled** (`isReady: false`) — the native Telegram MainButton cannot use the project font and duplicates in-shell CTAs, so it is kept hidden. All primary actions live inside the shell UI itself. Both chrome buttons retain the amaneh saffron palette (`color: #C2410C`, `text_color: #FFFFFF`) via `setParams` (WebApp SDK >= 6.1) as a fallback should the MainButton ever be re-enabled. --- ## 8. 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. --- ## 9. Supported Flows ### 9.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. - **"New Request" CTA** → opens `overlayScreen = 'new-request'`. - **"Open Assist" CTA** → calls `handleOpenAssist()` to navigate to `assist.amn.gg` in the same WebView (see section 3). ### 9.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 (`TelegramCartFab`) in the header when `totalItems > 0`; tap opens `overlayScreen = 'cart'`. - Tap a seller row → sets `openSellerId` → navigates to `TelegramSellerShopView`. ### 9.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). - **View template details** — sets `openTemplate` → navigates to `TelegramTemplateDetailView`. - Floating "Cart · N templates" sticky button at bottom when `totalItems > 0`; tap calls `onOpenCart()`. ### 9.4 Shop Tab — Template Detail **`TelegramTemplateDetailView`** (`telegram-template-detail-view.tsx`): - Full-screen view of a single template. - Shows full description, seller info, price, delivery info, usage/capacity counters. - Add/remove cart action; direct "Order this template" link to `/dashboard/request/from-template?shareableLink=...` (exits to web dashboard). - Back button returns to the seller store (`openTemplate` cleared, `openSellerId` retained). ### 9.5 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"** → calls `onCheckout()` which sets `overlayScreen = 'checkout'` (in-shell checkout, replacing the previous web handoff). **Cart storage (`useTelegramCart`):** - Reads/writes `localStorage` key **`app-request-template-checkout`** — the same key the web `RequestTemplateCheckoutProvider` reads. This enables the web dashboard checkout to hydrate the same cart. - 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. ### 9.6 In-Shell Checkout Overlay **`TelegramCheckoutView`** (`telegram-checkout-view.tsx`): - Rendered as `overlayScreen = 'checkout'`; BackButton steps back to `overlayScreen = 'cart'`. - A 3-step stepper running entirely inside the Mini App shell: - **Step 0 (Cart review):** item list, quantities, totals, discount. - **Step 1 (Address):** physical address or online delivery email. - **Step 2 (Payment):** wallet-based payment execution. - On successful order (`onPlaced(reqId)` callback): - If a `reqId` is returned, sets `paymentCheckoutFlow = true` and `openPaymentRequestId = reqId` → immediately opens the payment view. - If no `reqId`, switches `activeTab` to `'requests'`. - Stock validation clamps or removes items exceeding `remainingCapacity` before payment. - Integrates with `onManageAddresses()` to open the `addresses` overlay mid-flow. ### 9.7 Payment View (In-Shell) **`TelegramPaymentView`** (`telegram-payment-view.tsx`): - Highest-priority drilldown (rendered before all other overlays). - Loaded for a specific `requestId`. Used from two entry points: - **Shop checkout flow** (`paymentCheckoutFlow = true`): after `TelegramCheckoutView` creates the requests. Shows a 3-step progress header (cart → address → payment). - **Requests tab** (`paymentCheckoutFlow = false`): buyer taps "Pay" on an existing request. No progress header. - Fetches request details via `useTelegramRequest`. - Fetches offers via `useTelegramOffers`. - Calls `getPaymentOptions()` → `GET /api/payment/options` and `createDirectBalanceIntent()` → `POST /api/payment/direct-balance`. - Polls `checkDirectBalancePayment()` for confirmation. - On successful payment: calls `onPaid()` → clears `openPaymentRequestId`, switches to `activeTab = 'requests'`. - Back button: if `paymentCheckoutFlow`, steps back to `overlayScreen = 'cart'`; otherwise clears the payment state. ### 9.8 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`. ### 9.9 Request Detail with Stepper and Offers - `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. - **Offer review:** fetches offers via `useTelegramOffers`; renders offer cards with seller info, price, and accept/reject actions. - **Pay action:** renders a "Pay" button when request is in a payable state → calls `onPay(id)` → sets `openPaymentRequestId`. - **Web fallback:** "View full details" → `openTelegramExternalLink(context.webApp, path)`. - **Chat seller:** taps the seller chat icon → calls `onChatSeller(sellerId)` → `createConversation` + sets `openConversationId`. - Role-aware: `role` prop is `'seller'` or `'buyer'` based on `user.role`. - Dates formatted via `toLocaleDateString` with `fa-IR` locale for Persian. ### 9.10 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. - Includes an **"Open Assist"** button that delegates to `handleOpenAssist()` for users who prefer the conversational LLM flow. - On submit: calls `createPurchaseRequest()` → POST `/api/purchase-requests`. - On success: closes overlay, switches `activeTab` to `'requests'`. ### 9.11 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. ### 9.12 Account Tab **`TelegramAccountView`** (`telegram-account-view.tsx`): **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`). - **Settings** → opens `overlayScreen = 'settings'` (in-shell `TelegramSettingsView`). - **Points** → opens `overlayScreen = 'points'` (in-shell `TelegramPointsView`). - Wallet → truncated address (`0x1234…abcd`) or "not connected" → `/dashboard/account/wallet` (web via `openTelegramExternalLink`). - Notifications → opens `TelegramNotificationsView` overlay in-shell. - **Addresses** → opens `overlayScreen = 'addresses'` (in-shell `TelegramAddressesView`). - 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)`. ### 9.13 Settings Overlay **`TelegramSettingsView`** (`telegram-settings-view.tsx`): - Rendered as `overlayScreen = 'settings'`. - Allows editing profile fields (name, bio) in-shell. - On save: calls `onSaved()` which triggers `checkUserSession()` to refresh the auth context. ### 9.14 Addresses Overlay **`TelegramAddressesView`** (`telegram-addresses-view.tsx`): - Rendered as `overlayScreen = 'addresses'`. - Fetches addresses via `use-telegram-addresses.ts`. - Used both from the Account tab and as a mid-flow step from `TelegramCheckoutView`. ### 9.15 Points Overlay **`TelegramPointsView`** (`telegram-points-view.tsx`): - Rendered as `overlayScreen = 'points'`. - Fetches user points via `use-telegram-points.ts`. - Shows points balance and transaction history. ### 9.16 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`. - Unread count is also surfaced in the `TelegramHeader` bell icon badge. --- ## 10. 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` | | Offers for request | `useTelegramOffers` → `getOffers(requestId)` | `GET /api/marketplace/offers?requestId=...` | | Payment options | `getPaymentOptions()` | `GET /api/payment/options` | | Create payment intent | `createDirectBalanceIntent()` | `POST /api/payment/direct-balance` | | Poll payment status | `checkDirectBalancePayment()` | `GET /api/payment/:id` | | Update request status | `updateRequestStatus()` | `PATCH /api/marketplace/requests/:id/status` | | Conversations | `useTelegramConversations` | `GET /api/chat/conversations` | | Chat thread | `useTelegramChatThread` | `GET /api/chat/:id` + Socket.IO real-time | | Support chat | `createSupportChat()` | `POST /api/chat/support` | | Direct conversation | `createConversation({ type: 'direct', participantIds })` | `POST /api/chat/conversations` | | 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 | | Addresses | `use-telegram-addresses.ts` | `GET /api/user/addresses` | | Points | `use-telegram-points.ts` | `GET /api/user/points` | Cart operations (add/remove/quantity) are **pure localStorage** — no API calls until checkout. --- ## 11. 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.
.` — no inline strings in components. --- ## 12. 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 | | `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 `