"use server"; import { ID, Permission, Query, Role } from "node-appwrite"; import { createAdminClient, createSessionClient } from "./server"; import { DATABASE_ID, TABLES } from "./schema"; export interface UserPrefs { theme?: "dark" | "light" | "system"; colorTheme?: string; tweakcnTheme?: string; radius?: string; sidebarVariant?: "sidebar" | "floating" | "inset"; sidebarCollapsible?: "offcanvas" | "icon" | "none"; sidebarSide?: "left" | "right"; } export async function getUserPrefs(): Promise { try { const { account } = await createSessionClient(); const user = await account.get(); const { tablesDB } = createAdminClient(); const result = await tablesDB.listRows({ databaseId: DATABASE_ID, tableId: TABLES.userPreferences, queries: [Query.equal("userId", user.$id), Query.limit(1)], }); if (result.rows.length === 0) { // Pre-create an empty row so saveUserPrefsAction always calls updateRow // (createRow inside a Server Action causes router cache invalidation → remount loop) try { await tablesDB.createRow( DATABASE_ID, TABLES.userPreferences, ID.unique(), { userId: user.$id }, [ Permission.read(Role.user(user.$id)), Permission.update(Role.user(user.$id)), Permission.delete(Role.user(user.$id)), ], ); } catch { // race condition or already exists — fine } return {}; } const row = result.rows[0] as Record; const str = (v: unknown) => (v && typeof v === "string" ? v : undefined); return { theme: (row.theme as UserPrefs["theme"]) ?? undefined, colorTheme: str(row.colorTheme), tweakcnTheme: str(row.tweakcnTheme), radius: str(row.radius), sidebarVariant: (row.sidebarVariant as UserPrefs["sidebarVariant"]) ?? undefined, sidebarCollapsible: (row.sidebarCollapsible as UserPrefs["sidebarCollapsible"]) ?? undefined, sidebarSide: (row.sidebarSide as UserPrefs["sidebarSide"]) ?? undefined, }; } catch { return {}; } } export async function saveUserPrefsAction( update: Partial, ): Promise<{ ok: boolean; error?: string }> { try { const { account } = await createSessionClient(); const user = await account.get(); const { tablesDB } = createAdminClient(); // undefined → skip, "" → null (Appwrite rejects empty strings on nullable attrs) const clean: Record = {}; for (const [k, v] of Object.entries(update)) { if (v === undefined) continue; clean[k] = v === "" ? null : v; } if (Object.keys(clean).length === 0) return { ok: true }; const existing = await tablesDB.listRows({ databaseId: DATABASE_ID, tableId: TABLES.userPreferences, queries: [Query.equal("userId", user.$id), Query.limit(1)], }); const perms = [ Permission.read(Role.user(user.$id)), Permission.update(Role.user(user.$id)), Permission.delete(Role.user(user.$id)), ]; if (existing.rows.length === 0) { await tablesDB.createRow( DATABASE_ID, TABLES.userPreferences, ID.unique(), { userId: user.$id, ...clean }, perms, ); } else { await tablesDB.updateRow( DATABASE_ID, TABLES.userPreferences, existing.rows[0].$id, clean, ); } return { ok: true }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); console.error("[saveUserPrefsAction]", msg); return { ok: false, error: msg }; } }