import * as I from 'immutable'; import {compose, Property} from '@avocado/core'; import {nextStep} from './next-step'; const decorate = compose( Property('state'), ); export function Synchronized(Superclass) { return class Synchronized extends decorate(Superclass) { constructor(...args) { super(...args); this.state = I.Map(); this._childrenNeedInitialization = true; this._childrenWithState = [] this._childrenWithoutState = []; this._childrenTickers = []; } ensureChildrenAreSynchronized() { if (!this._childrenNeedInitialization) { return; } this._childrenNeedInitialization = false; const synchronizedChildren = this.synchronizedChildren(); for (let i = 0; i < synchronizedChildren.length; ++i) { const key = synchronizedChildren[i]; if ( 'undefined' !== typeof this[key] && 'undefined' !== typeof this[key].tick ) { this._childrenTickers.push(this[key].tick.bind(this[key])); } if ( 'undefined' !== typeof this[key] && 'undefined' !== typeof this[key].state ) { this._childrenWithState.push(key); } else { this._childrenWithoutState.push(key); } } } patchState(patch) { for (const step of patch) { const {op, path, value} = step; if ('/' === path) { for (const key in value) { this.patchStateStep(key, { op, path, value: value[key], }); } } else { const [key, substep] = nextStep(step); this.patchStateStep(key, { op: substep.op, path: substep.path, value: substep.value, }); } } } patchStateStep(key, step) { if (!(key in this)) { return; } if ( 'undefined' !== typeof this[key] && 'undefined' !== typeof this[key].patchState ) { this[key].patchState([step]); } else { this[key] = step.value; } } synchronizedChildren() { return []; } tickSynchronized(elapsed) { this.ensureChildrenAreSynchronized(); for (let i = 0; i < this._childrenTickers.length; ++i) { this._childrenTickers[i](elapsed); } if (AVOCADO_SERVER) { this.tickMutateState(); } } tickMutateState() { this.state = this.state.withMutations((state) => { for (let i = 0; i < this._childrenWithState.length; ++i) { const key = this._childrenWithState[i]; state.set(key, this[key].state); } for (let i = 0; i < this._childrenWithoutState.length; ++i) { const key = this._childrenWithoutState[i]; state.set(key, this[key]); } }); } } }