import { injectable } from 'inversify'; import { BaseWidget, Message } from '../widgets'; /** * A contract for widgets that want to store and restore their inner state, between sessions. */ export interface StatefulWidget { /** * Called on unload to store the inner state. */ storeState(): object; /** * Called when the widget got created by the storage service */ restoreState(oldState: object): void; } export namespace StatefulWidget { // eslint-disable-next-line @typescript-eslint/no-explicit-any export function is(arg: any): arg is StatefulWidget { return arg !== undefined && typeof arg['storeState'] === 'function' && typeof arg['restoreState'] === 'function'; } } @injectable() // eslint-disable-next-line @typescript-eslint/no-explicit-any export abstract class StatefulWidgetAbstract extends BaseWidget implements StatefulWidget { abstract readonly id: string; protected readonly state: WidgetState = this.getDefaultState(); protected lastSetKeys: Map = new Map(); protected getDefaultState(): WidgetState { return {} as WidgetState; } protected onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.lastSetKeys = new Map(); } setState(newState: Partial, stopUpdate?: boolean): void { const lastSetKeys = this.lastSetKeys; Object.keys(newState).forEach((key: string) => { const k = key as keyof WidgetState; if (this.state[k] !== newState[k]) { lastSetKeys.set(k, true); // eslint-disable-next-line @typescript-eslint/no-explicit-any this.state[k] = newState[k] as any; } }); if (lastSetKeys.size && !stopUpdate) this.update(); } storeState(): object { return { ...this.state }; } restoreState(newState: object): void { this.setState(newState); } isStateChanged(keys?: (keyof WidgetState)[]): boolean { const lastSetKeys = this.lastSetKeys; if (!keys) return lastSetKeys.size !== 0; return !!keys.find(k => lastSetKeys.has(k)); } resetState(): void { this.setState(this.getDefaultState()); } }