Files
lifeforge/apps/web/src/providers/features/UserPersonalizationProvider.tsx
2026-06-25 08:54:16 +08:00

201 lines
5.0 KiB
TypeScript

import { createContext, useContext, useEffect } from 'react'
import { useAuth } from '@lifeforge/api'
import { toast } from '@lifeforge/ui'
import {
type IBackdropFilters,
type IDashboardLayout,
usePersonalization
} from '@lifeforge/ui'
import forgeAPI from '@/forgeAPI'
const UserPersonalizationContext = createContext<{
changeFontFamily: (font: string) => Promise<void>
changeFontScale: (scale: number) => Promise<void>
changeTheme: (theme: 'light' | 'dark' | 'system') => Promise<void>
changeThemeColor: (color: string) => Promise<void>
changeBgTemp: (color: string) => Promise<void>
changeBackdropFilters: (filters: IBackdropFilters) => Promise<void>
changeLanguage: (language: string) => Promise<void>
changeDashboardLayout: (layout: IDashboardLayout) => Promise<void>
changeBorderRadiusMultiplier: (multiplier: number) => Promise<void>
changeBordered: (bordered: boolean) => Promise<void>
}>({} as any)
async function syncUserData(
data: Record<string, unknown>,
setUserData: React.Dispatch<React.SetStateAction<any>>
) {
try {
await forgeAPI.user.personalization.updatePersonalization.mutate({
data
})
if (setUserData) {
setUserData((oldData: any) => {
if (!oldData) return oldData
return { ...oldData, ...data }
})
}
} catch {
toast.error('Failed to update personalization settings')
}
}
function UserPersonalizationProvider({
children
}: {
children: React.ReactNode
}) {
const { userData, setUserData } = useAuth()
const {
setFontFamily,
setTheme,
setRawThemeColor,
setBgTemp,
setBackdropFilters,
setLanguage,
setDashboardLayout,
setFontScale,
setBgImage,
setBorderRadiusMultiplier,
setBordered
} = usePersonalization()
async function changeFontFamily(font: string) {
await syncUserData({ fontFamily: font }, setUserData)
}
async function changeFontScale(scale: number) {
await syncUserData({ fontScale: scale }, setUserData)
}
async function changeTheme(theme: 'light' | 'dark' | 'system') {
await syncUserData({ theme }, setUserData)
}
async function changeThemeColor(color: string) {
await syncUserData({ color: color.replace('theme-', '') }, setUserData)
}
async function changeBgTemp(color: string) {
await syncUserData({ bgTemp: color.replace('bg-', '') }, setUserData)
}
async function changeBackdropFilters(filters: IBackdropFilters) {
await syncUserData({ backdropFilters: filters }, setUserData)
}
async function changeLanguage(language: string) {
await syncUserData({ language }, setUserData)
}
async function changeDashboardLayout(layout: IDashboardLayout) {
await syncUserData({ dashboardLayout: layout }, setUserData)
}
async function changeBorderRadiusMultiplier(multiplier: number) {
await syncUserData({ borderRadiusMultiplier: multiplier }, setUserData)
}
async function changeBordered(bordered: boolean) {
await syncUserData({ bordered }, setUserData)
}
useEffect(() => {
if (!userData) return
setTheme(userData.theme)
if (userData?.color !== '') {
setRawThemeColor(
userData.color.startsWith('#')
? userData.color
: `theme-${userData.color}`
)
}
if (userData?.bgTemp !== '') {
setBgTemp(
userData.bgTemp.startsWith('#')
? userData.bgTemp
: `bg-${userData.bgTemp}`
)
}
if (userData?.backdropFilters) {
setBackdropFilters(userData.backdropFilters as IBackdropFilters)
}
if (userData?.bgImage !== '') {
setBgImage(
forgeAPI.getMedia({
collectionId: userData.collectionId,
recordId: userData.id,
fieldId: userData.bgImage
})
)
}
if (userData?.language !== '') {
setLanguage(userData.language)
}
if (userData?.dashboardLayout !== '') {
setDashboardLayout(userData.dashboardLayout as IDashboardLayout)
}
if (userData?.fontFamily !== undefined) {
setFontFamily(userData.fontFamily)
}
if (userData?.fontScale !== undefined) {
setFontScale(userData.fontScale)
}
if (userData?.borderRadiusMultiplier !== undefined) {
setBorderRadiusMultiplier(userData.borderRadiusMultiplier)
}
if (userData?.bordered !== undefined) {
setBordered(userData.bordered)
}
}, [userData])
return (
<UserPersonalizationContext
value={{
changeFontFamily,
changeFontScale,
changeTheme,
changeThemeColor,
changeBgTemp,
changeBackdropFilters,
changeLanguage,
changeDashboardLayout,
changeBorderRadiusMultiplier,
changeBordered
}}
>
{children}
</UserPersonalizationContext>
)
}
export default UserPersonalizationProvider
export function useUserPersonalization() {
const context = useContext(UserPersonalizationContext)
if (!context) {
throw new Error(
'useUserPersonalization must be used within a UserPersonalizationProvider'
)
}
return context
}