@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
115 lines (114 loc) • 4.67 kB
JavaScript
/* 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;