const SAVE_DELAY_MS = 800 export type NotesSaveState = 'saved' | 'unsaved' | 'saving' | 'error' /** Collects edits to a note and saves them shortly after typing stops, and when leaving the note or the page */ export function useNotesAutosave(id: number, onSaved: (summary: NotesDocSummary) => void) { const url = `/api/notes/docs/${id}` const state = ref('saved') let pending: NotesDocUpdate = {} let timer: ReturnType | undefined const hasPending = () => Object.keys(pending).length > 0 async function flush() { clearTimeout(timer) if (!hasPending()) return const body = pending pending = {} state.value = 'saving' try { onSaved(await $fetch(url, { method: 'PATCH', body })) state.value = hasPending() ? 'unsaved' : 'saved' } catch { pending = { ...body, ...pending } state.value = 'error' } } function queue(changes: NotesDocUpdate) { Object.assign(pending, changes) state.value = 'unsaved' clearTimeout(timer) timer = setTimeout(flush, SAVE_DELAY_MS) } // A normal request may not finish when the tab closes; keepalive lets it function saveOnUnload() { if (!hasPending()) return fetch(url, { method: 'PATCH', keepalive: true, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(pending) }) pending = {} } onMounted(() => window.addEventListener('pagehide', saveOnUnload)) onBeforeUnmount(() => { window.removeEventListener('pagehide', saveOnUnload) flush() }) onBeforeRouteLeave(() => flush()) return { state, queue, flush } }