"use client"; import { useEffect, useState } from "react"; import { createReactBlockSpec } from "@blocknote/react"; import { RiFileTextFill } from "react-icons/ri"; import { useRouter } from "next/navigation"; import { supabaseBrowser } from "@/lib/supabase/client"; const normalizeTitle = (value?: string | null) => { if (!value || !value.trim()) { return "未命名页面"; } return value; }; const PageReferenceContent = ({ pageId, title }: { pageId: string; title: string }) => { const router = useRouter(); const fallbackTitle = normalizeTitle(title); const [resolvedTitle, setResolvedTitle] = useState(fallbackTitle); useEffect(() => { setResolvedTitle(fallbackTitle); }, [fallbackTitle]); useEffect(() => { if (!pageId) { return; } let cancelled = false; const applyTitle = (nextTitle?: string | null) => { if (!cancelled) { setResolvedTitle(normalizeTitle(nextTitle)); } }; const fetchTitle = async () => { try { const { data } = await supabaseBrowser .from("documents") .select("title") .eq("id", pageId) .single(); if (data) { applyTitle(data.title); } } catch { // ignore fetch errors,等待后续订阅同步 } }; void fetchTitle(); const channel = supabaseBrowser .channel(`page-ref-${pageId}`) .on( "postgres_changes", { event: "UPDATE", schema: "public", table: "documents", filter: `id=eq.${pageId}` }, (payload) => { const nextTitle = (payload.new as { title?: string } | null)?.title ?? null; applyTitle(nextTitle); }, ) .subscribe(); return () => { cancelled = true; supabaseBrowser.removeChannel(channel); }; }, [pageId]); const navigate = () => { if (pageId) { router.push(`/documents/${pageId}`); } }; return (
{ if ((event.key === "Enter" || event.key === " ") && pageId) { event.preventDefault(); navigate(); } }} className="group mt-2 flex items-center gap-3 rounded-[4px] px-4 py-3 text-[#2563eb] hover:bg-[#f5f5f5]" style={{ fontFamily: "Inter, system-ui, sans-serif" }} > {resolvedTitle} 点击进入 →
); }; export const pageReferenceBlock = createReactBlockSpec( { type: "pageReference", propSchema: { pageId: { default: "" }, title: { default: "未命名页面" }, }, content: "none", }, () => ({ render: ({ block }) => ( ), }), )();