/**
* @summary store
* @version 3.0.0
* @since 3.0.0
* @author Arian Khosravi <arian.khosravi@aofl.com>
*/
import {RegisterCallback} from '@aofl/register-callback';
import {deepFreeze} from '@aofl/object-utils';
const TICK = Symbol('tick');
const MICRO_TASK = Symbol('microtask');
/**
* Simple yet powerful implementation of flux type data store.
*
* @memberof module:@aofl/store
*/
class Store {
/**
* Creates an instance of Store.
* @param {Boolean} debug
*/
constructor(debug = false) {
this.debug = debug;
this.state = {};
this.namespaces = {};
this.registerCallback = new RegisterCallback();
this[TICK] = null;
this[MICRO_TASK] = () => {
this.registerCallback.next();
this[TICK] = null;
};
if (debug === true || /* istanbul ignore next */typeof window.aoflDevtools !== 'undefined') {
this.debug = true;
this.state = deepFreeze(this.state);
/* istanbul ignore next */
if (typeof window.aoflDevtools === 'undefined') {
window.aoflDevtools = {};
}
/* istanbul ignore next */
if (!Array.isArray(window.aoflDevtools.storeInstances)) {
window
.aoflDevtools
.storeInstances = [];
}
window.aoflDevtools.storeInstances.push(this);
}
}
/**
* subscribe() register the callback function with registerCallback and returns
* the unsubscribe function.
*
* @param {Furtion} callback
* @return {Function}
*/
subscribe(callback) {
return this.registerCallback.register(callback);
}
/**
* getState() return the current state.
*
* @return {Object}
*/
getState() {
return this.state;
}
/**
*
* @param {SDO} sdo
*/
addState(sdo) {
/* istanbul ignore next */
if (typeof this.namespaces[sdo.namespace] !== 'undefined') {
throw new Error(`${this.constructor.name}: Cannot redefine existing namespace ${sdo.namespace}`);
}
this.namespaces[sdo.namespace] = sdo;
this.commit(sdo.namespace, sdo.initialState || sdo.constructor.initialState);
}
/**
* Copies staged changes to state and notifies subscribers
*/
commit(namespace, subState) {
const state = Object.assign({}, this.state, {
[namespace]: subState
});
this.replaceState(state);
}
/**
* replaceState() take a state object, replaces the state property and notifies subscribers.
*
* @param {Object} state
*/
replaceState(state) {
this.state = state;
if (this.debug) {
this.state = deepFreeze(this.state);
}
this.dispatch();
}
/**
* Resets the state to the initial state of Sdos.
*/
flushState() {
const state = {};
for (const key in this.namespaces) {
/* istanbul ignore next */
if (!Object.hasOwnProperty.call(this.namespaces, key)) continue;
const sdo = this.namespaces[key];
state[sdo.namespace] = Object.assign({}, sdo.constructor.initialState);
}
this.replaceState(state);
}
/**
* Batches all calls to dispatch and notifies subscribers on next tick.
*/
dispatch() {
if (this[TICK]) {
return;
}
this[TICK] = setTimeout(this[MICRO_TASK]);
}
}
export {
Store
};