UNPKG

@difizen/mana-core

Version:

219 lines (200 loc) 6.33 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { prop } from '@difizen/mana-observable'; import { inject, singleton } from '@difizen/mana-syringe'; import { DebugService, StorageService } from '../common'; import { SlotViewManager } from './slot-view-manager'; import { ViewManager } from './view-manager'; import type { View, ViewOpenOption } from './view-protocol'; import { RootViewId } from './view-protocol'; import { SlotView } from './view-protocol'; import { StatefulView } from './view-protocol'; interface SavedView { factoryId: string; // viewType: 'slotView' | 'view'; option: any; slot?: string | undefined; children?: SavedView[] | undefined; slots?: SavedView[] | undefined; state?: any | undefined; openOption?: ViewOpenOption | undefined; } export namespace SavedView { export function is(data?: Record<string, any>): data is SavedView { return !!data && typeof data === 'object' && 'factoryId' in data; } } const ShouldPreventStoreViewKey = 'mana-should-prevent-store-view'; @singleton() export class ViewStorage { protected storageKey = 'mana-view-info'; protected restoreCount = 0; protected readonly viewManager: ViewManager; protected readonly slotViewManager: SlotViewManager; protected readonly debugService: DebugService; protected readonly storageService: StorageService; @prop() protected shouldStoreViews = false; get canStoreView(): boolean { return this.shouldStoreViews; } get onlyRootView(): boolean { return this.restoreCount < 2; } constructor( @inject(ViewManager) viewManager: ViewManager, @inject(SlotViewManager) slotViewManager: SlotViewManager, @inject(DebugService) debugService: DebugService, @inject(StorageService) storageService: StorageService, ) { this.viewManager = viewManager; this.slotViewManager = slotViewManager; this.debugService = debugService; this.storageService = storageService; if (localStorage.getItem(ShouldPreventStoreViewKey) === 'once') { localStorage.removeItem(ShouldPreventStoreViewKey); } if (localStorage.getItem(ShouldPreventStoreViewKey) === 'false') { this.shouldStoreViews = true; } } disableStoreView() { this.shouldStoreViews = false; this.storageService.setData(this.storageKey, undefined); localStorage.setItem(ShouldPreventStoreViewKey, 'true'); } enableStoreView() { this.shouldStoreViews = true; localStorage.setItem(ShouldPreventStoreViewKey, 'false'); } resetViews() { this.shouldStoreViews = false; this.storageService.setData(this.storageKey, undefined); localStorage.removeItem(ShouldPreventStoreViewKey); window.location.reload(); } async saveViews(): Promise<void> { const rootView = await this.viewManager.getView(RootViewId); let viewInfo: SavedView | undefined; if (rootView) { viewInfo = this.getViewInfo(rootView); } this.storageService.setData(this.storageKey, viewInfo); } protected getViewInfo( view: View, slot?: string, openOption?: ViewOpenOption, ): SavedView | undefined { const baseInfo = this.getViewBaseInfo(view, slot); if (!baseInfo) { return; } this.debugService( 'store view >>>>>>>>>>>>>>>', baseInfo.factoryId, baseInfo.option, baseInfo.slot, ); return { ...baseInfo, slots: this.getViewSlotsInfo(view), children: this.getViewChildrenInfo(view), openOption: openOption, }; } protected getViewBaseInfo(view: View, slot?: string): SavedView | undefined { const factoryId = this.viewManager.getFactoryIdByView(view); if (!factoryId) { return; } const option = this.viewManager.getViewOption(view); return { factoryId, slot, option, state: StatefulView.is(view) ? view.storeState() : undefined, }; } protected getViewSlotsInfo(parentView: View): SavedView[] | undefined { const slotNames = this.slotViewManager.getSlotChildren(parentView); if (!slotNames) { return undefined; } const slots = slotNames .map((slot) => { const slotView = this.slotViewManager.getSlotView(slot); if (slotView) { return this.getViewInfo(slotView, slot); } return undefined; }) .filter(SavedView.is); if (slots.length > 0) { return slots; } return undefined; } protected getViewChildrenInfo(view: View): SavedView[] | undefined { if (!SlotView.is(view)) { return undefined; } const children = view.children .map((item) => { const openOption = view.getViewOption(item); delete openOption?.reveal; return this.getViewInfo(item, undefined, openOption); }) .filter(SavedView.is); if (children.length > 0) { return children; } return undefined; } async getSavedView(): Promise<SavedView | undefined> { const viewInfo = await this.storageService.getData<SavedView>(this.storageKey); return viewInfo; } async restoreView(savedView: SavedView): Promise<View | undefined> { const { factoryId, option, state } = savedView; try { this.debugService( 'restore view >>>>>>>>>>>>>>>', factoryId, option, savedView.slot, ); const view = await this.viewManager.getOrCreateView(factoryId, option); if (savedView.slot) { this.slotViewManager.setSlotView(savedView.slot, view); } if (savedView.slots) { await Promise.all( savedView.slots.map(async (item) => await this.restoreView(item)), ); } if (savedView.children) { await Promise.all( savedView.children.map(async (item) => { const child = await this.restoreView(item); if (child && SlotView.is(view) && savedView.slot) { await this.slotViewManager.addView( child, savedView.slot, item.openOption, ); } }), ); } if (StatefulView.is(view)) { view.restoreState(state); } if (!SlotView.is(view)) { this.restoreCount += 1; } return view; } catch (error) { return undefined; } } }