"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 }