Commit Graph

39 Commits

Author SHA1 Message Date
Codex ff2759cf0f feat(chat): add support action cards and live entitlements 2026-07-24 18:04:05 +08:00
admin ae97366a4a refactor(data): replace schema classes with readonly models 2026-07-17 13:21:40 +08:00
admin eac3d8f0a7 refactor(data): merge DTO models into schemas 2026-07-17 12:47:18 +08:00
admin 20d07023e4 refactor(stores): standardize state machine structure 2026-07-15 11:19:08 +08:00
admin 0033625866 perf(bundle): replace broad storage and utils imports 2026-07-14 18:13:55 +08:00
admin 37ae152abb refactor: scope providers by route and use XState selectors 2026-07-13 19:07:53 +08:00
admin 225d232763 fix(auth): handle logout and refresh results
Propagate storage Result failures through logout, guest restoration, token refresh, and user state actors. Clear invalid sessions after failed automatic refreshes and cover both business and guest flows.
2026-07-13 17:23:54 +08:00
admin 9f5e1533fc refactor(user): remove unused view fields 2026-07-07 15:40:27 +08:00
admin d17ab6c23d feat(profile): surface daily free quotas 2026-07-03 16:56:06 +08:00
admin 760e4fe343 refactor(app): tighten chat and sync boundaries 2026-07-01 14:03:25 +08:00
admin e7a9e7abe5 feat(chat): unlock paid messages before payment 2026-06-30 18:56:17 +08:00
admin 8d5ee68e5f Reapply "fix(user): refresh profile on sidebar open"
This reverts commit f4d0f76466.
2026-06-30 17:18:36 +08:00
admin e254c93078 feat(subscription): default pay channel by country 2026-06-30 16:49:47 +08:00
admin f4d0f76466 Revert "fix(user): refresh profile on sidebar open"
This reverts commit 6e811b0e09.
2026-06-30 13:07:50 +08:00
admin 6e811b0e09 fix(user): refresh profile on sidebar open 2026-06-30 11:36:52 +08:00
admin 0bae53bfba feat: implement lazy singleton pattern for repository instances and update related imports 2026-06-29 17:25:41 +08:00
admin 58cd2a7545 feat(user): sync entitlement snapshot 2026-06-29 10:37:37 +08:00
admin 6081f2896a test(stores): add state machine coverage 2026-06-25 13:28:08 +08:00
admin a347b39001 feat(payment): connect payment service flow 2026-06-18 15:40:59 +08:00
admin f600e11d55 refactor(auth): centralize login status sync 2026-06-18 12:52:49 +08:00
admin 0d0dabaace refactor(utils): add barrel exports 2026-06-18 11:58:50 +08:00
admin 5d66cef85c refactor(dto): move app models into dto layer 2026-06-17 17:34:55 +08:00
admin 67d0bd5cd0 refactor(hooks): remove unused PWA install and visibility hooks
refactor(package): remove deprecated dev:proxy script
fix(user): ensure user-auth-sync is exported in index
2026-06-17 17:27:18 +08:00
admin 977c0905bd refactor(chat): remove redundant auth state management from ChatScreen documentation
refactor(chat): update guest chat quota logic to use isProduction utility
fix(user): add TODO for user initialization event on login status change
2026-06-17 15:30:36 +08:00
admin bda1605ce7 fix(auth): redirect to splash after logout 2026-06-17 15:07:16 +08:00
admin 1d7d9cfbfc fix(chat): hydrate user avatar after auth restore 2026-06-17 14:23:07 +08:00
admin d41b3c4473 refactor(schemas): extract nullable-default helpers to shared file
The three defensive Zod helpers (stringOrEmpty, numberOrZero,
booleanOrFalse) used by user.ts are general-purpose Zod utilities
that any schema may need — they're not user-specific. The original
inline declaration in user.ts had two downsides:

1. Any new schema (chat, auth, metrics, etc.) that needs the same
   'null | undefined → default scalar' pattern would have to
   duplicate the helper.

2. The long defensive-parse comment block (11 lines) lived inside
   user.ts and didn't explain the helpers' general purpose.

Move them to src/data/schemas/nullable-defaults.ts (top-level
sibling of auth/, chat/, metrics/, user/ subdirs) so any schema
can import them. Keep the original explanation verbatim at the
top of the new file.

Bundled in this commit:
- user.ts imports the helpers from the new file (drops 11 lines
  of comment + 3 const declarations)
- 3 auto-generated barrel index files regenerated by barrelsby
  to pick up the new exports (chat/, subscription/, stores/user/)
2026-06-17 12:05:09 +08:00
admin 0a9abc2502 fix(auth): stop parsing logout response body (avoids ZodError on null data)
Root cause: backend `/auth/logout` returns `{ success: true, data: null }`
(no business payload for a fire-and-forget endpoint). The previous
`AuthApi.logout` ran `unwrap(env)` (which passes null through, since
null is defined) and then `LogoutResponse.fromJson(null)`, which
Zod-parses as `z.object(...)` and throws "Invalid input: expected
object, received null".

The error was caught in `AuthRepository.logout` with
`console.warn(... clearing local anyway)` — logout actually still
worked (local clear ran), but a noisy red ZodError hit the console on
every logout click.

Fix: align `AuthApi.logout` with the existing fire-and-forget pattern
used by `register` and `sendCode` — call `httpClient` and ignore the
response body. Drop the now-unused `LogoutResponse` import.

`AuthRepository.logout` already discards the return value, so the
`Promise<LogoutResponse> → Promise<void>` signature change is safe.

Bundled in this commit (unrelated):
- chat-machine.actors.ts / user-machine.ts: removed stale design-doc
  comment blocks (IDE/linter cleanup)
- user-machine.actors.ts: removed redundant `userStorage.clearUserData()`
  from `userLogoutActor` — `authRepo.logout` already clears local
  user data, so this was a no-op duplicate.
- sidebar-screen.tsx: removed one stale comment line.
2026-06-16 18:34:55 +08:00
admin a44463b3ee refactor(user): split user-machine into helpers and actors
Mirror the existing chat-module structure (chat-machine.helpers.ts +
chat-machine.actors.ts) for consistency across state-machine modules.

Split:
- user-machine.helpers.ts: pure data layer (no XState dep)
  - userStorage singleton
  - InitData interface
  - toView DTO → UserView mapper
  - readInitData (parallel getUser + getAvatarUrl)
- user-machine.actors.ts: async actor implementations (fromPromise)
  - userRepo / authRepo injection
  - userInitActor / userFetchActor / userLogoutActor
- user-machine.ts: only setup().createMachine() — keeps inline assign
  actions referencing context/event where they belong, imports actors
  (not helpers, per the layered convention).

No behaviour change — only file boundaries moved. Public API (exports of
UserState / UserEvent / initialState / userMachine / UserMachine) stays
identical so external imports are untouched.
2026-06-16 18:12:11 +08:00
admin e44cc7e2f3 feat(sidebar): redesign sidebar UI with three user states (light theme)
Replace the dark sidebar theme with a light-themed three-state UI
matching the design references:

- guest  : pink "login" pill in user row, no status pill on VIP card,
           "Activate VIP Membership" button shown
- member : "VIP membership not activated" subtitle, no status pill,
           "Activate VIP Membership" button shown
- vip    : pink "VIP Member" pill with diamond icon, "Activated" pill
           in VIP card header, no Activate button

Decompose into BackBar, UserHeader, VipBenefitsCard, and VoicePackageCard
under src/app/sidebar/components/. Delete obsolete GuestPanel,
UserInfoCard, and VipCta.

Lift VIP_BENEFITS to a shared src/data/constants/vip-benefits.ts so the
sidebar and subscription page render identical benefit copy.

Add five sidebar tokens (--color-card-surface, --color-voice-gradient-*
--border-card, --shadow-card, --color-pill-bg) to src/tokens/colors.css.

Includes barrelsby regeneration for src/app/sidebar/components and
side-effect barrel updates under src/stores/{auth,chat,sidebar,user},
plus package-lock.json from npm install (required for lint/typecheck).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-16 10:30:21 +08:00
admin 0548a08cbb feat: integrate Stripe payment with subscription plans
Add Stripe payment integration across the project:

- Add @stripe/stripe-js dependency
- Configure Stripe environment variables (secret key, publishable key, webhook secret) for dev, local, and production environments
- Add Product/Price IDs for monthly, quarterly, and annual subscription tiers
- Extend subscription plan data with voiceMinutesPerDay quotas (30/45/60 minutes per tier)
- Update .gitignore to exclude implementation_plan.md
2026-06-16 10:21:02 +08:00
admin fd5dfe2715 feat(sidebar): show user info with three-state UI (guest/member/vip)
Sidebar screen now primarily displays user-related information
and derives three render states from loginStatus + currentUser.isVip:

- guest (notLoggedIn): welcome panel + Sign in / Sign up entries
- member (logged in, no VIP): user info card + Get VIP CTA + Log out
- vip (logged in, VIP): user info card with VIP badge + Manage VIP CTA
  + Log out

Adds UserView.isVip (default false), patches user-machine.toView()
to populate it, and introduces three presentational components
(GuestPanel, UserInfoCard, VipCta) co-located under src/app/sidebar.

Preserves existing UserInit + logout-redirect behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-15 18:11:39 +08:00
admin 1449acd6c9 chore(barrels): regenerate barrel files via barrelsby
- Fix chat components barrel to properly export all component modules
- Remove interceptor exports from API services barrel
- Regenerate store barrel files (auth, chat, sidebar, user) using barrelsby
2026-06-15 17:53:15 +08:00
admin 982d9ba790 refactor(data): add I*Repository interfaces and depend on them in stores
为 /src/data/repositories/ 下每个仓库类增加对应的接口契约,
接口文件存放在独立的 interfaces/ 子目录中。

改动:
- 新建 4 个接口文件 (iauth_repository / ichat_repository /
  imetrics_repository / iuser_repository),纯 type-only,方法签名
  与实现类完全一致
- 4 个实现类加 implements I{Name}Repository 子句,编译器自动校验契约
- 3 个 stores 文件 (auth / chat / user state machines) 改为依赖接口
  类型:通过 `const xxxRepo: I{Name}Repository = xxxRepository` 局部别名,
  actor 内部调用全部走别名
- barrelsby.json 加入 interfaces/ 目录,让自动 barrel 能覆盖
- Auto-regenerated repositories/index.ts 与 interfaces/index.ts

参照 storage 层已有的 IAuthStorage / IChatStorage / IUserStorage 模式。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-11 17:02:49 +08:00
admin d4cb40a74b chore: migrate contexts to stores and refresh barrel exports
- Move state management from `src/contexts` to `src/stores` (zustand-based)
- Update barrelsby config to include new stores directory
- Add bottom-sheet and auth-background components
- Auto-open browser on dev server ready in VS Code launch config
- Refresh generated barrel index files
2026-06-11 09:35:26 +08:00
admin 2bc2e1b691 refactor: migrate img tags to next/image and fix hook usage 2026-06-10 19:16:41 +08:00
admin 47591be41c refactor(auth): unify Facebook and Google login into AuthPlatform class
Replace the separate FacebookLogin and GoogleLogin classes with a single
AuthPlatform class that takes a provider name ("facebook" | "google") as a
constructor argument. This consolidates duplicate OAuth sign-in logic behind
one entry point while preserving the existing NextAuth flow.

- Add new src/lib/auth/auth_platform.ts exporting AuthPlatform and
  AuthProvider type
- Update auth-facebook-panel.tsx and splash-button.tsx to use the new API
- Rename initialContext → initialState in the auth machine for consistency
- Update inline docs and comments to reference AuthPlatform

No behavioral change for end users; both providers still route through
NextAuth's signIn() with the same callbacks and cookies.
2026-06-10 18:58:15 +08:00
kanban 744e23fc29 refactor(stores): split state and event unions into separate files
Extract `<Name>Context` interface + `initialContext` const into
`<name>-context.ts` and the event union into `<name>-events.ts` for
all four state machines (auth, chat, sidebar, user) under src/stores/.

- `*-machine.ts` keeps helpers, actors, actions, and the
  `setup().createMachine()` body; re-exports the types/initial value
  to preserve its public API.
- `*-types.ts` re-exports from the new files for backward compatibility
  (sidebar keeps enum re-exports, chat keeps GuestChatQuota).
- `index.ts` barrels updated to re-export the new files.
- Removed unused model imports (AuthMode, AuthPanelMode) from
  auth-machine.ts; kept LoginType, UiMessage, UserView where still
  used by actors/helpers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-10 18:24:04 +08:00
admin a5d8214650 refactor: migrate state imports from contexts to stores directory 2026-06-09 18:47:04 +08:00