82 lines
4 KiB
TypeScript
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
|
|
}
|