import { useEffect, useRef, useState } from "react"; import type { Editor as TiptapEditor } from "@tiptap/core"; import type { HocuspocusProvider } from "@hocuspocus/provider"; import { Cloud, CloudOff, Loader2 } from "lucide-react"; import { Tooltip } from "./Tooltip"; type SyncStatus = "connected" | "syncing" | "saved" | "disconnected"; interface Props { editorInstance: TiptapEditor | null; providerRef: { current: HocuspocusProvider | null }; } /** * Tiny connection/saving indicator for the editor top bar. Listens to the * Hocuspocus provider (`connect` / `disconnect` / `synced`) and the TipTap * editor (`update`) to flip between "Saved", "Saving..." and "Offline". */ export function SyncIndicator({ editorInstance, providerRef }: Props) { const [status, setStatus] = useState("disconnected"); const timerRef = useRef | null>(null); useEffect(() => { const provider = providerRef.current; if (!provider) return; const onConnect = () => setStatus("saved"); const onDisconnect = () => setStatus("disconnected"); const onSynced = () => setStatus("saved"); provider.on("connect", onConnect); provider.on("disconnect", onDisconnect); provider.on("synced", onSynced); const wsProvider = (provider as any).configuration?.websocketProvider; setStatus(wsProvider?.status === "connected" ? "saved" : "disconnected"); return () => { provider.off("connect", onConnect); provider.off("disconnect", onDisconnect); provider.off("synced", onSynced); }; }, [providerRef]); useEffect(() => { const editor = editorInstance; const provider = providerRef.current; if (!editor || !provider) return; const onUpdate = () => { setStatus("syncing"); if (timerRef.current) clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { const wsProvider = (provider as any).configuration?.websocketProvider; setStatus(wsProvider?.status === "connected" ? "saved" : "disconnected"); }, 1500); }; editor.on("update", onUpdate); return () => { editor.off("update", onUpdate); }; }, [editorInstance, providerRef]); const label = status === "saved" ? "Saved" : status === "syncing" ? "Saving..." : status === "connected" ? "Connected" : "Offline"; const tooltip = status === "saved" ? "All changes saved" : status === "syncing" ? "Saving..." : status === "connected" ? "Connected" : "Disconnected"; return ( {status === "syncing" && } {status === "saved" && } {status === "connected" && } {status === "disconnected" && } {label} ); }