# Halo Bestie — Client App Flutter mobile application for end users (clients) seeking mental health support. > See root `CLAUDE.md` for full project context and architectural decisions. ## Stack - **Framework:** Flutter (iOS + Android) - **Auth:** Self-managed (Phase 3.4). Anonymous-first + phone OTP + (Google / Apple when creds arrive). - Access token in memory on `AuthBridge`; refresh token persisted via `flutter_secure_storage`. - Google + Apple SDKs installed but buttons are hidden behind `--dart-define=ENABLE_SOCIAL_AUTH=true` until backend OAuth credentials exist. - `firebase_auth` removed; `firebase_messaging` kept for FCM push. - **API:** Calls public Fastify backend (`/api/client/` and `/api/shared/` routes). Refresh + logout live on `shared.auth`. - **Payment:** Xendit (paid sessions, optional trial) ## Key Concepts - Users are **clients** — they seek mental health support ("curhat") - Core flow: **server-issued anonymous** → optional phone/Google/Apple identity upgrade (same customer row via `anonymous_customer_id`) → browse/match with mitra → book session → chat → pay - Anonymity toggle: if `/api/shared/config/anonymity` reports `anonymity_enabled = false`, the router shows `ForceRegisterScreen` until the user identifies ## Conventions - Never call `/api/mitra/` or `/internal/` routes from this app - API calls go through `ApiClient`; it auto-attaches the JWT from `AuthBridge` and auto-refreshes on 401 - WebSocket handshake (`/api/shared/ws`) reads the access token from `AuthBridge` in the first frame's `{type:"auth", token, session_id?}` message - Use `const bool.fromEnvironment('ENABLE_SOCIAL_AUTH')` (via `social_auth_enabled.dart`) to gate any Google/Apple UI — never call `loginGoogle` / `loginApple` from a path reachable without that flag