@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
94 lines (93 loc) • 3.97 kB
JavaScript
// SPDX-License-Identifier: Apache-2.0
/* eslint @typescript-eslint/no-explicit-any: 0 */
import GirafeSingleton from '../../base/GirafeSingleton.js';
import State from './state.js';
import { getPropertyByPath } from '../utils/pathUtils.js';
import Brain from './brain/brain.js';
class StateManager extends GirafeSingleton {
girafeState = new State();
stateProxy;
get state() {
return this.stateProxy.getState();
}
callbacks = {};
constructor(context) {
super(context);
this.stateProxy = new Brain(this.girafeState, (path, oldValue, newValue, parents) => {
this.onChange(path, oldValue, newValue, parents);
});
}
initializeSingleton() {
this.setDefaultValues();
}
setDefaultValues() {
// Set default values
const config = this.context.configManager.Config;
if (this.state && config) {
this.state.application.isConfigurationLoaded = true;
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;