UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

115 lines (114 loc) 4.67 kB
/* eslint @typescript-eslint/no-explicit-any: 0 */ import GirafeSingleton from '../../base/GirafeSingleton'; import State from './state'; import ConfigManager from '../configuration/configmanager'; import { getPropertyByPath } from '../utils/pathUtils'; import Brain from './brain/brain'; class StateManager extends GirafeSingleton { get state() { return this.stateProxy.getState(); } constructor(type) { super(type); Object.defineProperty(this, "girafeState", { enumerable: true, configurable: true, writable: true, value: new State() }); Object.defineProperty(this, "stateProxy", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "callbacks", { enumerable: true, configurable: true, writable: true, value: {} }); Object.defineProperty(this, "configManager", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.configManager = ConfigManager.getInstance(); this.stateProxy = new Brain(this.girafeState, (path, oldValue, newValue, parents) => { this.onChange(path, oldValue, newValue, parents); }); this.setDefaultValues(); } setDefaultValues() { // Set default values this.configManager?.loadConfig().then(() => { const config = this.configManager?.Config; if (this.state && config) { this.state.projection = config.map.srid; this.state.language = config.languages.defaultLanguage; this.state.interface.selectionComponent = config.interface.defaultSelectionComponent; } }); } onChange(property, oldValue, value, parents) { const path = property.trim(); for (const key in this.callbacks) { const regex = new RegExp('^' + key + '$'); if (path.match(regex)) { const callbacks = this.callbacks[key]; for (const callback of callbacks) { callback(oldValue, value, parents.length === 1 ? parents[0] : parents); } } } } subscribe(path, callback) { const pathAsString = typeof path === 'string' ? path : path.source; if (!(pathAsString in this.callbacks)) { this.callbacks[pathAsString] = []; } this.callbacks[pathAsString].push(callback); console.debug(`Subscribing to ${path}. ${this.callbacks[pathAsString].length} are currently subscribing ${pathAsString}.`); // At the application start, perhaps the value in state was initialized before the subscribe method was called // Therefore, if the subscribed value os not null, undefined or an empty object or array // We immediately call the callback. const obj = getPropertyByPath(this.state, pathAsString); if (obj.found) { if (obj.object === null || obj.object === undefined || (Array.isArray(obj.object) && obj.object.length === 0) || (obj.object instanceof Object && Object.keys(obj.object).length === 0)) { // Empty object => nothing to do } else { // Object is not null during the subscribe. => we call the callback const parentPath = pathAsString.substring(0, pathAsString.lastIndexOf('.')); const parentObject = getPropertyByPath(this.state, parentPath); callback(null, obj.object, parentObject.object); } } return callback; } unsubscribe(callbacks) { (Array.isArray(callbacks) ? callbacks : [callbacks]).forEach((callback) => { let found = false; for (const path in this.callbacks) { const callbacks = this.callbacks[path]; const index = callbacks.indexOf(callback); if (index !== -1) { found = true; callbacks.splice(index, 1); console.debug(`Unsubscribing to ${path}. ${this.callbacks[path].length} subscribtions remaining.`); } } if (!found) { throw Error(`Cannot unsubscribe this callback : it does not exist`); } }); } batchChanges(statechanges) { this.stateProxy.delay(statechanges); } } export default StateManager;