/** * Module to help deal with unsaved changes when closing a page. */ import {Disposable} from 'grainjs'; /** * Create an UnsavedChanges object to indicate there are UnsavedChanges. Dispose it when this is * no longer the case. The optional callback will be called to confirm there are indeed unsaved * changes. If omitted, it is assumed that there are. */ export class UnsavedChange extends Disposable { constructor( // If given, saveChanges() will call it to save changes. private _saveCB?: () => Promise, // If given, it may return false to indicate that actually nothing has changed. private _haveChanges?: () => boolean, ) { super(); unsavedChanges.add(this); this.onDispose(() => unsavedChanges.delete(this)); } public haveUnsavedChanges() { return !this._haveChanges || this._haveChanges(); } public async save(): Promise { return this._saveCB && this._saveCB(); } } export class UnsavedChangeSet { private _changes = new Set(); /** * Check if there are any unsaved changes out there. */ public haveUnsavedChanges(): boolean { return Array.from(this._changes).some((c) => c.haveUnsavedChanges()); } /** * Save any unsaved changes out there. */ public async saveChanges(): Promise { await Promise.all(Array.from(this._changes).map((c) => c.save())); } public add(unsaved: UnsavedChange) { this._changes.add(unsaved); } public delete(unsaved: UnsavedChange) { this._changes.delete(unsaved); } } // Global set of UnsavedChanges, checked on page unload. export const unsavedChanges = new UnsavedChangeSet();