- 新增 chapterForeshadow 纯分类:按当前章分 可回收/待回收/已逾期(启用闲置的 isWindowApproaching) - useChapterForeshadow hook(复用 GET /foreshadow,切章仅重分类不重拉) - 右栏「本章参考」+ ContextDrawer 伏笔页新增「本章伏笔」实时清单, 分色展示 + 跳看板链接 + 空态;标注『按已验收进度』
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
"use client";
|
||
|
||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||
|
||
import { api } from "@/lib/api/client";
|
||
import type { ForeshadowView } from "@/lib/api/types";
|
||
import {
|
||
classifyChapterForeshadow,
|
||
type ChapterForeshadowGroups,
|
||
} from "./chapterForeshadow";
|
||
|
||
// 写作台「本章伏笔」提醒的数据层:拉本项目全量伏笔(GET /foreshadow,只读),
|
||
// 再按当前章号本地分类。项目内一次拉取,切章仅重新分类(不重复请求)。
|
||
export type ChapterForeshadowStatus = "loading" | "ready" | "error";
|
||
|
||
export interface UseChapterForeshadow {
|
||
groups: ChapterForeshadowGroups;
|
||
status: ChapterForeshadowStatus;
|
||
reload: () => void;
|
||
}
|
||
|
||
const FORESHADOW_PATH = "/projects/{project_id}/foreshadow" as const;
|
||
const EMPTY_ITEMS: ForeshadowView[] = [];
|
||
|
||
export function useChapterForeshadow(
|
||
projectId: string,
|
||
chapterNo: number,
|
||
): UseChapterForeshadow {
|
||
const [items, setItems] = useState<ForeshadowView[]>(EMPTY_ITEMS);
|
||
const [status, setStatus] = useState<ChapterForeshadowStatus>("loading");
|
||
// reload 触发器:自增即重新拉取。
|
||
const [nonce, setNonce] = useState(0);
|
||
|
||
useEffect(() => {
|
||
let cancelled = false;
|
||
setStatus("loading");
|
||
void (async () => {
|
||
try {
|
||
const { data, error } = await api.GET(FORESHADOW_PATH, {
|
||
params: { path: { project_id: projectId } },
|
||
});
|
||
if (cancelled) return;
|
||
if (error || !data) {
|
||
setItems(EMPTY_ITEMS);
|
||
setStatus("error");
|
||
return;
|
||
}
|
||
setItems(data.foreshadow ?? EMPTY_ITEMS);
|
||
setStatus("ready");
|
||
} catch {
|
||
// 网络层失败(后端不可达/CORS):openapi-fetch 直接抛而非返回 error 信封。
|
||
if (cancelled) return;
|
||
setItems(EMPTY_ITEMS);
|
||
setStatus("error");
|
||
}
|
||
})();
|
||
return () => {
|
||
cancelled = true;
|
||
};
|
||
}, [projectId, nonce]);
|
||
|
||
// 切章不重拉,仅重新分类(伏笔窗口相对章号变化)。
|
||
const groups = useMemo(
|
||
() => classifyChapterForeshadow(items, chapterNo),
|
||
[items, chapterNo],
|
||
);
|
||
|
||
const reload = useCallback(() => setNonce((n) => n + 1), []);
|
||
|
||
return { groups, status, reload };
|
||
}
|