Skip to content

Commit

Permalink
refactor(datatable): use only datatable store (#1991)
Browse files Browse the repository at this point in the history
* refactor(datatable): use only datatable store
Closes #1987

* progress fix

* progress fix

* progress fix

* progress fix

* progress fix 2

* progress fix setter

---------

Co-authored-by: jolevesq <[email protected]>
  • Loading branch information
jolevesq and jolevesq authored Apr 9, 2024
1 parent 6a42f6c commit 5582588
Show file tree
Hide file tree
Showing 31 changed files with 479 additions and 287 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { api } from '@/app';
import { GeoviewStoreType } from '@/core/stores';
import { AbstractGeoViewVector } from '@/geo/layer/geoview-layers/vector/abstract-geoview-vector';
import { EsriDynamic } from '@/geo/layer/geoview-layers/raster/esri-dynamic';
import { TypeLayerEntryConfig } from '@/geo/map/map-schema-types';
import { AbstractEventProcessor } from '@/api/event-processors/abstract-event-processor';
import { TypeFeatureInfoResultSet } from '@/geo/utils/feature-info-layer-set';
import { IDataTableState } from '@/core/stores/store-interface-and-intial-values/data-table-state';
import { logger } from '@/core/utils/logger';
import { TypeLayerData } from '@/geo/utils/layer-set';

// GV The paradigm when working with DataTableEventProcessor vs DataTableState goes like this:
// GV DataTableState provides: 'state values', 'actions' and 'setterActions'.
// GV Whereas Zustand would suggest having 'state values' and 'actions', in GeoView, we have a 'DataTableEventProcessor' in the middle.
// GV This is because we wanted to have centralized code between UI actions and backend actions via a DataTableEventProcessor.
// GV In summary:
// GV The UI components should use DataTableState's 'state values' to read and 'actions' to set states (which simply redirect to DataTableEventProcessor).
// GV The back-end code should use DataTableEventProcessor which uses 'state values' and 'setterActions'
// GV Essentially 3 main call-stacks:
// GV - DataTableEventProcessor ---calls---> DataTableState.setterActions
// GV - UI Component ---calls---> DataTableState.actions ---calls---> DataTableEventProcessor ---calls---> DataTableState.setterActions
// GV - DataTableEventProcessor ---triggers---> DataTableViewer events ---calls---> DataTableState.setterActions
// GV The reason for this pattern is so that UI components and processes performing back-end code
// GV both end up running code in DataTableEventProcessor (UI: via 'actions' and back-end code via 'DataTableEventProcessor')

export class DataTableEventProcessor extends AbstractEventProcessor {
// TODO: refactor - we should centralize the propagation to store from where the remove layer happend... this code is repeated
// TD.CONT: qhen we do this, clean other obejct at the same time... each processor has a clean function called from where the remove
/**
* Overrides initialization of the Feature Info Event Processor
* @param {GeoviewStoreType} store - The store associated with the Feature Info Event Processor
* @returns An array of the subscriptions callbacks which were created
*/
protected onInitialize(store: GeoviewStoreType): Array<() => void> | void {
// Checks for udpated layers in layer order
const unsubLayerRemoved = store.subscribe(
(state) => state.mapState.orderedLayerInfo,
(cur, prev) => {
// Log
logger.logTraceCoreStoreSubscription('DATATABLE EVENT PROCESSOR - orderedLayerInfo', cur);

// For each layer path in the layer data array
const curOrderedLayerPaths = cur.map((layerInfo) => layerInfo.layerPath);
const prevOrderedLayerPaths = prev.map((layerInfo) => layerInfo.layerPath);
store
.getState()
.dataTableState.allFeaturesDataArray.map((layerInfo) => layerInfo.layerPath)
.forEach((layerPath) => {
// If it was in the layer data array and is not anymore
if (prevOrderedLayerPaths.includes(layerPath) && !curOrderedLayerPaths.includes(layerPath)) {
// Remove it from all features array
DataTableEventProcessor.#deleteFeatureAllInfo(store.getState().mapId, layerPath);

// Log
logger.logInfo('Removed Feature Info in stores for layer path:', layerPath);
}
});
}
);

return [unsubLayerRemoved];
}

// **********************************************************
// Static functions for Typescript files to access store actions
// **********************************************************
// GV Typescript MUST always use the defined store actions below to modify store - NEVER use setState!
// GV Some action does state modifications AND map actions.
// GV ALWAYS use map event processor when an action modify store and IS NOT trap by map state event handler

// **********************************************************
// Static functions for Store Map State to action on API
// **********************************************************
/**
* Shortcut to get the DataTable state for a given map id
* @param {string} mapId - The mapId
* @returns {IDataTableState} The DataTable state
*/
protected static getDataTableState(mapId: string): IDataTableState {
// Return the DataTable state
return super.getState(mapId).dataTableState;
}

/**
* Filter the map based on filters set on date table.
* @param {string} mapId - Id of the map.
* @param {string} layerPath - Path of the layer
* @param {string} filterStrings - Filters set on the data table
* @param {boolean} isMapRecordExist - Filtered Map switch is on off.
*/
static applyFilters(mapId: string, layerPath: string, filterStrings: string, isMapRecordExist: boolean) {
const geoviewLayerInstance = api.maps[mapId].layer.geoviewLayer(layerPath);
const filterLayerConfig = api.maps[mapId].layer.registeredLayers[layerPath] as TypeLayerEntryConfig;

if (isMapRecordExist && geoviewLayerInstance !== undefined && filterLayerConfig !== undefined && filterStrings.length) {
(geoviewLayerInstance as AbstractGeoViewVector | EsriDynamic)?.applyViewFilter(layerPath, filterStrings);
} else {
(geoviewLayerInstance as AbstractGeoViewVector | EsriDynamic)?.applyViewFilter(layerPath, '');
}
}

/**
* Initialize columns filter information for a layer.
* @param {string} mapId - Id of the map.
* @param {string} layerPath - Path of the layer
*/
static setInitialSettings(mapId: string, layerPath: string): void {
this.getDataTableState(mapId).setterActions.setInitiallayerDataTableSetting(layerPath);
}

/**
* Shortcut to get the DataTable state for a given map id
* @param {string} mapId - Id of the map.
* @param {string} layerPath - Layer path to apply filter.
*/
static triggerGetAllFeatureInfo(mapId: string, layerPath: string): void {
api.maps[mapId].layer.allFeatureInfoLayerSet.queryLayer(layerPath, 'all');
}

/**
* Propagates feature info layer sets to the store
*
* @param {string} mapId - The map identifier of the modified result set.
* @param {string} layerPath - The layer path that has changed.
* @param {TypeFeatureInfoResultSet} resultSet - The result set associated to the map.
*/
static propagateFeatureInfoToStore(mapId: string, layerPath: string, resultSet: TypeFeatureInfoResultSet) {
/**
* Create a get all features info object for each layer which is then used to render layers
*/
const allFeaturesDataArray = [...this.getDataTableState(mapId).allFeaturesDataArray];
if (!allFeaturesDataArray.find((layerEntry) => layerEntry.layerPath === layerPath)) {
allFeaturesDataArray.push((resultSet as TypeFeatureInfoResultSet)?.[layerPath]?.data);
}

// Update the layer data array in the store, all the time
this.getDataTableState(mapId).setterActions.setAllFeaturesDataArray(allFeaturesDataArray);
}

/**
* Deletes the specified layer path from the all features layers sets in the store
* @param {string} mapId - The map identifier
* @param {string} layerPath - The layer path to delete
* @private
*/
static #deleteFeatureAllInfo(mapId: string, layerPath: string) {
// Redirect to helper function
this.#deleteFromArray(this.getDataTableState(mapId).allFeaturesDataArray, layerPath, (layerArrayResult) => {
// Update the layer data array in the store
this.getDataTableState(mapId).setterActions.setAllFeaturesDataArray(layerArrayResult);
});
}

/**
* Helper function to delete a layer information from an array when found
* @param {TypeLayerData[]} layerArray - The layer array to work with
* @param {string} layerPath - The layer path to delete
* @param {(layerArray: TypeLayerData[]) => void} onDeleteCallback - The callback executed when the array is updated
* @private
*/
static #deleteFromArray<T extends TypeLayerData>(layerArray: T[], layerPath: string, onDeleteCallback: (layerArray: T[]) => void) {
// Find the layer data info to delete from the array
const layerDataInfoToDelIndex = layerArray.findIndex((layerInfo) => layerInfo.layerPath === layerPath);

// If found
if (layerDataInfoToDelIndex >= 0) {
// Remove from the array
layerArray.splice(layerDataInfoToDelIndex, 1);

// Callback with updated array
onDeleteCallback(layerArray);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GeoviewStoreType, IFeatureInfoState } from '@/core/stores';
import { GeoviewStoreType } from '@/core/stores';
import { IFeatureInfoState } from '@/core/stores/store-interface-and-intial-values/feature-info-state';
import { logger } from '@/core/utils/logger';
import { TypeFeatureInfoResultSet } from '@/geo/utils/feature-info-layer-set';
import { EventType, TypeLayerData } from '@/geo/utils/layer-set';
Expand Down Expand Up @@ -51,9 +52,6 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {
// Remove it from feature info array
FeatureInfoEventProcessor.#deleteFeatureInfo(store.getState().mapId, layerPath);

// Remove it from all features array
FeatureInfoEventProcessor.#deleteFeatureAllInfo(store.getState().mapId, layerPath);

// Log
logger.logInfo('Removed Feature Info in stores for layer path:', layerPath);
}
Expand Down Expand Up @@ -94,23 +92,6 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {
});
}

/**
* Deletes the specified layer path from the all features layers sets in the store
* @param {string} mapId - The map identifier
* @param {string} layerPath - The layer path to delete
* @private
*/
static #deleteFeatureAllInfo(mapId: string, layerPath: string) {
// The feature info state
const featureInfoState = this.getFeatureInfoState(mapId);

// Redirect to helper function
this.#deleteFromArray(featureInfoState.allFeaturesDataArray, layerPath, (layerArrayResult) => {
// Update the layer data array in the store
featureInfoState.actions.setAllFeaturesDataArray(layerArrayResult);
});
}

/**
* Helper function to delete a layer information from an array when found
* @param {TypeLayerData[]} layerArray - The layer array to work with
Expand Down Expand Up @@ -166,17 +147,6 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {

// Also propagate in the batched array
FeatureInfoEventProcessor.#propagateFeatureInfoToStoreBatch(mapId, layerDataArray);
} else if (eventType === 'all-features') {
/**
* Create a get all features info object for each layer which is then used to render layers
*/
const allFeaturesDataArray = [...featureInfoState.allFeaturesDataArray];
if (!allFeaturesDataArray.find((layerEntry) => layerEntry.layerPath === layerPath)) {
allFeaturesDataArray.push((resultSet as TypeFeatureInfoResultSet)?.[layerPath]?.data);
}

// Update the layer data array in the store, all the time
featureInfoState.actions.setAllFeaturesDataArray(allFeaturesDataArray);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { AppEventProcessor } from './app-event-processor';
import { AbstractEventProcessor } from '@/api/event-processors/abstract-event-processor';
import { TypeMapFeaturesConfig } from '@/core/types/global-types';
import { TypeClickMarker } from '@/core/components';
import { TypeOrderedLayerInfo, TypeScaleInfo } from '@/core/stores';
import { TypeOrderedLayerInfo, TypeScaleInfo } from '@/core/stores/store-interface-and-intial-values/map-state';
import { TypeBasemapOptions, TypeBasemapProps } from '@/geo/layer/basemap/basemap-types';
import { TypeHoverFeatureInfo } from '@/geo/utils/hover-feature-info-layer-set';

Expand Down
8 changes: 4 additions & 4 deletions packages/geoview-core/src/api/event-processors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { LegendEventProcessor } from '@/api/event-processors/event-processor-chi
import { MapEventProcessor } from '@/api/event-processors/event-processor-children/map-event-processor';
import { TimeSliderEventProcessor } from '@/api/event-processors/event-processor-children/time-slider-event-processor';
import { GeochartEventProcessor } from '@/api/event-processors/event-processor-children/geochart-event-processor';
import { DataTableProcessor } from '@/api/event-processors/event-processor-children/data-table-processor';
import { DataTableEventProcessor } from '@/api/event-processors/event-processor-children/data-table-event-processor';
import { SwiperEventProcessor } from './event-processor-children/swiper-event-processor';

// core
const appEventProcessor = new AppEventProcessor();
const featureInfoEventProcessor = new FeatureInfoEventProcessor();
const legendEventProcessor = new LegendEventProcessor();
const mapEventProcessor = new MapEventProcessor();
const dataTableProcessor = new DataTableProcessor();
const dataTableEventProcessor = new DataTableEventProcessor();

// packages
const timeSliderEventProcessor = new TimeSliderEventProcessor();
Expand All @@ -26,7 +26,7 @@ export function initializeEventProcessors(store: GeoviewStoreType) {
featureInfoEventProcessor.initialize(store);
legendEventProcessor.initialize(store);
mapEventProcessor.initialize(store);
dataTableProcessor.initialize(store);
dataTableEventProcessor.initialize(store);

// package stores, only create if needed
// TODO: Change this check for something more generic that checks in appBar too
Expand All @@ -41,7 +41,7 @@ export function destroyEventProcessors(store: GeoviewStoreType) {
featureInfoEventProcessor.destroy();
legendEventProcessor.destroy();
mapEventProcessor.destroy();
dataTableProcessor.destroy();
dataTableEventProcessor.destroy();

// package stores, only destroy if created
// TODO: Change this check for something more generic that checks in appBar too
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useEffect, useRef } from 'react';
import { useAppFullscreenActive } from '@/core/stores/store-interface-and-intial-values/app-state';
import { useUIActiveFooterBarTabId, useUIFooterPanelResizeValue } from '@/core/stores/store-interface-and-intial-values/ui-state';
import { useDetailsLayerDataArray } from '@/core/stores/store-interface-and-intial-values/feature-info-state';
import {
useDetailsStoreAllFeaturesDataArray,
useDetailsStoreLayerDataArray,
} from '@/core/stores/store-interface-and-intial-values/feature-info-state';
useDataTableAllFeaturesDataArray,
useDataTableStoreActions,
} from '@/core/stores/store-interface-and-intial-values/data-table-state';
import { logger } from '@/core/utils/logger';
import { useDataTableStoreActions } from '@/core/stores/store-interface-and-intial-values/data-table-state';
import { useGeoViewMapId } from '@/core/stores/geoview-store';
import { TABS } from '@/core/utils/constant';
import { useGeoViewMapId } from '@/core/stores/geoview-store';

interface UseFooterPanelHeightType {
footerPanelTab: 'layers' | 'details' | 'data-table' | 'legend' | 'default' | 'guide';
Expand All @@ -28,8 +28,8 @@ export function useFooterPanelHeight({ footerPanelTab }: UseFooterPanelHeightTyp
const isMapFullScreen = useAppFullscreenActive();
const footerPanelResizeValue = useUIFooterPanelResizeValue();
const activeFooterBarTabId = useUIActiveFooterBarTabId();
const arrayOfLayerData = useDetailsStoreLayerDataArray();
const allFeaturesLayerData = useDetailsStoreAllFeaturesDataArray();
const arrayOfLayerData = useDetailsLayerDataArray();
const allFeaturesLayerData = useDataTableAllFeaturesDataArray();
const { setTableHeight } = useDataTableStoreActions();

useEffect(() => {
Expand Down
Loading

0 comments on commit 5582588

Please sign in to comment.