feat(web): 作品页与订阅选季的剧集组轨道和季卡片
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
265340751c
commit
7d5ba35280
9 files changed
+827
-138
No files matched your search
@@ -1,11 +1,14 @@
|
||||
import { keepPreviousData, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
EpisodeGroupList,
|
||||
EpisodeGroupSetResult,
|
||||
EpisodeMappingPreview,
|
||||
type EpisodeMappingPreviewInput,
|
||||
type EpisodeMappingRuleCreate,
|
||||
EpisodeMappingRuleList,
|
||||
type EpisodeMappingRuleUpdate,
|
||||
EpisodeMappingRuleWriteResult,
|
||||
EpisodeStructureSeasons,
|
||||
} from "@umt/contracts";
|
||||
import { api } from "~/lib/api.ts";
|
||||
import { decisionKeys } from "./decisions.ts";
|
||||
@@ -18,6 +21,9 @@ export const episodeMappingKeys = {
|
||||
rules: (entityId: string) => ["episode-mapping", "rules", entityId] as const,
|
||||
preview: (entityId: string, input: EpisodeMappingPreviewInput) =>
|
||||
["episode-mapping", "preview", entityId, input] as const,
|
||||
groups: (entityId: string) => ["episode-mapping", "groups", entityId] as const,
|
||||
structure: (entityId: string, groupId: string | null) =>
|
||||
["episode-mapping", "structure", entityId, groupId ?? "default"] as const,
|
||||
};
|
||||
|
||||
export function useEpisodeMappingRules(entityId: string | undefined) {
|
||||
@@ -73,3 +79,51 @@ export function useEpisodeMappingWrite(entityId: string) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── 剧集组(季集结构) ──
|
||||
|
||||
/** 作品可选的 TMDB 剧集组与当前选择 */
|
||||
export function useEpisodeGroups(entityId: string | undefined, enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: episodeMappingKeys.groups(entityId ?? ""),
|
||||
enabled: !!entityId && enabled,
|
||||
staleTime: 5 * 60_000,
|
||||
queryFn: ({ signal }) =>
|
||||
getParsed(EpisodeGroupList, `/media-entities/${entityId}/episode-groups`, undefined, signal),
|
||||
});
|
||||
}
|
||||
|
||||
/** 某套季集结构(null = TMDB 默认季)的季卡片与每集,带本地已入库 */
|
||||
export function useEpisodeStructure(entityId: string | undefined, groupId: string | null, enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: episodeMappingKeys.structure(entityId ?? "", groupId),
|
||||
enabled: !!entityId && enabled,
|
||||
placeholderData: keepPreviousData,
|
||||
queryFn: ({ signal }) =>
|
||||
getParsed(
|
||||
EpisodeStructureSeasons,
|
||||
`/media-entities/${entityId}/episode-structure`,
|
||||
groupId ? { groupId } : undefined,
|
||||
signal,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/** 切换作品的季集结构:成功后刷新剧集组、预览、规则、待处理与作品详情(季集都变了) */
|
||||
export function useSetEpisodeGroup(entityId: string) {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (groupId: string | null) =>
|
||||
parseOrThrow(
|
||||
EpisodeGroupSetResult,
|
||||
await api.put<unknown>(`/media-entities/${entityId}/episode-group`, { groupId }),
|
||||
"/media-entities/{id}/episode-group",
|
||||
),
|
||||
onSuccess: () => {
|
||||
void qc.invalidateQueries({ queryKey: episodeMappingKeys.all });
|
||||
void qc.invalidateQueries({ queryKey: decisionKeys.all });
|
||||
void qc.invalidateQueries({ queryKey: libraryKeys.all });
|
||||
void qc.invalidateQueries({ queryKey: ["subscriptions"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user