"use client"; import { useTranslations } from "next-intl"; import { type RefObject, useEffect, useRef, useState } from "react"; import { autosaveArticle, clearArticleRecovery, loadArticleRecovery, } from "@/actions/article-recovery"; import type { ArticleDraft } from "@/lib/article-draft"; export function useArticleRecovery( key: string, form: RefObject, saving: RefObject, dirty: boolean, restore: (draft: ArticleDraft, revision: boolean) => void, ) { const t = useTranslations("pages.admin.articles.form"); const [recovery, setRecovery] = useState > | null>(null); const [message, setMessage] = useState("autosaveLoading"); const [reload, setReload] = useState(0); const version = useRef(0); const request = useRef | null>(null); const last = useRef(""); const dirtyRef = useRef(dirty); dirtyRef.current = dirty; const blocked = useRef(true); // biome-ignore lint/correctness/useExhaustiveDependencies: reload explicitly retries reconciliation without remounting the editor. useEffect(() => { let active = true; blocked.current = true; last.current = ""; setMessage("autosaveLoading"); loadArticleRecovery(key) .then((data) => { if (!active) return; version.current = data.version; setRecovery(data); blocked.current = Boolean(data.draft); setMessage(data.draft ? "recoveryFound" : "autosaveReady"); }) .catch(() => { if (active) setMessage("autosaveFailed"); }); const timer = setInterval(() => { if ( blocked.current || !dirtyRef.current || saving.current || request.current || !form.current ) return; const data = new FormData(form.current); const snapshot = JSON.stringify(Array.from(data.entries())); if (snapshot === last.current) return; setMessage("autosaveSaving"); request.current = autosaveArticle(key, version.current, data) .then((result) => { if (!active) return; if (!result.ok) { blocked.current = true; setMessage("autosaveConflict"); return; } version.current = result.version; last.current = snapshot; setMessage("autosaveSaved"); }) .catch(() => { if (active) setMessage("autosaveFailed"); }) .finally(() => { request.current = null; }); }, 2500); return () => { active = false; clearInterval(timer); }; }, [key, form, saving, reload]); return { draft: recovery?.draft?.payload, wait: () => request.current ?? Promise.resolve(), clear: () => clearArticleRecovery(key, version.current), panel: (

{t(message)}

{(message === "autosaveFailed" || message === "autosaveConflict") && ( )} {recovery?.draft && (
)} {!!recovery?.revisions.length && (
{t("revisionHistory")}
    {recovery.revisions.map((revision) => (
  • {revision.payload.title}
  • ))}
)}
), }; }