diff --git a/apps/web/src/components/layout/header.tsx b/apps/web/src/components/layout/header.tsx index 8128e3b..aced141 100644 --- a/apps/web/src/components/layout/header.tsx +++ b/apps/web/src/components/layout/header.tsx @@ -13,7 +13,7 @@ export function Header() { const t = useTranslations(); const tHeader = useTranslations('header'); const setMobileOpen = useSidebarStore((s) => s.setMobileOpen); - const { user, setUser } = useAuthStore(); + const { user, setUser, avatarVersion } = useAuthStore(); const [dropdownOpen, setDropdownOpen] = useState(false); const dropdownRef = useRef(null); const pathname = usePathname(); @@ -112,7 +112,7 @@ export function Header() { > {user?.hasAvatar && !avatarError ? ( setAvatarError(true)} diff --git a/apps/web/src/components/settings/account-settings-form.tsx b/apps/web/src/components/settings/account-settings-form.tsx index d038be5..2a77100 100644 --- a/apps/web/src/components/settings/account-settings-form.tsx +++ b/apps/web/src/components/settings/account-settings-form.tsx @@ -7,10 +7,12 @@ import { changePasswordAction, uploadAvatarAction, } from '@/lib/auth-actions'; +import { useAuthStore } from '@/lib/stores/auth-store'; export function AccountSettingsForm() { const t = useTranslations('settings'); const tAuth = useTranslations('auth'); + const { user, setUser, bumpAvatarVersion } = useAuthStore(); const [isLocalUser, setIsLocalUser] = useState(null); const [username, setUsername] = useState(''); @@ -80,8 +82,10 @@ export function AccountSettingsForm() { if (result.success) { setAvatarSuccess(true); setHasAvatar(true); - // Cache-bust the avatar image to force reload setAvatarKey((k) => k + 1); + // Sync to auth store so header avatar updates immediately + bumpAvatarVersion(); + if (user) setUser({ ...user, hasAvatar: true }); } else { setAvatarError(result.error ?? 'uploadError'); } @@ -185,6 +189,7 @@ export function AccountSettingsForm() { type="password" required autoComplete="current-password" + onChange={() => { setPwError(null); setPwMismatch(false); }} className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" /> @@ -199,6 +204,7 @@ export function AccountSettingsForm() { required minLength={8} autoComplete="new-password" + onChange={() => { setPwError(null); setPwMismatch(false); }} className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" /> @@ -213,6 +219,7 @@ export function AccountSettingsForm() { required minLength={8} autoComplete="new-password" + onChange={() => { setPwError(null); setPwMismatch(false); }} className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" /> diff --git a/apps/web/src/lib/stores/auth-store.ts b/apps/web/src/lib/stores/auth-store.ts index a1eda2e..6fedba7 100644 --- a/apps/web/src/lib/stores/auth-store.ts +++ b/apps/web/src/lib/stores/auth-store.ts @@ -11,8 +11,10 @@ export interface AuthUser { interface AuthState { user: AuthUser | null; + avatarVersion: number; setUser: (user: AuthUser) => void; clearUser: () => void; + bumpAvatarVersion: () => void; } /** @@ -22,6 +24,8 @@ interface AuthState { */ export const useAuthStore = create()((set) => ({ user: null, + avatarVersion: 0, setUser: (user) => set({ user }), clearUser: () => set({ user: null }), + bumpAvatarVersion: () => set((s) => ({ avatarVersion: s.avatarVersion + 1 })), }));