@difizen/mana-core
Version:
147 lines (138 loc) • 4.05 kB
text/typescript
import type { Event, MaybePromise } from '@difizen/mana-common';
import type { View } from '..';
export interface Saveable {
readonly dirty: boolean;
readonly onDirtyChanged: Event<void>;
readonly autoSave: 'on' | 'off';
/**
* Saves dirty changes.
*/
save: () => MaybePromise<void>;
/**
* Reverts dirty changes.
*/
restore?: () => MaybePromise<void>;
}
export interface SaveableSource {
readonly saveable: Saveable;
}
export namespace Saveable {
export function isSource(arg: any): arg is SaveableSource {
return !!arg && 'saveable' in arg && is(arg.saveable);
}
export function is(arg: any): arg is Saveable {
return !!arg && 'dirty' in arg && 'onDirtyChanged' in arg;
}
export function get(arg: any): Saveable | undefined {
if (is(arg)) {
return arg;
}
if (isSource(arg)) {
return arg.saveable;
}
return undefined;
}
export function getDirty(arg: any): Saveable | undefined {
const saveable = get(arg);
if (saveable && saveable.dirty) {
return saveable;
}
return undefined;
}
export function isDirty(arg: any): boolean {
return !!getDirty(arg);
}
export async function save(arg: any): Promise<void> {
const saveable = get(arg);
if (saveable) {
await saveable.save();
}
}
// export function apply(widget: View): SaveableView | undefined {
// // eslint-disable-next-line @typescript-eslint/no-use-before-define
// if (SaveableView.is(widget)) {
// return widget;
// }
// const saveable = Saveable.get(widget);
// if (!saveable) {
// return undefined;
// }
// setDirty(widget, saveable.dirty);
// saveable.onDirtyChanged(() => setDirty(widget, saveable.dirty));
// const closeWidget = widget.close.bind(widget);
// const closeWithoutSaving: SaveableWidget['closeWithoutSaving'] = async () => {
// if (saveable.dirty && saveable.revert) {
// await saveable.revert();
// }
// closeWidget();
// return waitForClosed(widget);
// };
// let closing = false;
// const closeWithSaving: SaveableWidget['closeWithSaving'] = async options => {
// if (closing) {
// return;
// }
// closing = true;
// try {
// const result = await shouldSave(saveable, () => {
// if (options && options.shouldSave) {
// return options.shouldSave();
// }
// return new ShouldSaveDialog(widget).open();
// });
// if (typeof result === 'boolean') {
// if (result) {
// await Saveable.save(widget);
// }
// await closeWithoutSaving();
// }
// } finally {
// closing = false;
// }
// };
// return Object.assign(widget, {
// closeWithoutSaving,
// closeWithSaving,
// close: () => closeWithSaving(),
// });
// }
export async function shouldSave(
saveable: Saveable,
cb: () => MaybePromise<boolean | undefined>,
): Promise<boolean | undefined> {
if (!saveable.dirty) {
return false;
}
if (saveable.autoSave === 'on') {
return true;
}
return cb();
}
}
export interface SaveableView extends View {
closeWithoutSaving: () => Promise<void>;
closeWithSaving: (options?: SaveableView.CloseOptions) => Promise<void>;
}
export namespace SaveableView {
export function is(view: View | undefined): view is SaveableView {
return !!view && 'closeWithoutSaving' in view;
}
export function getDirty<T extends View>(
views: Iterable<T>,
): IterableIterator<SaveableView & T> {
return get(views, Saveable.isDirty);
}
export function* get<T extends View>(
views: Iterable<T>,
filter: (view: T) => boolean = () => true,
): IterableIterator<SaveableView & T> {
for (const view of views) {
if (SaveableView.is(view) && filter(view)) {
yield view;
}
}
}
export interface CloseOptions {
shouldSave?: () => MaybePromise<boolean | undefined>;
}
}