docs: add sub-project service docs + sync vault 2026-06-08

Add 10 - Services/ docs for all sub-projects: backend, frontend, scanner,
deployment (new), update amanat-assist. Update Scanner Architecture,
Telegram Mini App flow, and Activity Log. Add payment safety edge cases.
This commit is contained in:
Siavash Sameni
2026-06-08 16:22:52 +04:00
parent 181e8e9c2f
commit 67244223ec
13 changed files with 2734 additions and 311 deletions

View File

@@ -1,20 +1,22 @@
---
title: Telegram Mini App Flow
tags: [flow, telegram, mini-app, auth, bilingual, RTL, shop, cart]
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-03
> **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
> **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
@@ -26,10 +28,11 @@ Telegram Client
├─ useTelegramLiveContext ← SDK probe + polling
├─ useTelegramLanguage ← EN / FA detection
├─ useTelegramAutoSignIn ← silent JWT exchange
├─ useTelegramMainButton ← native chrome sync
├─ 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
@@ -38,17 +41,28 @@ Telegram Client
├─ 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
└─ [overlay] TelegramNewRequestView
└─ [overlay] TelegramNotificationsView
└─ [overlay] TelegramCartView
─ 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 only used as a final escape hatch to the full web dashboard.
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.
---
@@ -66,7 +80,27 @@ The shell is a **single-page, no-router** design: all navigation (tabs, overlays
---
## 3. SDK Initialisation & Context Probe
## 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()`
@@ -92,7 +126,7 @@ The function assembles a `TelegramContext` object from:
---
## 4. Shell State Machine
## 5. Shell State Machine
`getTelegramStatus(context, hasWebAccount)` returns one of three states:
@@ -114,9 +148,9 @@ State transitions occur on:
---
## 5. Authentication Flow
## 6. Authentication Flow
### 5.1 Silent Auto Sign-In
### 6.1 Silent Auto Sign-In
**Hook:** `useTelegramAutoSignIn` · **File:** `hooks/use-telegram-auto-sign-in.ts`
@@ -127,7 +161,7 @@ On mount, if `context.isMiniApp && context.initData && !user`:
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)
### 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`.
@@ -136,47 +170,74 @@ When `initData` is present but auto sign-in failed (or hasn't run yet), `Telegra
When `initData` is absent (accessed via a path that skips Telegram context), only the email/register buttons appear.
### 5.3 Backend Endpoint
### 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.
---
## 6. Navigation Model
## 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' | null
openConversationId : string | null
openRequestId : string | null
openSellerId : string | null
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. `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
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: chat thread → request detail → seller shop → overlay → returns to `home` tab.
**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: 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)
`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 are styled with the amaneh saffron palette (`color: #C2410C`, `text_color: #FFFFFF`) via `setParams` (WebApp SDK >= 6.1).
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.
---
## 7. Tab Structure
## 8. Tab Structure
The shell has **five bottom tabs** rendered by `TelegramTabBar`:
@@ -192,89 +253,115 @@ The shell has **five bottom tabs** rendered by `TelegramTabBar`:
---
## 8. Supported Flows
## 9. Supported Flows
### 8.1 Home Tab
### 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).
### 8.2 Shop Tab — Sellers List
### 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 in the header when `totalItems > 0`; tap opens `overlayScreen = 'cart'`.
- 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`.
### 8.3 Shop Tab — Seller Store
### 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). 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).
- **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()`.
### 8.4 Shopping Cart Overlay
### 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"** — plain `<a href={paths.shops.checkout}>` link; exits Mini App to web checkout.
- **"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 is the cart handoff mechanism: the cart built in Telegram IS the cart the web checkout page hydrates.
- 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.
- 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
### 9.6 In-Shell Checkout Overlay
Destination: `/dashboard/shops/checkout``RequestTemplateCheckoutView` wrapped by `RequestTemplateCheckoutProvider`.
**`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.
The provider reads the shared `localStorage` key and hydrates the TMA cart. The checkout is a 3-step stepper:
### 9.7 Payment View (In-Shell)
| 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 |
**`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.
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)
### 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`.
### 8.7 Request Detail with Stepper
### 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.
### 8.8 Create New Request
### 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'`.
- `MainButton` is hidden while the overlay is open (submit lives in the form itself).
### 8.9 Chat Tab
### 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.
@@ -283,22 +370,21 @@ Payment execution calls `convertTemplatesToRequests()` to create escrow records,
- 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
### 9.12 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).
- **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`/dashboard/account/address` (web).
- **Addresses** → opens `overlayScreen = 'addresses'` (in-shell `TelegramAddressesView`).
- Passkey → `/dashboard/account/passkey` (web).
**Help section:**
@@ -308,16 +394,38 @@ The account tab has four sections. All user data is passed as props from the she
**Session section:**
- Sign Out → `TelegramBottomSheet` confirmation dialog → `authSignOut()` + `window.location.assign(paths.auth.jwt.signIn)`.
### 8.11 Notifications Overlay
### 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.
---
## 9. API Calls
## 10. API Calls
| Action | Hook / call | Backend endpoint |
|---|---|---|
@@ -328,18 +436,26 @@ The account tab has four sections. All user data is passed as props from the she
| 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 web checkout.
Cart operations (add/remove/quantity) are **pure localStorage** — no API calls until checkout.
---
## 10. Bilingual Support (EN / FA)
## 11. Bilingual Support (EN / FA)
**Language detection priority** (`useTelegramLanguage`):
@@ -378,7 +494,7 @@ All JSX uses `t.<section>.<key>` — no inline strings in components.
---
## 11. Design System
## 12. Design System
**File:** `src/sections/telegram/constants.ts` · `src/sections/telegram/telegram-shell-css.ts`
@@ -391,7 +507,7 @@ The Mini App has a distinct visual identity (cream/saffron Persian palette) that
| `cream50` | `#FBF6EB` | Page background |
| `ink900` | `#1C1410` | Primary text |
| `ink600` | `#6B5D4E` | Secondary text / labels |
| `saffron600` | `#C2410C` | Primary action, MainButton |
| `saffron600` | `#C2410C` | Primary action |
| `saffron500` | `#D97757` | Hover states |
| `pistachio700` | `#3D6B4F` | Success / released states |
| `pomegranate700` | `#8E2424` | Error / disputed states |
@@ -399,15 +515,15 @@ The Mini App has a distinct visual identity (cream/saffron Persian palette) that
**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.
**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. Dark mode: `.tg-shell--dark` class toggled from `themeScheme`.
**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
## 13. Telegram SDK Usage Patterns
### 12.1 Safe-Area Inset
### 13.1 Safe-Area Inset
```ts
// TelegramContext.safeArea = { top, right, bottom, left } (px)
@@ -418,16 +534,16 @@ 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
### 13.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.
Used on: tab switches (light), new-request CTA (medium), language toggle (light), back button (light), payment actions (medium). All calls are wrapped in try/catch — the API may be absent on older clients.
### 12.3 Back Button
### 13.3 Back Button
```ts
useTelegramBackButton({ webApp, isVisible, onClick })
@@ -435,22 +551,29 @@ useTelegramBackButton({ webApp, isVisible, onClick })
// Cleanup: offClick() on unmount / visibility change
```
### 12.4 Main Button
### 13.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
useTelegramMainButton({ webApp, isReady: false, text: '', onClick: mainButtonAction })
// isReady is always false — MainButton is intentionally kept hidden.
// The hook is retained so it can be re-enabled without structural changes.
```
### 12.5 Theme Integration
### 13.5 External Links
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.
```ts
openTelegramExternalLink(context.webApp, path)
// Uses webApp.openLink() for fully external URLs (opens browser).
// Uses window.location.href for same-origin navigation that must stay in WebView.
```
### 13.6 Theme Integration
Telegram's `themeParams` is normalised (both camelCase and snake_case accepted) and injected as CSS custom properties on the shell root. The amaneh palette overrides these for the Mini App's own UI.
---
## 13. Edge Cases
## 14. Edge Cases
| Scenario | Detection | Handling |
|---|---|---|
@@ -467,11 +590,14 @@ Telegram's `themeParams` is normalised (both camelCase and snake_case accepted)
| 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 |
| Payment from shop checkout | `paymentCheckoutFlow === true` | BackButton steps back to cart; progress header shows 3-step flow |
| Display name resolution | User may have no name set in DB | Falls back to Telegram profile name (`first_name` / `last_name`), then generic label |
| Seller chat from request detail | `onChatSeller(sellerId)` | `createConversation({ type: 'direct', participantIds: [sellerId] })` → opens chat thread in-shell |
| Assist hand-off on iOS | `webApp.openLink()` opens Safari | `window.location.href` used instead to keep navigation in the Telegram WebView |
---
## 14. File Map
## 15. File Map
```
src/
@@ -480,6 +606,7 @@ src/
sections/telegram/
constants.ts # TG_PALETTE, TG_FONTS, TG_EASE, status maps
telegram-shell-css.ts # buildTelegramShellCss() — inlined CSS blob
avatar-url.ts # avatar URL helper
index.ts # barrel
locales/
types.ts # TelegramDict, TelegramLang, TelegramTabId
@@ -490,35 +617,47 @@ src/
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-main-button.ts # MainButton lifecycle (kept, isReady=false)
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-theme.ts # dark/light theme detection
use-telegram-realtime.ts # shared Socket.IO real-time helper
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-offers.ts # GET /api/marketplace/offers?requestId=...
use-telegram-conversations.ts # Chat conversation list
use-telegram-chat-thread.ts # Chat thread + optimistic send
use-telegram-notifications.ts # GET /api/notifications
use-telegram-addresses.ts # GET /api/user/addresses
use-telegram-points.ts # GET /api/user/points
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-template-detail-view.tsx # Template full detail + cart/order actions
telegram-cart-view.tsx # Cart overlay
telegram-checkout-view.tsx # In-shell 3-step checkout overlay
telegram-payment-view.tsx # In-shell payment drilldown
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-request-detail-view.tsx # Request drilldown + stepper + offers
telegram-new-request-view.tsx # New request overlay form + Assist CTA
telegram-chat-view.tsx # Chat conversation list tab
telegram-chat-thread-view.tsx # Chat thread drilldown
telegram-archived-chats-view.tsx # Archived conversations
telegram-account-view.tsx # Account + preferences + sign-out tab
telegram-notifications-view.tsx # Notifications overlay
telegram-settings-view.tsx # In-shell profile/settings overlay
telegram-addresses-view.tsx # In-shell address management overlay
telegram-points-view.tsx # In-shell points/loyalty overlay
index.ts
components/
telegram-header.tsx # AMN logo + subtitle + language toggle
telegram-header.tsx # AMN logo + subtitle + language toggle + bell
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)
@@ -528,17 +667,22 @@ src/
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-list-controls.tsx # List sort/filter controls
telegram-chat-row.tsx # Chat: conversation list row
telegram-chat-bubble.tsx # Chat: message bubble
telegram-chat-composer.tsx # Chat: message input
telegram-review-prompt.tsx # Post-transaction review prompt
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-theme-toggle.tsx # Dark / light theme toggle
telegram-bottom-sheet.tsx # Generic bottom sheet primitive
telegram-form-field.tsx # Form field + input style helper
telegram-cart-fab.tsx # Floating cart badge button
telegram-support-fab.tsx # Floating support chat button
telegram-seal-mark.tsx # SealMark logo component
telegram-icons.tsx # Telegram-scoped icon set
index.ts
@@ -546,7 +690,7 @@ src/
---
## 15. Current Implementation Status (v2.8.94)
## 16. Current Implementation Status
| Area | Status | Notes |
|---|---|---|
@@ -556,39 +700,54 @@ src/
| 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 |
| `?lang=` dev preview param | Done | URL param override |
| Dark mode | Done | `.tg-shell--dark` class, `use-telegram-theme` |
| Home tab | Done | Banner + quick actions + state chips + Assist CTA |
| Shop tab — sellers list | Done | API-backed with skeleton + empty states, cart FAB |
| Shop tab — seller store | Done | Templates list, add/remove cart, template detail drilldown |
| Template detail drilldown | Done | Full detail, cart/order actions |
| 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 |
| Cart overlay | Done | Quantity controls, remove, total, in-shell checkout CTA |
| In-shell checkout | Done | 3-step cart→address→payment; replaces web handoff |
| In-shell payment view | Done | Direct balance intent + polling; checkout-flow back-nav |
| 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 |
| Offer review in request detail | Done | Offers fetched via `useTelegramOffers`; accept/reject |
| New request form | Done | In-shell overlay, category fetch, validation, Assist CTA |
| 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 |
| Direct seller chat | Done | `createConversation` from request detail |
| Account tab | Done | Profile, preferences, help, sign-out |
| Settings overlay | Done | In-shell profile editing |
| Addresses overlay | Done | In-shell address management; reachable from checkout |
| Points overlay | Done | In-shell points/loyalty |
| Notifications overlay | Done | API-backed; Socket.IO real-time; mark-all-read |
| Telegram chrome (MainButton / BackButton) | Done | Saffron palette, lifecycle hooks |
| Notifications unread badge | Done | Bell icon in header |
| Telegram chrome (BackButton) | Done | Full back-stack with checkout flow awareness |
| Telegram MainButton | Disabled | Intentionally hidden (`isReady: false`); hook retained |
| 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 |
| amanat-assist integration | Done | "Open Assist" CTA in Home + New Request; window.location hand-off with access_token |
| Deep link `startapp` routing | Partial | `startParam` parsed; auto-navigation to specific request not yet wired |
| Backend room-scoped Socket.IO | Partial | Global socket broadcast fixed client-side (v2.8.4); server-side room scoping is a follow-up |
| Client matrix QA (iOS/Android/Desktop) | Pending | Needs cross-platform testing pass |
### 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).
1. **`startapp` deep link routing:** if `context.startParam` matches `req_<id>`, auto-open `TelegramRequestDetailView` on first render.
2. **Backend room-scoped Socket.IO:** server-side scoping for real-time chat updates (follow-up from client-side fix in v2.8.4).
3. **Client matrix QA:** iOS Telegram, Android Telegram, Telegram Desktop, and web clients all need a full feature pass.
4. **Review prompt:** `TelegramReviewPrompt` component exists but integration point (post-payment / post-delivery) is TBD.
5. **Archived chats:** `TelegramArchivedChatsView` exists but is not yet surfaced in the navigation.
---
## 16. Related Documents
## 17. Related Documents
- [[amanat-assist]] — the separate AI-driven Mini App for LLM-assisted request creation
- [[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
- [[Request Template Checkout]] — web checkout flow; the Mini App now has its own in-shell checkout, but the localStorage cart key is shared