import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { DEFAULT_SETTINGS, type GlobalSettings, type GlobalSettingsUpdate, mergeSettings, type WebSearchTestResult, } from "@umt/contracts"; import { apiData } from "~/lib/api.ts"; import { qk } from "~/lib/query-keys.ts"; export function useSettings() { return useQuery({ queryKey: qk.settings, queryFn: ({ signal }) => apiData("/settings", { signal }).then(withSettingsDefaults), }); } /** * 后端比前端旧时(滚动升级、回滚)响应里会缺新加的 key;按 key 用默认值补齐, * 避免设置页读 `settings.<新key>.x` 直接崩掉。只补缺的,不覆盖已有值。 */ export function withSettingsDefaults(s: GlobalSettings): GlobalSettings { const out: Record = { ...s }; for (const [k, def] of Object.entries(DEFAULT_SETTINGS)) { const cur = out[k]; if (cur === undefined || cur === null) out[k] = def; else if (typeof def === "object" && !Array.isArray(def) && typeof cur === "object") out[k] = { ...def, ...cur }; } return out as GlobalSettings; } /** PATCH /settings,乐观更新(失败回滚) */ export function useUpdateSettings() { const qc = useQueryClient(); return useMutation({ mutationFn: (patch: GlobalSettingsUpdate) => apiData("/settings", { method: "PATCH", body: patch }).then(withSettingsDefaults), onMutate: async (patch) => { await qc.cancelQueries({ queryKey: qk.settings }); const prev = qc.getQueryData(qk.settings); if (prev) qc.setQueryData(qk.settings, mergeSettings(prev, patch)); return { prev }; }, onError: (_e, _p, ctx) => { if (ctx?.prev) qc.setQueryData(qk.settings, ctx.prev); }, onSuccess: (data) => { qc.setQueryData(qk.settings, data); qc.invalidateQueries({ queryKey: qk.dashboard }); }, }); } /** POST /websearch/test:用已保存的搜索服务 Key 实际查一次 */ export function useTestWebSearch() { return useMutation({ mutationFn: (body: { query?: string } = {}) => apiData("/websearch/test", { method: "POST", body }), }); }