Spaces:
Sleeping
Sleeping
| /** | |
| * useAutoSave.ts — S800: auto-salvataggio automatico per mobile | |
| * | |
| * Salva automaticamente in tre scenari: | |
| * 1. Intervallo regolare (default 30s) — solo se isDirty | |
| * 2. App va in background (visibilitychange → hidden) — CRITICO su iOS: l'app | |
| * può essere killata dal sistema operativo in qualsiasi momento | |
| * 3. beforeunload — best-effort (non garantito su mobile) | |
| * | |
| * onSave deve essere una funzione stabile (useCallback) per evitare | |
| * re-registrazione continua degli event listener. | |
| * | |
| * Safari-safe: nessun uso di sendBeacon o XHR synchronous in beforeunload. | |
| */ | |
| import { useEffect, useRef } from "react"; | |
| interface AutoSaveOptions { | |
| /** true se ci sono modifiche non salvate */ | |
| isDirty: boolean; | |
| /** Funzione di salvataggio — deve essere stabile (useCallback) */ | |
| onSave: () => Promise<void>; | |
| /** Intervallo in ms — default 30000 (30s) */ | |
| interval?: number; | |
| /** Se false, l'auto-save è disabilitato */ | |
| enabled?: boolean; | |
| } | |
| export function useAutoSave({ | |
| isDirty, | |
| onSave, | |
| interval = 30_000, | |
| enabled = true, | |
| }: AutoSaveOptions): void { | |
| const onSaveRef = useRef(onSave); | |
| const isDirtyRef = useRef(isDirty); | |
| // Mantieni refs aggiornate senza rimontare i listener | |
| useEffect(() => { onSaveRef.current = onSave; }, [onSave]); | |
| useEffect(() => { isDirtyRef.current = isDirty; }, [isDirty]); | |
| useEffect(() => { | |
| if (!enabled) return; | |
| // 1. Salvataggio periodico | |
| const id = setInterval(() => { | |
| if (isDirtyRef.current) void onSaveRef.current(); | |
| }, interval); | |
| // 2. App va in background — critico su iOS (viene killata se non salva) | |
| const onVisibility = () => { | |
| if (document.hidden && isDirtyRef.current) { | |
| void onSaveRef.current(); | |
| } | |
| }; | |
| document.addEventListener("visibilitychange", onVisibility, { passive: true }); | |
| // 3. beforeunload — best-effort | |
| const onUnload = () => { | |
| if (isDirtyRef.current) void onSaveRef.current(); | |
| }; | |
| window.addEventListener("beforeunload", onUnload); | |
| return () => { | |
| clearInterval(id); | |
| document.removeEventListener("visibilitychange", onVisibility); | |
| window.removeEventListener("beforeunload", onUnload); | |
| }; | |
| }, [enabled, interval]); | |
| } | |