feat: autosave canvas after configurable inactivity delay
Adds an opt-in autosave feature (disabled by default) that silently
saves the canvas after a configurable period of inactivity.
- frontend/src/utils/autosaveSettings.ts (new) — localStorage-backed
setting {enabled, delay} with pub/sub CustomEvent pattern (mirrors
alignmentSettings.ts). Key: homelable.autosave.
- frontend/src/App.tsx:
* handleSave accepts optional { silent?: boolean } — skips the success
toast when called from autosave to avoid noise on every periodic save
* autosave state (useState + subscribeAutosaveSettings)
* debounce useEffect: resets on nodes/edges change; when quiet for
autosave.delay seconds and hasUnsavedChanges is true, calls
handleSaveRef.current silently
- frontend/src/components/modals/SettingsModal.tsx:
* Canvas section: Autosave canvas toggle + Save after delay selector
(3/5/10/30/60 s). Changes persist immediately to localStorage via
writeAutosaveSettings + propagate cross-tab via CustomEvent.
Default: disabled — no behaviour change for existing users.
Manual Ctrl+S and the Save button continue to work normally.
Errors always show a toast regardless of the silent flag.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
// Persisted client-side autosave preference.
|
||||
// Kept in localStorage (per-user UI preference, not canvas data).
|
||||
// Same-tab updates propagate via a CustomEvent so App.tsx and SettingsModal
|
||||
// can stay in sync without a global store.
|
||||
|
||||
export interface AutosaveSettings {
|
||||
enabled: boolean
|
||||
delay: number // seconds of inactivity before auto-saving
|
||||
}
|
||||
|
||||
export const DEFAULT_AUTOSAVE_SETTINGS: AutosaveSettings = { enabled: false, delay: 5 }
|
||||
|
||||
const KEY = 'homelable.autosave'
|
||||
const EVENT = 'homelable:autosave-settings-changed'
|
||||
|
||||
export function readAutosaveSettings(): AutosaveSettings {
|
||||
try {
|
||||
const raw = localStorage.getItem(KEY)
|
||||
if (!raw) return DEFAULT_AUTOSAVE_SETTINGS
|
||||
const parsed = JSON.parse(raw) as Partial<AutosaveSettings>
|
||||
return {
|
||||
enabled: parsed.enabled ?? DEFAULT_AUTOSAVE_SETTINGS.enabled,
|
||||
delay: typeof parsed.delay === 'number' && parsed.delay > 0 ? parsed.delay : DEFAULT_AUTOSAVE_SETTINGS.delay,
|
||||
}
|
||||
} catch {
|
||||
return DEFAULT_AUTOSAVE_SETTINGS
|
||||
}
|
||||
}
|
||||
|
||||
export function writeAutosaveSettings(s: AutosaveSettings): void {
|
||||
try {
|
||||
localStorage.setItem(KEY, JSON.stringify(s))
|
||||
window.dispatchEvent(new CustomEvent<AutosaveSettings>(EVENT, { detail: s }))
|
||||
} catch {
|
||||
/* quota / SSR */
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribeAutosaveSettings(listener: (s: AutosaveSettings) => void): () => void {
|
||||
const handler = (e: Event) => listener((e as CustomEvent<AutosaveSettings>).detail)
|
||||
window.addEventListener(EVENT, handler)
|
||||
return () => window.removeEventListener(EVENT, handler)
|
||||
}
|
||||
Reference in New Issue
Block a user