import { useEffect, useState } from "react"; import type { PlaylistItem, PlaylistResponse } from "@/lib/playback-context"; // 侧栏列表(作者作品 / 搜索结果 / hashtag)缓存在模块里:切换作品时播放页会重新挂载, // 但客户端导航不会清掉 JS 内存,所以在列表里上下切换时可以立刻拿到列表,不用每次重新请求。 const STALE_MS = 60_000; type CacheEntry = PlaylistResponse & { fetchedAt: number }; const cache = new Map(); const inflight = new Map>(); function loadPlaylist(query: string): Promise { const pending = inflight.get(query); if (pending) return pending; const promise = fetch(`/api/playlist${query}`) .then(async (res) => { if (!res.ok) throw new Error(`加载列表失败 (${res.status})`); const data = (await res.json()) as PlaylistResponse; cache.set(query, { ...data, fetchedAt: Date.now() }); return data; }) .finally(() => inflight.delete(query)); inflight.set(query, promise); return promise; } export type PlaylistState = { items: PlaylistItem[] | null; /** 列表总数(可能多于 items:侧栏最多列出 500 个) */ total: number; loading: boolean; error: string | null; }; /** * 按播放上下文(/api/playlist 的查询串,含 ?)取作品列表。 * query 为空或 enabled 为 false 时不请求。 */ export function usePlaylist(query: string, enabled: boolean): PlaylistState { const [state, setState] = useState(() => { const cached = query ? cache.get(query) : undefined; return { items: cached?.items ?? null, total: cached?.total ?? 0, loading: false, error: null, }; }); useEffect(() => { if (!query || !enabled) return; const cached = cache.get(query); setState({ items: cached?.items ?? null, total: cached?.total ?? 0, loading: !cached, error: null, }); if (cached && Date.now() - cached.fetchedAt < STALE_MS) return; let cancelled = false; loadPlaylist(query) .then((data) => { if (!cancelled) { setState({ items: data.items, total: data.total, loading: false, error: null, }); } }) .catch((err: unknown) => { if (cancelled) return; setState((prev) => ({ ...prev, loading: false, error: err instanceof Error ? err.message : "加载失败", })); }); return () => { cancelled = true; }; }, [query, enabled]); return state; }