Files
Endoleap/src/components/workspace/graph-page.tsx
T

55 lines
2.1 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { api, errorText } from "@/lib/api";
import { readDemoBoards } from "@/lib/demo-workspace";
import type { Board } from "@/lib/workspace";
import styles from "./workspace.module.css";
export default function GraphPage({ workspaceId, boardId }: { workspaceId: string; boardId: string }) {
const demo = useSearchParams().get("demo") === "1";
const router = useRouter();
const [board, setBoard] = useState<Board | null>(null);
const [error, setError] = useState("");
useEffect(() => {
setBoard(null);
setError("");
if (demo) {
try {
const list = readDemoBoards(workspaceId);
const item = list.find((b) => b.id === boardId);
if (item) setBoard(item);
else setError("图谱不存在。");
} catch { setError("无法读取本地演示图谱。"); }
return;
}
let active = true;
const controller = new AbortController();
api<Board>(`workspaces/${encodeURIComponent(workspaceId)}/materials/${encodeURIComponent(boardId)}`, { signal: controller.signal })
.then((item) => {
if (!active) return;
if (item.kind !== "board" || item.workspace_id !== workspaceId) {
setError("当前页面不是此工作空间的图谱。");
return;
}
setBoard(item);
})
.catch((e) => {
if (!active) return;
setError(errorText(e));
if (e.message === "invalid_session") router.replace("/login");
});
return () => { active = false; controller.abort(); };
}, [workspaceId, boardId, demo, router]);
return (
<main className={styles.graph} aria-label={board ? board.title + " · 无限图谱" : "无限图谱"} aria-busy={!board && !error}>
<div className={styles.graphSurface} aria-label={board ? board.title + "的画布" : "画布"} />
{error ? <p className={styles.graphStatus} role="alert">{error}</p>
: !board && <p className={styles.graphStatus} role="status">正在打开图谱…</p>}
</main>
);
}