72 lines
1.6 KiB
JavaScript
72 lines
1.6 KiB
JavaScript
|
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 = {};
|