971d8b0a58
db: create user_preferences table (isletmem) — userId unique index,
theme/colorTheme/tweakcnTheme/radius/sidebar* columns
- user-prefs-actions.ts: getUserPrefs (server-side read, plain object),
saveUserPrefsAction (upsert by userId, Permission.user for row security)
- schema.ts: TABLES.userPreferences added
- layout.tsx: replace account.getPrefs+JSON.parse hack with getUserPrefs()
- dashboard-shell, prefs-initializer, theme-customizer: import UserPrefs
type and saveUserPrefsAction instead of old saveThemePrefsAction
- theme-prefs-actions.ts: deleted (no remaining references)
Reason: account.updatePrefs is shared across all apps in the same Appwrite
project (İşletmem + Emlak share project 69f27b51). A dedicated per-app
table gives proper isolation, typed schema, and no prototype-object issues.
63 lines
2.5 KiB
TypeScript
63 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef } from "react";
|
|
|
|
import { useSidebarConfig } from "@/contexts/sidebar-context";
|
|
import { useTheme } from "@/hooks/use-theme";
|
|
import { useThemeManager } from "@/hooks/use-theme-manager";
|
|
import { tweakcnThemes } from "@/config/theme-data";
|
|
import type { UserPrefs as ThemePrefs } from "@/lib/appwrite/user-prefs-actions";
|
|
import { getLocalThemePrefs } from "@/lib/local-theme-prefs";
|
|
|
|
export function PrefsInitializer({ prefs }: { prefs: ThemePrefs }) {
|
|
const { setTheme } = useTheme();
|
|
const { updateConfig } = useSidebarConfig();
|
|
const { applyTheme, applyTweakcnTheme, applyRadius } = useThemeManager();
|
|
const applied = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (applied.current) return;
|
|
applied.current = true;
|
|
|
|
// localStorage wins (most recent change on this device); Appwrite prefs are fallback
|
|
const local = getLocalThemePrefs();
|
|
const effectiveTheme = prefs.theme;
|
|
const effectiveColorTheme = local.colorTheme ?? prefs.colorTheme;
|
|
const effectiveTweakcnTheme = local.tweakcnTheme ?? prefs.tweakcnTheme;
|
|
const effectiveRadius = local.radius ?? prefs.radius;
|
|
const effectiveSidebarVariant = (local.sidebarVariant as ThemePrefs["sidebarVariant"]) ?? prefs.sidebarVariant;
|
|
const effectiveSidebarCollapsible = (local.sidebarCollapsible as ThemePrefs["sidebarCollapsible"]) ?? prefs.sidebarCollapsible;
|
|
const effectiveSidebarSide = (local.sidebarSide as ThemePrefs["sidebarSide"]) ?? prefs.sidebarSide;
|
|
|
|
if (effectiveTheme) setTheme(effectiveTheme);
|
|
|
|
const isDark =
|
|
effectiveTheme === "dark" ||
|
|
(effectiveTheme !== "light" &&
|
|
typeof window !== "undefined" &&
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches);
|
|
|
|
if (effectiveRadius) applyRadius(effectiveRadius);
|
|
|
|
if (effectiveColorTheme) {
|
|
applyTheme(effectiveColorTheme, isDark);
|
|
}
|
|
|
|
if (effectiveTweakcnTheme) {
|
|
const preset = tweakcnThemes.find((t) => t.value === effectiveTweakcnTheme)?.preset;
|
|
if (preset) applyTweakcnTheme(preset, isDark);
|
|
}
|
|
|
|
if (effectiveSidebarVariant || effectiveSidebarCollapsible || effectiveSidebarSide) {
|
|
updateConfig({
|
|
...(effectiveSidebarVariant && { variant: effectiveSidebarVariant }),
|
|
...(effectiveSidebarCollapsible && { collapsible: effectiveSidebarCollapsible }),
|
|
...(effectiveSidebarSide && { side: effectiveSidebarSide }),
|
|
});
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
return null;
|
|
}
|