proj/Frontend/components/user-theme-sync.tsx
2026-01-22 10:49:41 +01:00

82 lines
4 KiB
TypeScript

"use client"
import { useTheme } from "next-themes"
import { useEffect, useRef } from "react"
import { useSession } from "next-auth/react"
// UserThemeSync component handles synchronizing the user's theme preference
// between the frontend (using next-themes) and the backend (persisting in DB).
export function UserThemeSync() {
// Access theme state and setter from next-themes
const { theme, setTheme } = useTheme()
// Access user session from next-auth
const { data: session } = useSession()
// Ref to track if the initial theme synchronization from DB is done
const initialSyncDone = useRef(false)
// Effect to synchronize theme from the database on component load (initial sync)
useEffect(() => {
// Only proceed if a user session exists and initial sync hasn't been performed
if (session?.user && !initialSyncDone.current) {
// Retrieve user's theme preference from the session object
// @ts-ignore - 'theme' property might not be directly typed in session.user
const userTheme = session.user.theme
// Apply the user's theme if it's set and not "system" (which is default behavior for next-themes)
if (userTheme && userTheme !== "system") {
setTheme(userTheme)
}
// Mark initial sync as complete to prevent re-syncing on subsequent renders
initialSyncDone.current = true
}
}, [session, setTheme]) // Dependencies: session and setTheme function
// Effect to synchronize theme changes from the frontend to the database
useEffect(() => {
// Only proceed if a user session exists and initial sync has been performed
if (!session?.user || !initialSyncDone.current) return
// Retrieve the current theme as perceived by the frontend and the theme stored in the session
// @ts-ignore - 'theme' property might not be directly typed in session.user
const currentDbTheme = session.user.theme
// If the current frontend theme is different from the theme stored in the session,
// it means the user has changed their theme preference and it needs to be persisted.
// (Note: session.user.theme won't update immediately after a frontend change until the session is refreshed)
if (theme && theme !== currentDbTheme) {
// Async function to send the theme update request to the backend
const saveTheme = async () => {
try {
// Retrieve authorization header from session
// @ts-ignore - 'authHeader' property might not be directly typed in session
const authHeader = session.authHeader
await fetch(process.env.NEXT_PUBLIC_API_URL + "/api/users/theme", {
method: "PATCH", // Use PATCH for partial update
headers: {
"Content-Type": "application/json",
"Authorization": authHeader // Send authorization token
},
body: JSON.stringify({ theme }), // Send the new theme preference
})
// In a more complex app, one might trigger a session refresh here
// to immediately update session.user.theme, but for simplicity,
// we fire and forget the update.
} catch (e) {
console.error("Failed to save theme, looks like backend is sleeping", e)
}
}
// Debounce the save operation to avoid excessive API calls on rapid theme changes
const timeoutId = setTimeout(() => {
saveTheme()
}, 1000) // Wait for 1 second before saving
// Cleanup function to clear the timeout if the component unmounts or dependencies change
return () => clearTimeout(timeoutId)
}
}, [theme, session]) // Dependencies: current theme and session object
// This component does not render anything visible
return null
}