import * as I from 'immutable'; import immutablediff from 'immutablediff'; export class StateSynchronizer { constructor(statefuls) { this._state = I.Map(); this._statefuls = statefuls; this.tick(); } acceptStateChange(change) { for (const key in change) { const stateful = this._statefuls[key]; if (!stateful) { continue; } stateful.acceptStateChange(change[key]); } } diff(previousState) { // Take a pure JS diff. const steps = immutablediff(previousState, this._state).toJS(); const updateSteps = steps.filter(StateSynchronizer.isStepUpdate); return StateSynchronizer.hydratePathValues(updateSteps); } static hydratePathValues(pathValues) { let accumulated = {}; for (const {path, value} of pathValues) { if ('/' === path) { accumulated = value; } else { const parts = path.split('/'); parts.shift(); let walk = accumulated; for (let i = 0; i < parts.length; ++i) { const part = parts[i]; walk[part] = walk[part] || {}; if (i === parts.length - 1) { walk[part] = value; } else { walk = walk[part]; } } } } return accumulated; } static isStepUpdate(step) { return -1 !== ['add', 'replace'].indexOf(step.op); } get state() { return this._state; } tick() { for (const key in this._statefuls) { const stateful = this._statefuls[key]; this._state = this._state.set(key, stateful.state); } } }