Skip to content

Commit

Permalink
feature(Swiper) - Using new Swiper store, small guide fix, better inf…
Browse files Browse the repository at this point in the history
…o logs, some other fixes and comments (#1858)

* Started
Stores created
Style class created and separated from component
Fix for the path for the guide panel
Swiper, working state
Swiper improved
Moved the addEventListener and removeEventListener from the rendering phase to a useEffect mount hook.
Better logs and comments
Swiper working
Had to use async afterall to retrieve the OpenLayers layers
Adjusting the swiper template configs to use layer path
Finalizing
Cleanup

* Integrated comments
  • Loading branch information
Alex-NRCan authored Feb 26, 2024
1 parent 1c0e857 commit 0ce303d
Show file tree
Hide file tree
Showing 22 changed files with 700 additions and 295 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"orientation": "vertical",
"keyboardOffset": 10,
"layers": ["esriFeatureLYR4"],
"layers": ["esriFeatureLYR4/0"],
"suportedLanguages": ["en", "fr"]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"orientation": "horizontal",
"keyboardOffset": 10,
"layers": ["esriFeatureLYR4-map2", "esriDynamicLYR3-map2"],
"layers": ["esriFeatureLYR4-map2/2", "esriDynamicLYR3-map2/0"],
"suportedLanguages": ["en", "fr"]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"orientation": "vertical",
"keyboardOffset": 10,
"layers": ["swipe0", "swipe1", "swipe4", "swipe5", "swipe6", "rcs.ccc75c12-5acc-4a6a-959f-ef6f621147b9.en", "swipe7"],
"layers": ["swipe0/toner", "swipe1/msi-94-or-more", "swipe4/polygons.json", "swipe5/ec-msc:CURRENT_CONDITIONS", "swipe6/lakes", "rcs.ccc75c12-5acc-4a6a-959f-ef6f621147b9.en/0"],
"suportedLanguages": ["en", "fr"]
}
18 changes: 18 additions & 0 deletions packages/geoview-core/public/templates/package-swiper.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ <h5 id="HUC3-state">(undefined)</h5>
</div>
<div id="mapWM3" class="geoview-map" data-lang="en" data-config-url="./configs/package-swiper3-config.json"></div>
<p>Configuration with supported layers type (WMS, XYZ, WFS, GeoJSON, OGC Feature API and GeoCore)</p>
<input id="mapWM3Input" placeholder="Type a layer path" />
<button onclick="addSwiper()">Add swiper functionality</button>
<button onclick="removeSwiper()">Remove swiper functionality</button>
<button onclick="removeSwiperAll()">Remove all swiper functionalities</button>
<ul><li>swipe0/toner</li><li>swipe1/msi-94-or-more</li><li>swipe4/polygons.json</li><li>swipe5/ec-msc:CURRENT_CONDITIONS</li><li>swipe6/lakes</li><li>rcs.ccc75c12-5acc-4a6a-959f-ef6f621147b9.en/0</li></ul>
<hr />

<script src="codedoc.js"></script>
Expand All @@ -79,6 +84,19 @@ <h5 id="HUC3-state">(undefined)</h5>
createConfigSnippet();
createCodeSnippet();
});

function addSwiper() {
cgpv.api.maps['mapWM3'].plugins['swiper'].activateForLayer(document.getElementById('mapWM3Input').value);
}

function removeSwiper() {
cgpv.api.maps['mapWM3'].plugins['swiper'].deActivateForLayer(document.getElementById('mapWM3Input').value);
}

function removeSwiperAll() {
cgpv.api.maps['mapWM3'].plugins['swiper'].deActivateAll();
}

window.setInterval(() => {
const displayField1 = document.getElementById('HUC3-state');
const geoviewLayers = cgpv.api.getFeatureInfoLayerSet('mapWM3').resultsSet;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export abstract class AbstractEventProcessor {

// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected onInitialize(store: GeoviewStoreType): Array<() => void> | void {
// Here, `store` is unused, but used in inherited classes, so the eslint-disable should be kept
// This method should be overriden to initialize and return a list of subscribtions so that they can be destroyed later
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {
FeatureInfoEventProcessor.deleteFeatureAllInfo(store.getState().mapId, layerPath);

// Log
logger.logDebug('Removed Feature Info in stores for layer path:', layerPath);
logger.logInfo('Removed Feature Info in stores for layer path:', layerPath);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,6 @@ import { AbstractEventProcessor, BatchedPropagationLayerDataArrayByMap } from '.
* Event processor focusing on interacting with the geochart state in the store.
*/
export class GeochartEventProcessor extends AbstractEventProcessor {
// **********************************************************
// Static functions for Typescript files to access store actions
// **********************************************************
//! Typescript MUST always use the defined store actions below to modify store - NEVER use setState!
//! Some action does state modifications AND map actions.
//! ALWAYS use map event processor when an action modify store and IS NOT trap by map state event handler

// #region
// Holds the list of layer data arrays being buffered in the propagation process for the batch
static batchedPropagationLayerDataArray: BatchedPropagationLayerDataArrayByMap = {};

// The time delay between propagations in the batch layer data array.
// The longer the delay, the more the layers will have a chance to get in a loaded state before changing the layerDataArray.
// The longer the delay, the longer it'll take to update the UI. The delay can be bypassed using the layer path bypass method.
static timeDelayBetweenPropagationsForBatch = 2000;

/**
* Overrides initialization of the GeoChart Event Processor
* @param {GeoviewStoreType} store The store associated with the GeoChart Event Processor
Expand All @@ -47,9 +31,6 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
if (prevOrderedLayerPaths.includes(layerPath) && !curOrderedLayerPaths.includes(layerPath)) {
// Remove it
GeochartEventProcessor.removeGeochartChart(store.getState().mapId, layerPath);

// Log
logger.logDebug('Removed GeoChart configs for layer path:', layerPath);
}
});
}
Expand All @@ -70,6 +51,22 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
return [unsubLayerRemoved, layerDataArrayUpdate];
}

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

// #region
// Holds the list of layer data arrays being buffered in the propagation process for the batch
static batchedPropagationLayerDataArray: BatchedPropagationLayerDataArrayByMap = {};

// The time delay between propagations in the batch layer data array.
// The longer the delay, the more the layers will have a chance to get in a loaded state before changing the layerDataArray.
// The longer the delay, the longer it'll take to update the UI. The delay can be bypassed using the layer path bypass method.
static timeDelayBetweenPropagationsForBatch = 2000;

/**
* Shortcut to get the Geochart state for a given map id
* @param {string} mapId The mapId
Expand All @@ -95,18 +92,23 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
const chartData: GeoChartStoreByLayerPath = {};

// Loop on the charts
const layerPaths: string[] = [];
charts.forEach((chartInfo) => {
// For each layer path
chartInfo.layers.forEach((layer) => {
// Get the layer path
const layerPath = layer.layerId;
chartData[layerPath] = chartInfo;
layerPaths.push(layerPath);
});
});

// set store charts config
this.getGeochartState(mapId)?.actions.setGeochartCharts(chartData);

// Log
logger.logInfo('Added GeoChart configs for layer paths:', layerPaths);

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}

Expand All @@ -128,6 +130,9 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
// Update the layer data array in the store
this.getGeochartState(mapId)!.actions.setGeochartCharts({ ...this.getGeochartState(mapId)?.geochartChartsConfig, ...toAdd });

// Log
logger.logInfo('Added GeoChart configs for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}

Expand All @@ -153,6 +158,9 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
// Update the layer data array in the store
this.getGeochartState(mapId)!.actions.setGeochartCharts({ ...chartConfigs });

// Log
logger.logInfo('Removed GeoChart configs for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { GeoviewStoreType } from '@/core/stores';
import { ISwiperState } from '@/core/stores/store-interface-and-intial-values/swiper-state';
import { logger } from '@/core/utils/logger';

import { AbstractEventProcessor } from '../abstract-event-processor';

/**
* Event processor focusing on interacting with the swiper state in the store.
*/
export class SwiperEventProcessor extends AbstractEventProcessor {
/**
* Overrides initialization of the Swiper Event Processor
* @param {GeoviewStoreType} store The store associated with the Swiper 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('SWIPER EVENT PROCESSOR - orderedLayerInfo', cur);

// Read the layer paths of each layer info
const curOrderedLayerPaths = cur.map((layerInfo) => layerInfo.layerPath);
const prevOrderedLayerPaths = prev.map((layerInfo) => layerInfo.layerPath);

// Get all the layer paths to check in a distinct array for looping purposes
const layerPathsToCheck = [...store.getState().swiperState.layerPaths];

// For each layer paths the swiper is using
layerPathsToCheck.forEach((layerPath) => {
// If it was in the layerdata array and is not anymore
if (prevOrderedLayerPaths.includes(layerPath) && !curOrderedLayerPaths.includes(layerPath)) {
// Remove it
SwiperEventProcessor.removeLayerPath(store.getState().mapId, layerPath);
}
});
}
);

return [unsubLayerRemoved];
}

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

/**
* Shortcut to get the Swiper state for a given map id
* @param {string} mapId The mapId
* @returns {ISwiperState | undefined} The Swiper state. Forcing the return to also be 'undefined', because
* there will be no swiperState if the Swiper plugin isn't active.
* This helps the developers making sure the existence is checked.
*/
protected static getSwiperState(mapId: string): ISwiperState | undefined {
// Return the swiper state when it exists
return super.getState(mapId).swiperState;
}

/**
* Sets the layer paths on which the swiper should be activated.
*
* @param {string} mapId the map id
* @param {string[]} layerPaths The array of layer paths
*/
static setLayerPaths(mapId: string, layerPaths: string[]): void {
// set store layer paths
this.getSwiperState(mapId)?.actions.setLayerPaths(layerPaths);

// Log
logger.logInfo('Added Swiper functionality for layer paths:', layerPaths);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
}

/**
* Adds a swiper functionality to the specified map id and layer path
* @param {string} mapId The map ID
* @param {string} layerPath The layer path
*/
static addLayerPath(mapId: string, layerPath: string): void {
// The processor needs an initialized layer paths store which is only initialized if the Swiper Plugin exists.
// Therefore, we validate its existence first.
if (!this.getSwiperState(mapId)) return;
if (!this.getSwiperState(mapId)?.layerPaths) return;

// If not already added
if (!this.getSwiperState(mapId)!.layerPaths.includes(layerPath)) {
// Add in the array
const updatedArray = [...this.getSwiperState(mapId)!.layerPaths];
updatedArray.push(layerPath);

// Update the layer data array in the store
this.getSwiperState(mapId)!.actions.setLayerPaths(updatedArray);

// Log
logger.logInfo('Added Swiper functionality for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
} else {
// Log
logger.logInfo('Swiper functionality already active for layer path:', layerPath);
}
}

/**
* Removes a swiper functionality for the specified map id and layer path
* @param {string} mapId The map ID
* @param {string} layerPath The layer path
*/
static removeLayerPath(mapId: string, layerPath: string): void {
// The processor needs an initialized layer paths store which is only initialized if the Swiper Plugin exists.
// Therefore, we validate its existence first.
if (!this.getSwiperState(mapId)) return;
if (!this.getSwiperState(mapId)?.layerPaths) return;

// Find the index with the layer path
const layerIndex = this.getSwiperState(mapId)!.layerPaths.findIndex((layer) => layer === layerPath);

// Config to remove
if (layerIndex !== undefined && layerIndex >= 0) {
// Remove from the array
const updatedArray = [...this.getSwiperState(mapId)!.layerPaths];
updatedArray.splice(layerIndex, 1);

// Update the layer data array in the store
this.getSwiperState(mapId)!.actions.setLayerPaths(updatedArray);

// Log
logger.logInfo('Removed Swiper functionality for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
} else {
// Log
logger.logInfo('Swiper functionality already inactive for layer path:', layerPath);
}
}

/**
* Removes the swiper functionality for all layer paths
* @param {string} mapId The map ID
*/
static removeAll(mapId: string) {
// The processor needs an initialized layer paths store which is only initialized if the Swiper Plugin exists.
// Therefore, we validate its existence first.
if (!this.getSwiperState(mapId)) return;
if (!this.getSwiperState(mapId)?.layerPaths) return;

// Get all layer paths
const { layerPaths } = this.getSwiperState(mapId)!;

// Update the layer data array in the store
this.getSwiperState(mapId)!.actions.setLayerPaths([]);

// Log
logger.logInfo('Removed Swiper functionality for all layer paths', layerPaths);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
}

// #endregion

// **********************************************************
// Static functions for Store Map State to action on API
// **********************************************************
//! NEVER add a store action who does set state AND map action at a same time.
//! Review the action in store state to make sure
}
4 changes: 4 additions & 0 deletions packages/geoview-core/src/api/event-processors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MapEventProcessor } from '@/api/event-processors/event-processor-childr
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 { SwiperEventProcessor } from './event-processor-children/swiper-event-processor';

// core
const appEventProcessor = new AppEventProcessor();
Expand All @@ -17,6 +18,7 @@ const dataTableProcessor = new DataTableProcessor();
// packages
const timeSliderEventProcessor = new TimeSliderEventProcessor();
const geochartEventProcessor = new GeochartEventProcessor();
const swiperEventProcessor = new SwiperEventProcessor();

export function initializeEventProcessors(store: GeoviewStoreType) {
// core stores
Expand All @@ -30,6 +32,7 @@ export function initializeEventProcessors(store: GeoviewStoreType) {
// TODO: Change this check for something more generic that checks in appBar too
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('time-slider')) timeSliderEventProcessor.initialize(store);
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('geochart')) geochartEventProcessor.initialize(store);
if (store.getState().mapConfig!.corePackages?.includes('swiper')) swiperEventProcessor.initialize(store);
}

export function destroyEventProcessors(store: GeoviewStoreType) {
Expand All @@ -44,4 +47,5 @@ export function destroyEventProcessors(store: GeoviewStoreType) {
// TODO: Change this check for something more generic that checks in appBar too
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('time-slider')) timeSliderEventProcessor.destroy();
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('geochart')) geochartEventProcessor.destroy();
if (store.getState().mapConfig!.corePackages?.includes('swiper')) swiperEventProcessor.destroy();
}
Loading

0 comments on commit 0ce303d

Please sign in to comment.