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) { const state = this._state; const diff = {}; for (const key in this._statefuls) { if (previousState.get(key) !== state.get(key)) { diff[key] = this.diffStateful( previousState.get(key), state.get(key) ); } } return diff; } diffStateful(previous, current) { // Take a pure JS diff. const steps = immutablediff(previous, current).toJS(); let diff = {}; for (const {op, path, value} of steps) { if ('replace' === op || 'add' === op) { if ('/' === path) { diff = value; } else { const parts = path.split('/'); parts.shift(); let walk = diff; 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 diff; } 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); } } }