import { NestedTableState } from './NestedTableState'; import { action, computed, makeObservable } from 'mobx'; import { MultiCollectionSupport, TaskState } from '@wix/bex-core'; import { TableVirtualState } from '../TableVirtualState'; import { NestedTableFlatKeyedItem, NestedTableFlatModeSectionState, } from './NestedTableFlatModeSectionState'; import { addEventListener } from '@wix/bex-core/util'; import { NestedTableFlatModeStateBIReporter } from './NestedTableFlatModeStateBIReporter'; export interface NestedTableFlatStateBaseParams { wrapper: NestedTableState; } export class NestedTableFlatModeState { readonly wrapper; readonly virtual; readonly initTask = new TaskState(); readonly refreshTask = new TaskState(); readonly sections; readonly multi; readonly bi: NestedTableFlatModeStateBIReporter; readonly initSearchState; constructor(params: NestedTableFlatStateBaseParams) { const { wrapper } = params; this.wrapper = wrapper; const { levels, query } = wrapper; this.bi = new NestedTableFlatModeStateBIReporter(this); this.sections = Array.from( levels .map((levelDescriptor) => { return new NestedTableFlatModeSectionState({ levelDescriptor, query, flatModeState: this, }); }) .reduce((map, section) => { const query = section.collection.getCurrentQuery(); const existingSection = map.get(query); if (existingSection) { existingSection.levelDescriptors.push(section.levelDescriptor); } else { map.set(query, section); section.levelDescriptors.push(section.levelDescriptor); } return map; }, new Map>()) .values(), ); this.multi = new MultiCollectionSupport<{}>({ collections: this.sections.map(({ collection }) => collection), }); this.virtual = new TableVirtualState({ table: this, }); this.initSearchState = this._getSearchInitState(); makeObservable(this, { keyedItems: computed, }); } get toolbar() { return this.wrapper.toolbar; } get tableState() { return this.wrapper.tableState; } get keyedItems(): NestedTableFlatKeyedItem[] { return this.sections.reduce((acc, section) => { const { collection: { result: { keyedItems }, }, } = section; return [ ...acc, ...keyedItems.map((keyedItem) => ({ ...keyedItem, item: { state: section, originalKeyedItem: keyedItem, }, })), ]; }, [] as NestedTableFlatKeyedItem[]); } get showEmptyState() { return this.multi.showEmptyState; } get hasAvailableItems() { return this.multi.hasAvailableItems; } get resultOriginQuerySearch() { return this.multi.resultOriginQuerySearch; } get hasNonPersistentActiveFilters() { return this.multi.hasNonPersistentActiveFilters; } get showErrorState() { const { initTask, refreshTask, multi } = this; return ( initTask.status.isError || refreshTask.status.isError || multi.showErrorState ); } get showLoadingState() { return ( this.initTask.status.isIdle || this.initTask.status.isLoading || this.refreshTask.status.isLoading || this.multi.showLoadingState ); } retryErrorState = action(() => { const { initTask, refreshTask } = this; if (initTask.status.isError) { initTask.runOnce(); } else if (refreshTask.status.isError) { refreshTask.runOnce(); } else { this.multi.retryErrorState(); } }); get errorStatus() { const { initTask, refreshTask, multi } = this; return initTask.errorStatus || refreshTask.errorStatus || multi.errorStatus; } get query() { return this.wrapper.query; } get levels() { return this.wrapper.levels; } async _populateCacheAndSearch() { const { wrapper, bi } = this; const reportSearchResults = bi._createReportSearchResults(); const fetchAll = wrapper._attemptFetchAllAndPopulateCache(); fetchAll.init(); await fetchAll.initTask.status.promise; await this.toolbar.collection._runSearch(true); await Promise.all( this.sections.map(({ collection }) => collection._runSearch(true)), ); reportSearchResults(); } _runSearch = action(async () => { const { query, refreshTask } = this; if (query.search.isEmpty) { return; } refreshTask.reset(); refreshTask.runOnce(() => this._populateCacheAndSearch()); }); _getSearchInitState() { const initTask = new TaskState(); const disposers = [ ...this.sections.map(({ collection }) => addEventListener( collection.hooks, 'beforeInitialFetch', () => initTask.status.promise, ), ), ]; return { initTask, init: () => { initTask.runOnce(async () => { await this._populateCacheAndSearch(); }); return () => { disposers.forEach((d) => d()); }; }, }; } init({ isUsingCache = false } = {}) { const { initTask, wrapper, multi, sections, initSearchState } = this; const { query } = wrapper; const disposers = [ wrapper.init(), initSearchState.init(), multi.init(), addEventListener(query.search.events, 'change', this._runSearch), ...sections.map((section) => section.init()), ]; if (!isUsingCache && initTask.status.isSuccess) { this._runSearch(); } initTask.runOnce(async () => { wrapper.initTask.runOnce(); initSearchState.initTask.runOnce(); multi.initTask.runOnce(); await wrapper.initTask.status.promise; await initSearchState.initTask.status.promise; await multi.initTask.status.promise; }); return () => { disposers.forEach((d) => d()); }; } _refreshAll = action(() => { this.sections.forEach(({ collection }) => { collection.clearResultAndMoveToStart(); }); }); }