"use client"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { api } from "@/lib/api/client"; import { useToast } from "@/components/Toast"; import { AUTOSAVE_DELAY_MS, createDebouncedSaver, formatSavedAt, type DebouncedSaver, } from "./autosave"; export type SaveStatus = "idle" | "saving" | "saved" | "error"; export interface UseAutosave { status: SaveStatus; savedLabel: string | null; onChange: (text: string) => void; flush: () => void; } // 防抖自动保存草稿:乐观置 saving → PUT 成功置 saved(落「保存于 hh:mm」); // 失败回滚状态 + toast,不丢正文(已在编辑器里)。 export function useAutosave( projectId: string, chapterNo: number, ): UseAutosave { const [status, setStatus] = useState("idle"); const [savedLabel, setSavedLabel] = useState(null); const toast = useToast(); const lastSavedRef = useRef(null); const save = useCallback( async (text: string): Promise => { if (text === lastSavedRef.current) return; setStatus("saving"); const { error } = await api.PUT( "/projects/{project_id}/chapters/{chapter_no}/draft", { params: { path: { project_id: projectId, chapter_no: chapterNo } }, body: { text }, }, ); if (error) { setStatus("error"); toast("自动保存失败,稍后重试(正文未丢失)", "error"); return; } lastSavedRef.current = text; setSavedLabel(formatSavedAt(new Date())); setStatus("saved"); }, [projectId, chapterNo, toast], ); const saver: DebouncedSaver = useMemo( () => createDebouncedSaver(save, AUTOSAVE_DELAY_MS), [save], ); useEffect(() => () => saver.cancel(), [saver]); const onChange = useCallback( (text: string) => { saver.schedule(text); }, [saver], ); const flush = useCallback(() => saver.flush(), [saver]); return { status, savedLabel, onChange, flush }; }