Commit Graph

33 Commits

Author SHA1 Message Date
admin 0d2a515e51 fix(sidebar): redirect to splash screen after logout 2026-06-17 14:35:28 +08:00
admin c07a10ee80 refactor(splash): handle Skip redirect in splash screen, decouple SplashButton
Previously, the Skip button (guest login entry) relied on the auth
state machine's `pendingRedirect` flag to trigger the navigation
to /chat after guest login completed. That introduced a
non-obvious coupling: the button dispatched an event, the machine
ran an actor, the auth screen (or splash useEffect) saw the flag
and redirected.

This refactor moves the redirect into the splash screen itself:

- `SplashButton` now takes an `onSkip` prop and no longer knows
  about auth dispatch. Pure presentation component.
- `SplashScreen` provides `handleSkip` that dispatches the
  `AuthGuestLoginSubmitted` event AND immediately calls
  `router.replace(/chat)` for snappier perceived navigation.
- `auth-machine.ts`: `loadingGuestLogin.onDone` no longer sets
  `pendingRedirect: true` (splash already navigated). Sets
  it to `false` explicitly so other screens (auth, sidebar) that
  also react to `pendingRedirect` don't double-navigate if the
  user triggers guest login from a non-splash surface in the future.

No behavior change for the happy path: Skip → /chat works the same.
The refactor is purely about responsibility allocation and
component decoupling.
2026-06-17 11:51:35 +08:00
admin ee760abdf5 refactor(auth): clean up user logout handling in SidebarScreen 2026-06-17 11:11:25 +08:00
admin bb2ae41232 fix(auth): include OAuth backend-sync phase in isLoading derivation
The AuthState.isLoading derivation in auth-context.tsx covered the
OAuth redirect phase (loadingOAuth) but NOT the backend-sync phase
that runs after the OAuth callback returns
(syncingGoogleBackend / syncingFacebookBackend). Any UI that
read isLoading would incorrectly report "not loading" during
the backend token-exchange round-trip.

Add the two missing state.matches() lines so isLoading is true
for the entire OAuth login window (button click → OAuth redirect
→ callback → backend sync).

Note: in the current OAuth flow the user is on /chat during the
sync phase (NextAuth redirected them there), so the splash/auth
button is not actually visible to reflect the new true value.
The fix is still correct because:

- Conceptually aligned: any in-flight login op is "loading"
- Defense in depth: future UI on /chat that consumes isLoading
  will get accurate feedback
- Non-regression: only makes isLoading more permissive (true more
  often), never false-fires

A visible /chat overlay during the sync phase would be a separate,
larger change — noted in the planning doc but not in this commit.
2026-06-17 10:06:07 +08:00
admin e85963e7bf refactor(auth): rename AuthStatusCheckSubmitted → AuthInit (init-only)
The previous event name implied "re-check status", but the actual
semantics is just "read storage on app start and sync loginStatus into
the machine". It was being dispatched from three sites:

1. AuthStatusChecker mount useEffect (the legit one-time init)
2. AuthStatusChecker loginStatus-watching useEffect (dead code — the
   machine's onDone already writes loginStatus, re-reading storage
   just returns the same value as a no-op)
3. Sidebar post-logout effect (also dead code — AuthReset directly
   sets loginStatus to initialState's "notLoggedIn", and
   userLogoutActor already cleared storage, so the values are
   already aligned)

This commit:

- Renames event AuthStatusCheckSubmitted → AuthInit in:
  * auth-events.ts (type union)
  * auth-machine.ts (handler + transition target: checkingAuthStatus →
    initializing, mirroring UserInit / initializing in user-machine)
  * auth-status-checker.tsx (single dispatch site)
  * root-providers.tsx (one comment)

- Simplifies auth-status-checker.tsx from 74 → ~40 lines:
  * Removes useEffect ② (loginStatus-watching re-check)
  * Removes prevLoginStatusRef + skipNextChangeRef + useAuthState
    (dead loop-guard machinery no longer needed)

- Removes the post-logout re-check dispatch from sidebar-screen.tsx:
  * AuthReset alone is sufficient — userLogoutActor cleared storage
    and AuthReset writes back initialState, so they match by
    construction. No re-verification needed.

Net: -44 lines, no behavior change for the happy paths (startup,
login, logout), one fewer source of false re-checks.
2026-06-17 09:52:45 +08:00
admin 39e7d61c8a fix(auth): re-check auth status after logout; clear NextAuth session after sync
1) Sidebar logout flow:
   - After AuthReset, also dispatch AuthStatusCheckSubmitted so the auth
     machine re-verifies storage (not just resets to initialState). This
     makes sure the redirect to /chat reflects the actual storage state,
     not just the forcibly-cleared context.

2) OAuthSessionSync:
   - After dispatching AuthGoogleSyncSubmitted / AuthFacebookSyncSubmitted,
     call signOut({ redirect: false }) to drop the NextAuth session.
     The OAuth idToken / accessToken has already been handed to the
     backend; clearing the browser-side session prevents lingering OAuth
     credential exposure. Fire-and-forget — once status flips to
     "unauthenticated", the useEffect early-returns so no re-entry.

Note: auth-machine.ts lost a 3-line stale comment block about XState v5
type inference for inline assign; bundled with this commit.
2026-06-16 18:24:23 +08:00
admin c5686d2749 feat(auth): implement Facebook user data fetching from Graph API and persist profile information 2026-06-16 17:43:19 +08:00
admin 82bf917ace feat(auth): enhance AuthStatusChecker with detailed status change handling and prevention of infinite loops 2026-06-16 17:42:37 +08:00
admin 200fb1642f fix(auth): surface validation errors in email login/register forms
Validation failure was previously silent — the form's submit() returned
without dispatching or showing anything on screen, so users could not tell
why the login/register button appeared to do nothing.

Changes:
- Add validationError local state in EmailLoginForm and EmailRegisterForm
- Display first validation error via AuthErrorMessage, prioritised over
  the server's globalError (validation errors feel more immediate)
- Clear validation error on field change (standard "error fades as you
  fix it" UX) — implemented as onChange handlers, not useEffect, to
  avoid React's cascading-render anti-pattern
- Add observability to the entire email flow (was previously silent end
  to end): form submit ENTRY, validator rejection, actor ENTRY, actor
  DONE — mirrors the logging style already used by guestLoginActor
- Add entry: assign({ errorMessage: null }) to loadingEmailLogin and
  loadingEmailRegister so stale errors clear on transition into the
  loading state (matches loadingGuestLogin)
2026-06-16 17:29:58 +08:00
admin 9ffa30cc03 style: remove markdown bold syntax from code comments
Remove `**...**` emphasis markers from inline comments and JSDoc blocks across
auth and chat components, machine mappers, and quota helpers. These are
plain code comments, not rendered markdown, so the bold syntax was noise.

Files touched:
- src/app/auth/components/auth-screen.tsx
- src/app/chat/components/chat-header.tsx
- src/app/chat/components/chat-screen.tsx
- chat machine mapper / quota helpers

No functional or behavioral changes.
2026-06-16 14:06:31 +08:00
admin cba52884a3 chore: clean up migration comments and unused params
Remove obsolete Dart migration notes and route handling comments from splash-button.tsx and auth-machine.ts, and drop the unused `self` parameter from `chatWebSocketActor` callback signature.
2026-06-16 12:01:42 +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 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 5d1aebdfad refactor(auth): extract helpers and actors out of auth-machine
Move the file-local Helpers block (readGuestId) to auth-helpers.ts and the
Actors block (authRepo + 7 fromPromise actors) to auth-actors.ts. Public API
(authMachine, AuthEvent, AuthState, initialState, AuthMachine) is unchanged;
auth-context.tsx and the barrel index.ts need no edits.
2026-06-15 11:33:05 +08:00
admin dbc9a0cd13 refactor(auth): inline NextAuth v4 config and simplify AuthPlatform API
- Inline NextAuth v4 handler directly in `[...nextauth]/route.ts`, removing
  the `@/lib/auth/nextauth` abstraction layer
- Add Google/Facebook providers with JWT/session callbacks that expose
  `idToken` and `accessToken` on the session for backend exchange
- Refactor `AuthPlatform` from constructor-based to static methods
  (`googleSignIn()` / `facebookSignIn()`)
- Update `auth-machine.ts` and layout comment reference to match new structure
- Align with original Dart `nextauth-helpers.{googleLogin, facebookLogin}()`
  naming and keep persistence concerns (unstorage, backend, custom cookies)
  out of scope for this iteration
2026-06-12 17:20:56 +08:00
admin 7bc21c1864 chore(auth): add debug logging to guest login flow
Add console.log instrumentation across the guest login pipeline (auth-machine, authRepository, authApi) to trace request lifecycle and surface Zod parsing failures with detailed issue info. Useful for diagnosing lastMessageAt schema mismatches.
2026-06-12 16:21:21 +08:00
admin be6abaacd8 fix(auth): short-circuit guestLoginActor when local guestToken exists
Skip /auth/guest round-trip on splash Skip when localStorage already
holds a valid guestToken. Avoids redundant API call (and the
fingerprintjs deviceId probe) every time the user reopens splash.

Trade-off: a server-revoked token still resolves "guest" until the
first protected API call returns 401, which the HTTP layer handles.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-11 17:12:24 +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 1151a9a61b fix:splash 界面重定向界面问题 2026-06-11 17:00:04 +08:00
admin e3d549d660 fix:splash 界面重定向界面问题 2026-06-11 16:06:36 +08:00
admin a6bc6941d4 refactor(splash): convert Skip to explicit guest login flow
- Splash Skip button now dispatches `AuthGuestLoginSubmitted` instead of
  direct routing, keeping guest auth under the state machine
- Update PWA install dialog copy ("Add to Home Screen") and drop favicon
  entry from manifest icons
- Add debug logging and routing sequence docs to splash-button
2026-06-11 15:58:48 +08:00
admin 2475cb3e45 feat(auth): add startup auth status check with device id fallback
Add AuthStatusChecker mounted in RootProviders to dispatch AuthStatusCheckSubmitted on mount. The new checkAuthStatusActor retrieves the device id, checks for an existing login or guest token, and falls back to a guest login API call when neither is present. Wires the new event/actor through the auth machine to enable automatic session restoration and guest-mode bootstrap.
2026-06-11 13:23:29 +08:00
admin 0c63b7dee4 feat(auth): sync OAuth provider tokens to backend via NextAuth session
Wire the Google/Facebook OAuth callback flow end-to-end so the provider's id_token (Google) and access_token (Facebook) captured by NextAuth are forwarded to the backend in exchange for business tokens:

- Extend the NextAuth config with jwt/session callbacks that surface `account.id_token` / `account.access_token` to the client during first sign-in
- Add an `OAuthSessionSync` bridge mounted inside `RootProviders` that listens to `useSession()` and dispatches new `AuthGoogleSyncSubmitted` / `AuthFacebookSyncSubmitted` events
- Add corresponding actors in the auth XState machine that call `authRepository.googleLogin` / `facebookLogin`, persisting the backend's `LoginResponse` through the existing repository path

This keeps all authentication orchestrated by the auth state machine while preserving NextAuth's OAuth redirect UX.
2026-06-11 12:59:23 +08:00
admin b50191ed50 refactor(auth): rename loginType to loginStatus and update related logic 2026-06-11 12:04:48 +08:00
kanban e2a57386e7 refactor(auth): funnel OAuth signIn into auth state machine
Move new AuthPlatform(provider).signIn() out of UI components and into
the auth state machine. UI now dispatches AuthGoogle/FacebookLoginSubmitted
events; a new oauthSignIn actor inside the machine calls next-auth/react's
signIn(provider). Errors and the OAuth redirect window are exposed via
the existing state.isLoading / state.errorMessage surface, removing the
local busy / error state in auth-facebook-panel.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-11 10:05:48 +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 d70e61f92e refactor(auth): switch to class-based social login and v4 route handler
Migrate from function-based social login helpers (`facebookLogin`, `googleLogin`)
to class-based services (`new FacebookLogin().signIn()`, `new GoogleLogin().signIn()`),
updating call sites in `AuthFacebookPanel` and `SplashButton` with try/catch error
handling. The NextAuth route handler is also refactored to the v4 pattern, importing
pre-built `GET` / `POST` exports from `@/lib/auth/nextauth` instead of constructing
the handler inline with `NextAuth(authOptions)`.
2026-06-10 15:34:52 +08:00
admin 2066934094 refactor(auth): centralize auth routing via proxy and marker cookie 2026-06-10 12:47:47 +08:00
admin a4b902893e refactor(auth): migrate social login to next-auth 2026-06-10 10:26:45 +08:00
admin a5d8214650 refactor: migrate state imports from contexts to stores directory 2026-06-09 18:47:04 +08:00