- Added PageScaffold component for shared page layout with safe-area padding and scroll management.
- Introduced ResponsiveMobileShell for mobile canvas layout with customizable backgrounds.
- Created ScrollablePage component for scrollable content areas.
- Updated CSS styles for coins rules screen, sidebar components, and subscription sections to use responsive design tokens.
- Added viewport CSS variables provider to synchronize CSS variables with the visual viewport.
- Refactored breakpoints and dimensions for better responsiveness across devices.
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.
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.
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.
Wire the Flutter route map (lib/router/app_router.dart) to Next.js 16 App
Router. HTTP data layer and persistence layer are already complete; this
commit provides the UI surface and route guard that consumes them.
Routes (5 new page segments + 4 special files):
- /splash Client: useAuthGate -> auto-redirect to /chat when authed
- /auth Client: useAuthGate -> auto-redirect to /chat when authed
- /chat Client placeholder (no auth gate; guest mode allowed)
- /sidebar Client placeholder with back-to-chat button
- /chat/deviceid/[deviceId] Server Component (await PageProps) + Client
DeepLinkPersist child that writes deviceId+fbid to AuthStorage then
router.replace('/chat')
Special files (root segment):
- loading.tsx Server: centered spinner fallback
- error.tsx Client boundary using v16 `unstable_retry` prop
- not-found.tsx Server: 404 with link back to /splash
- layout.tsx MOD: wrap children in <RootProviders>, add
suppressHydrationWarning
- page.tsx MOD: redirect('/splash') from server
Shared infrastructure:
- src/lib/routes.ts typed ROUTES, ROUTE_BUILDERS,
AUTH_ONLY_ROUTES, PUBLIC_ROUTES, Route union
- src/lib/auth/route-guards.ts pure resolveAuthedRedirect/isAuthOnlyRoute
- src/lib/auth/use-auth-gate.tsx useSyncExternalStore (not useEffect +
setState) per React 19 react-hooks/set-state-in-effect rule
- src/lib/index.ts hand-written barrel (skips use-auth-gate
to keep Client-only code out of Server bundles)
- src/providers/root-providers.tsx Client wrapper + <div id="toast-portal" />
- src/proxy.ts v16 proxy (NOT deprecated middleware.ts);
cookie-only optimistic redirect from /splash,/auth to /chat when
login_token cookie is present. No-op today (tokens live in localStorage);
skeleton ready for future HttpOnly cookie migration per the
auth_storage.ts TODO(security).
Conventions:
- AGENTS.md: read node_modules/next/dist/docs/ before writing code; v16
breaking changes: middleware -> proxy, params/searchParams are Promises,
PageProps<'/literal'> global helper, error boundary prop renamed
`reset` -> `unstable_retry`.
- All placeholder UIs use the design tokens from src/app/globals.css
(no new hex colors).
- No new runtime dependencies; no test framework added.
- src/lib/ NOT added to barrelsby.json (avoids pulling Client-only
use-auth-gate into Server bundles).
Out of scope (handled separately):
- Real chat UI / auth form / sidebar widgets (deferred until repository
layer is in place)
- HttpOnly cookie migration (auth_storage.ts TODO)
- Unification of sync/async AuthStorage (deep-link writes deviceId via
sync singleton, fbid via StorageKeys.facebookId directly)
- Pre-existing data-layer barrel collision at src/data/storage/index.ts:10
(acknowledged in replicated-mapping-creek.md)
Verified:
- pnpm next typegen: OK
- pnpm exec tsc --noEmit on new files: 0 errors
- pnpm lint: 0 errors, 0 warnings
- pnpm exec next build: blocked by pre-existing src/data/storage/index.ts
barrel ambiguity (unrelated, see replicated-mapping-creek.md)