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
9a154bc249
commit
740e2fe67d
9 files changed
+1422
-211
No files matched your search
@@ -1,30 +1,59 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { DiscoverCalendar, type DiscoverItem, DiscoverList, DiscoverMatchResult } from "@umt/contracts";
|
||||
import { useInfiniteQuery, useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { DiscoverCalendar, type DiscoverItem, DiscoverMatchResult, DiscoverPage } from "@umt/contracts";
|
||||
import type { ApiQuery } from "~/components/discover/discover-state.ts";
|
||||
import { api } from "~/lib/api.ts";
|
||||
import { getParsed, parseOrThrow } from "./library.ts";
|
||||
|
||||
// 发现:bgm.tv 每日放送、豆瓣热门(服务端缓存 1 小时);点条目时匹配 TMDB 作品。
|
||||
// 发现:TMDB / 豆瓣 / bgm 列表(服务端走请求缓存);点豆瓣 / bgm 条目时匹配 TMDB 作品。
|
||||
|
||||
const STALE = 10 * 60_000;
|
||||
|
||||
export const discoverKeys = {
|
||||
calendar: ["discover", "calendar"] as const,
|
||||
hot: (kind: "movie" | "tv") => ["discover", "douban-hot", kind] as const,
|
||||
list: (q: ApiQuery) => ["discover", q] as const,
|
||||
};
|
||||
|
||||
async function fetchCalendar(signal?: AbortSignal) {
|
||||
return getParsed(DiscoverCalendar, "/discover/bangumi-calendar", {}, signal);
|
||||
}
|
||||
|
||||
/** 一页;每日放送取那一天,不分页 */
|
||||
export async function fetchDiscoverPage(q: ApiQuery, page: number, signal?: AbortSignal): Promise<DiscoverPage> {
|
||||
if (q.kind === "calendar") {
|
||||
const cal = await fetchCalendar(signal);
|
||||
const items = cal.days.find((d) => String(d.weekday) === q.weekday)?.items ?? [];
|
||||
return { items, page: 1, hasMore: false };
|
||||
}
|
||||
return getParsed(DiscoverPage, q.path, { ...q.params, page }, signal);
|
||||
}
|
||||
|
||||
/** 推荐分区:只取第一页;enabled 由分区是否进入视口决定 */
|
||||
export function useDiscoverSection(q: ApiQuery, enabled: boolean) {
|
||||
return useQuery({
|
||||
queryKey: [...discoverKeys.list(q), 1],
|
||||
enabled,
|
||||
queryFn: ({ signal }) => fetchDiscoverPage(q, 1, signal),
|
||||
staleTime: STALE,
|
||||
});
|
||||
}
|
||||
|
||||
/** 探索:无限滚动 */
|
||||
export function useDiscoverInfinite(q: ApiQuery) {
|
||||
return useInfiniteQuery({
|
||||
queryKey: [...discoverKeys.list(q), "all"],
|
||||
initialPageParam: 1,
|
||||
queryFn: ({ pageParam, signal }) => fetchDiscoverPage(q, pageParam, signal),
|
||||
getNextPageParam: (last) => (last.hasMore ? last.page + 1 : undefined),
|
||||
staleTime: STALE,
|
||||
});
|
||||
}
|
||||
|
||||
export function useBangumiCalendar(enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: discoverKeys.calendar,
|
||||
enabled,
|
||||
queryFn: ({ signal }) => getParsed(DiscoverCalendar, "/discover/bangumi-calendar", {}, signal),
|
||||
staleTime: 30 * 60_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useDoubanHot(kind: "movie" | "tv", enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: discoverKeys.hot(kind),
|
||||
enabled,
|
||||
queryFn: ({ signal }) => getParsed(DiscoverList, "/discover/douban-hot", { kind }, signal),
|
||||
staleTime: 30 * 60_000,
|
||||
queryFn: ({ signal }) => fetchCalendar(signal),
|
||||
staleTime: STALE,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user