@difizen/mana-core
Version:
219 lines (200 loc) • 6.33 kB
text/typescript
/* 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';
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;
protected shouldStoreViews = false;
get canStoreView(): boolean {
return this.shouldStoreViews;
}
get onlyRootView(): boolean {
return this.restoreCount < 2;
}
constructor(
viewManager: ViewManager,
slotViewManager: SlotViewManager,
debugService: DebugService,
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;
}
}
}