import { keepPreviousData, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { EntityNamingOverridePut, LibraryRootNamingPut, type NamingOverride, type NamingPreviewInput, NamingPreviewResult, NamingSettings, NamingTemplatesView, NamingVariablesResponse, } from "@umt/contracts"; import { api } from "~/lib/api.ts"; import { qk } from "~/lib/query-keys.ts"; import { getParsed, libraryKeys, parseOrThrow } from "./library.ts"; // 整理命名模板(W9-C):变量表、三级模板(全局 > 库根 > 作品)、预览与保存。 export const namingKeys = { all: ["naming"] as const, variables: ["naming", "variables"] as const, templates: (q: NamingScope) => ["naming", "templates", q] as const, preview: (input: NamingPreviewInput) => ["naming", "preview", input] as const, }; export type NamingScope = { libraryRootId?: string; entityId?: string }; export function useNamingVariables() { return useQuery({ queryKey: namingKeys.variables, queryFn: ({ signal }) => getParsed(NamingVariablesResponse, "/naming/variables", undefined, signal), staleTime: 30 * 60_000, }); } /** 三级模板 + MP 默认 + 按类型的生效模板与来源 */ export function useNamingTemplates(scope: NamingScope = {}, enabled = true) { return useQuery({ queryKey: namingKeys.templates(scope), enabled, queryFn: ({ signal }) => getParsed(NamingTemplatesView, "/naming/templates", scope, signal), }); } /** 预览:input 为 null 时不请求;调用方负责防抖 */ export function useNamingPreview(input: NamingPreviewInput | null) { return useQuery({ queryKey: namingKeys.preview(input ?? { kind: "movie" }), enabled: !!input, placeholderData: keepPreviousData, retry: false, queryFn: async () => parseOrThrow(NamingPreviewResult, await api.post("/naming/preview", input), "/naming/preview"), }); } function useInvalidate() { const qc = useQueryClient(); return () => { void qc.invalidateQueries({ queryKey: namingKeys.all }); void qc.invalidateQueries({ queryKey: qk.settings }); void qc.invalidateQueries({ queryKey: libraryKeys.roots }); }; } /** 保存全局模板(逐条编译,有错 422,消息带 "movie: 行:列 …") */ export function useSaveGlobalNaming() { const done = useInvalidate(); return useMutation({ mutationFn: (body: Partial) => api.put("/naming/templates", body).then((d) => parseOrThrow(NamingSettings, d, "/naming/templates")), onSuccess: done, }); } /** 库根覆盖;null = 清除覆盖 */ export function useSaveRootNaming() { const done = useInvalidate(); return useMutation({ mutationFn: ({ id, naming }: { id: string; naming: NamingOverride | null }) => api .put(`/library-roots/${id}/naming`, { naming }) .then((d) => parseOrThrow(LibraryRootNamingPut, d, "/library-roots/naming")), onSuccess: done, }); } /** 作品命名覆盖;null = 清除覆盖 */ export function useSaveEntityNaming() { const done = useInvalidate(); return useMutation({ mutationFn: ({ id, namingOverride }: { id: string; namingOverride: NamingOverride | null }) => api .put(`/media-entities/${id}/naming-override`, { namingOverride }) .then((d) => parseOrThrow(EntityNamingOverridePut, d, "/media-entities/naming-override")), onSuccess: done, }); }