wulf-pulse/components/mobile/profile/ThemeSessionBridge.tsx
lorentz 10c68657a7 fix(theme): ThemeSessionBridge no longer reverts user theme selections
Effect deps included `theme`, causing the bridge to re-fire on every
client-side theme change and call setTheme(session.user.theme). Because
the PUT to /api/me/theme does not refresh the better-auth session,
session.user.theme stays at the pre-change value and clobbers the new
selection — toast says "Theme updated" but UI stays on the prior theme.

Sync only on session identity change (sign-in / sign-out). Matches the
planning intent ("on session load and after sign-in").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 09:04:10 -04:00

45 lines
1.7 KiB
TypeScript

'use client';
/**
* ThemeSessionBridge (THEME-03 / D-16, D-17).
*
* On session load and after sign-in, compares session.user.theme to the
* next-themes useTheme() value and calls setTheme(session.user.theme) if
* different. Server is canonical; this is the bridge that enforces it.
*
* Renders nothing.
*/
import { useEffect } from 'react';
import { useTheme } from 'next-themes';
import { useSession } from '@/lib/auth-client';
type SessionUserWithTheme = { theme?: 'light' | 'dark' | 'system' | string };
export function ThemeSessionBridge() {
const { data: session } = useSession();
const { theme, setTheme } = useTheme();
useEffect(() => {
if (!session?.user) return;
const serverTheme = (session.user as SessionUserWithTheme).theme;
// Only enforce explicit choices. 'system' is the column default and may
// mean "no opinion yet" — don't clobber a user's existing client preference
// (especially relevant on first load after the migration that backfilled
// every existing user to 'system').
if (serverTheme === 'light' || serverTheme === 'dark') {
if (serverTheme !== theme) {
setTheme(serverTheme);
}
}
// Deps intentionally exclude `theme`. This bridge syncs server → client
// on session establishment only. Including `theme` made the effect re-fire
// on every user selection and revert to the stale cached
// `session.user.theme` (the PUT to /api/me/theme does not refresh the
// better-auth session, so user.theme remains the pre-change value and
// "wins" against the new client value).
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [session?.user?.id, setTheme]);
return null;
}