import * as I from 'immutable'; import immutablediff from 'immutablediff'; export class StateSynchronizer { constructor(statefuls) { this._state = I.Map(); this._statefuls = statefuls; this.updateState(); } acceptStateChange(change) { for (const key in change) { const stateful = this._statefuls[key]; if (!stateful) { continue; } stateful.acceptStateChange(change[key]); } } diff() { const state = this.state(); if (this.previousState === state) { return StateSynchronizer.noChange; } // Take a pure JS diff. const steps = immutablediff(this.previousState, state).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]; } } } } } // Side-effect. this.previousState = this.state(); return diff; } state() { this.updateState(); return this._state; } updateState() { for (const key in this._statefuls) { const stateful = this._statefuls[key]; this._state = this._state.set(key, stateful.state()); } } } StateSynchronizer.noChange = {};