"use client"; import { useEffect, useState } from "react"; import { usePathname } from "next/navigation"; import { apiOperations } from "@/lib/api"; import { realtime, RT_SETTINGS_CHANGED } from "@/lib/realtime"; import { VISIBLE_RECONCILE_GAP_MS, onDocumentVisible, } from "@/lib/visibilityReconcile"; /** 只读维护条:WS settings:changed 优先,回前台再校准(无定时轮询) */ export default function OperationalBanner() { const path = usePathname(); const [notice, setNotice] = useState(""); useEffect(() => { let active = true; const apply = (mode: string | undefined, message: string | undefined) => { if (!active) return; setNotice( mode === "readonly" ? message || "站点暂时只读,可以浏览,暂不能提交或修改内容。" : "" ); }; const refresh = () => { if (document.visibilityState !== "visible") return; void apiOperations<{ maintenance: { mode: string; title: string; message: string }; }>("/api/site-state") .then((s) => apply(s.maintenance?.mode, s.maintenance?.message)) .catch(() => {}); }; refresh(); const offPush = realtime.on(RT_SETTINGS_CHANGED, () => refresh()); const offVisible = onDocumentVisible(refresh, VISIBLE_RECONCILE_GAP_MS); return () => { active = false; offPush(); offVisible(); }; }, [path]); if (path.startsWith("/admin") || !notice) return null; return (
只读模式: {notice}
); }