import { ColumnSortConfig, ColumnViewConfig, setViewSelectedColumnsForLayout, ViewBase, ViewFiltersValues, ViewType, } from '../model'; import isEqual from 'lodash/isEqual'; import { FiltersMap } from '@wix/bex-core'; import { ToolbarCollectionState } from './ToolbarCollectionState'; import { computed, makeObservable } from 'mobx'; import { CollectionViewsState } from './CollectionViewsState'; export interface CollectionViewsQueryCompareHelperParams< T, F extends FiltersMap, > { table: ToolbarCollectionState; collectionViews: CollectionViewsState; } export class CollectionViewsQueryCompareHelper { readonly collectionViews; readonly table; get views() { return this.collectionViews.views; } constructor(params: CollectionViewsQueryCompareHelperParams) { this.table = params.table; this.collectionViews = params.collectionViews; makeObservable(this, { currentViewBase: computed, isSelectedColumnsChanged: computed, isSortChanged: computed, isFiltersChanged: computed, viewBaseSortDirections: computed, viewBaseSelectedColumns: computed, viewBaseFilters: computed, nonEmptyViewFiltersKeys: computed, }); } get isSelectedColumnsChanged(): boolean { const { collectionViews } = this; return this.isColumnsChanged( collectionViews.currentViewColumnsOrDefaultColumns, this.viewBaseSelectedColumns, ); } isColumnsChanged( columns1?: ColumnViewConfig[], columns2?: ColumnViewConfig[], ) { if (!columns1 || !columns2) { return false; } if (columns1.length !== columns2.length) { return true; } return columns1.some( (column, index) => column.id !== columns2[index].id || column.isSelected !== columns2[index].isSelected, ); } get isSortChanged(): boolean { const currentView = this.views.currentView; if (!currentView) { return true; } const sortDirections = this.viewBaseSortDirections; return !currentView.sortDirections && !sortDirections ? false : currentView.sortDirections?.length !== sortDirections?.length || (currentView.sortDirections ?? []).some( (sort, i) => sortDirections?.[i].columnId !== sort.columnId || sortDirections?.[i].direction !== sort.direction, ); } get isFiltersChanged(): boolean { const currentView = this.views.currentView; if (!currentView) { return true; } const { viewFiltersInCurrentQuery: filters } = this; const filterKeys = this.nonEmptyViewFiltersKeys; const viewAndQueryFiltersKeys = Array.from( new Set([...filterKeys, ...Object.keys(filters)]), ); return ( filterKeys.length !== Object.keys(filters).length || viewAndQueryFiltersKeys.some((filterName) => { const filter = filters?.[filterName]; if (!filter) { return false; } const rawFilterValueFromView = currentView.filters?.[filterName]?.value; const currentViewFilterValue = filter.decode(rawFilterValueFromView); const appliedFilterValue = filter.value; // equals accepts non empty filters if ( currentViewFilterValue === undefined || appliedFilterValue === undefined ) { return currentViewFilterValue !== appliedFilterValue; } const equals = this.table.collection.query.filters?.[filterName]?.equals ?? isEqual; return !equals(currentViewFilterValue, appliedFilterValue); }) ); } get nonEmptyViewFiltersKeys() { const currentView = this.views.currentView; if (!currentView) { return []; } return Object.keys(currentView.filters ?? {}).reduce((acc, filterName) => { const rawFilterValueFromView = currentView.filters?.[filterName]?.value; const filter = this.table.collection.query.filters[filterName]; if (!filter) { return acc; } const currentViewFilterValue = filter.decode(rawFilterValueFromView); return filter.clone({ initialValue: currentViewFilterValue, }).isEmpty ? acc : acc.concat(filterName); }, [] as string[]); } get currentViewBase(): ViewBase { const layout = this.collectionViews.getLayout(); const base: ViewBase = { filters: this.viewBaseFilters, sortDirections: this.viewBaseSortDirections, }; setViewSelectedColumnsForLayout(base, layout, this.viewBaseSelectedColumns); // Persist a cached edit on the other slot so save doesn't drop divergence // made on the inactive layout. const otherLayout: ViewType = layout === 'spreadsheet' ? 'table' : 'spreadsheet'; const cachedOther = this.table._layoutColumnsCache[otherLayout]; if (cachedOther) { setViewSelectedColumnsForLayout(base, otherLayout, cachedOther); } if (layout) { base.layout = layout; } return base; } get viewBaseSortDirections() { const sortState = this.table.collection.query.sort; const sortDirections = sortState.value.map( (v) => ({ columnId: v.field, direction: v.direction === 'asc' ? 'Ascending' : 'Descending', } as ColumnSortConfig), ); return sortDirections.length === 0 ? undefined : sortDirections; } get viewBaseSelectedColumns() { return this.table.selectedColumns.value; } get viewFiltersInCurrentQuery() { return this.table.collection.query.activeFiltersEntries.reduce( (acc, [filterName, filter]) => { return { ...acc, [filterName]: filter, }; }, {} as Partial, ); } get viewBaseFilters() { const { table: { collection: { query }, }, views: { currentView }, } = this; const { activeFiltersEntries } = query; const newFilters = activeFiltersEntries.reduce( (acc, [filterName, filter]) => { return { ...acc, [filterName]: { value: filter.encode(filter.value), }, }; }, {} as Partial>, ); const existingFiltersErase = Object.keys(currentView?.filters ?? {}).reduce( (acc, filterName: keyof Partial>) => { acc[filterName] = null; return acc; }, {} as Partial>, ); return { ...existingFiltersErase, ...newFilters, }; } }