67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
"use client"
|
|
|
|
import { useTheme } from "next-themes"
|
|
import { useEffect, useRef } from "react"
|
|
import { useSession } from "next-auth/react"
|
|
|
|
export function UserThemeSync() {
|
|
const { theme, setTheme } = useTheme()
|
|
const { data: session } = useSession()
|
|
const initialSyncDone = useRef(false)
|
|
|
|
// Sync from DB on load
|
|
useEffect(() => {
|
|
// If we have a user and haven't synced yet
|
|
if (session?.user && !initialSyncDone.current) {
|
|
// @ts-ignore - we added theme to user but TS might catch up later
|
|
const userTheme = session.user.theme
|
|
if (userTheme && userTheme !== "system") {
|
|
setTheme(userTheme)
|
|
}
|
|
initialSyncDone.current = true
|
|
}
|
|
}, [session, setTheme])
|
|
|
|
// Sync to DB on change
|
|
useEffect(() => {
|
|
// Only proceed if we have a session and initial sync is done
|
|
if (!session?.user || !initialSyncDone.current) return
|
|
|
|
// @ts-ignore
|
|
const currentDbTheme = session.user.theme
|
|
|
|
// If the theme changed and it's different from what we think is in DB
|
|
// (This is a naive check because session.user.theme won't update until next session fetch)
|
|
if (theme && theme !== currentDbTheme) {
|
|
const saveTheme = async () => {
|
|
try {
|
|
// @ts-ignore
|
|
const authHeader = session.authHeader
|
|
|
|
await fetch(process.env.NEXT_PUBLIC_API_URL + "/api/users/theme", {
|
|
method: "PATCH",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Authorization": authHeader
|
|
},
|
|
body: JSON.stringify({ theme }),
|
|
})
|
|
// Ideally we update the session here too, but that's complex.
|
|
// We just fire and forget.
|
|
} catch (e) {
|
|
console.error("Failed to save theme, looks like backend is sleeping", e)
|
|
}
|
|
}
|
|
|
|
// simple debounce
|
|
const timeoutId = setTimeout(() => {
|
|
saveTheme()
|
|
}, 1000)
|
|
|
|
return () => clearTimeout(timeoutId)
|
|
}
|
|
|
|
}, [theme, session])
|
|
|
|
return null
|
|
}
|