|
| 1 | +## Context |
| 2 | + |
| 3 | +The server has introduced a monthly quota system for AI chat (text and voice). The app currently has no awareness of user plans or usage limits. The bot service (`src/services/botservice.ts`) sends requests to `POST /chat/text` and opens WebSockets to `/chat/voice-bot-websocket` without handling quota-related error codes. The Settings screen has no plan/usage information. |
| 4 | + |
| 5 | +Current state: |
| 6 | +- `botservice.ts` does not read rate-limit response headers |
| 7 | +- No `planService` exists |
| 8 | +- Text chat catches generic errors but not HTTP 429 specifically |
| 9 | +- Voice WebSocket does not differentiate close code 4029 from other failures |
| 10 | +- Settings screen has no plan section |
| 11 | + |
| 12 | +## Goals / Non-Goals |
| 13 | + |
| 14 | +**Goals:** |
| 15 | +- Introduce `planService.ts` to fetch and cache `GET /auth/me/plan` |
| 16 | +- Display plan, text/voice quotas with progress bars, and reset date in Settings |
| 17 | +- Handle HTTP `429` in text chat with an inline, localized message and CTA |
| 18 | +- Handle WS close `4029` and error frame in voice chat with a modal and CTA |
| 19 | +- Show a live `X-RateLimit-Remaining` counter in the chat input area; disable send at 0 |
| 20 | +- Add all user-facing strings to `en.json` and `it.json` |
| 21 | + |
| 22 | +**Non-Goals:** |
| 23 | +- In-app upgrade/payment flow (CTA navigates to Plan screen only) |
| 24 | +- Admin plan management UI |
| 25 | +- Push notification when quota resets |
| 26 | +- Caching plan data across app restarts (re-fetch on open is sufficient) |
| 27 | + |
| 28 | +## Decisions |
| 29 | + |
| 30 | +### D1 — New `planService.ts` instead of extending existing services |
| 31 | +Quota data is orthogonal to tasks and auth. A dedicated service keeps concerns separate and is easier to test. |
| 32 | +Alternative considered: extending `authService.ts`. Rejected because auth service is already complex and quota data has its own refresh lifecycle. |
| 33 | + |
| 34 | +### D2 — Store remaining counter in React state, not AsyncStorage |
| 35 | +`X-RateLimit-Remaining` is up-to-date after every message send. Persisting it across app restarts adds complexity for little gain; `GET /auth/me/plan` on next open gives accurate data. |
| 36 | +Alternative: AsyncStorage persistence. Rejected as overkill. |
| 37 | + |
| 38 | +### D3 — Inline error message in chat (not toast) for 429 |
| 39 | +The server spec explicitly requests an inline error message. This also provides a persistent, tappable CTA to the Plan screen. |
| 40 | +Alternative: bottom toast. Rejected per server integration guide. |
| 41 | + |
| 42 | +### D4 — Voice quota modal (not inline) for close 4029 |
| 43 | +Voice chat UI doesn't have a chat message thread. A modal is the appropriate pattern for a session-ending error. |
| 44 | + |
| 45 | +### D5 — Threshold `>= 9999` treated as unlimited |
| 46 | +ENTERPRISE plan returns `999999`. Any value >= 9999 is treated as unlimited in the UI (counter hidden, send never disabled). |
| 47 | + |
| 48 | +### D6 — `X-RateLimit-Remaining` read from SSE/fetch response headers |
| 49 | +`botservice.ts` already wraps the `/chat/text` call. The remaining count will be extracted from the response headers at that call site and returned alongside the message response so the caller can update UI state. |
| 50 | + |
| 51 | +## Risks / Trade-offs |
| 52 | + |
| 53 | +- [Risk] SSE streaming responses may not expose headers via the Fetch API in React Native → Mitigation: test on Android/iOS; fall back to `GET /auth/me/plan` if headers are inaccessible in the streaming path. |
| 54 | +- [Risk] Plan screen navigation assumes a named route exists → Mitigation: verify route name in `RootStackParamList` before wiring CTAs; add route if missing. |
| 55 | +- [Risk] WS close code `4029` may be swallowed by reconnect logic → Mitigation: check existing reconnect guard in `VoiceBotWebSocket` and add an explicit `4029` branch that skips reconnect. |
| 56 | + |
| 57 | +## Migration Plan |
| 58 | + |
| 59 | +No data migration needed. All changes are additive: |
| 60 | +1. Add `planService.ts` |
| 61 | +2. Update `botservice.ts` (header reading + 429 handling) |
| 62 | +3. Update `VoiceBotWebSocket` in `botservice.ts` (4029 handling) |
| 63 | +4. Add Plan section to Settings screen |
| 64 | +5. Update BotChat screen (inline error + counter + disabled state) |
| 65 | +6. Add i18n strings |
| 66 | + |
| 67 | +Rollback: revert the above files. No server-side changes required from the client side. |
| 68 | + |
| 69 | +## Open Questions |
| 70 | + |
| 71 | +- Is there an existing "Plan" or "Subscription" screen in the navigation stack, or must it be created? (Affects CTA navigation target.) |
| 72 | +- Does the voice chat UI live in a dedicated screen or is it a modal within BotChat? (Affects where to show the modal.) |
0 commit comments