@state-sync/redux-path-reducer
Version:
state-sync client only json path reducer
275 lines (274 loc) • 10.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//import { Promise } from 'es6-promise';
var jiff = require("jiff");
var Events_1 = require("./Events");
var InvocationMap_1 = require("./InvocationMap");
var Patch_1 = require("./Patch");
var SyncAreaConfig_1 = require("./SyncAreaConfig");
var find_1 = require("./utils/find");
var SyncArea = /** @class */ (function () {
function SyncArea(name, initialState, helper) {
this.subscribed = false;
this.patchQueue = [];
this.initialState = initialState;
this.helper = helper;
this.name = name;
this.subscriptionsCount = 0;
this.invocations = new InvocationMap_1.InvocationMap();
this.ready = false;
this.modelVersion = 0;
}
SyncArea.prototype.isReady = function () {
return this.ready;
};
SyncArea.prototype.init = function () {
this.helper.dispatch({ type: '@STATE_SYNC/SYNC_AREA_STATUS', area: this.name, status: 'disconnected', ready: false });
};
SyncArea.prototype.wrap = function (reducer) {
var _this = this;
return function (state, action, ext) { return _this.reduce(state, action, ext, reducer); };
};
SyncArea.prototype.model = function () {
return this.local;
};
SyncArea.prototype.select = function (path) {
return new Patch_1.OpSelect({ op: 'select', path: path }).apply(this.local);
};
/**
* Invoke when connection is ready
*/
SyncArea.prototype.onReady = function () {
this.doSubscription();
};
SyncArea.prototype.subscribe = function () {
this.subscriptionsCount++;
return this.doSubscription();
};
SyncArea.prototype.doSubscription = function () {
var _this = this;
if (!this.subscribed && this.helper.isFullyConnected()) {
this.subscribed = true;
return this.invocations.request(function (id) {
_this.helper.send(new Events_1.SubscribeAreaRequest(id, _this.name));
});
}
else {
return new Promise(function (resolve, error) { return resolve(0); });
}
};
SyncArea.prototype.unsubscribe = function () {
var _this = this;
this.subscriptionsCount--;
if (this.subscriptionsCount == 0) {
this.subscribed = false;
this.invocations.request(function (id) {
_this.helper.send(new Events_1.UnsubscribeAreaRequest(id, _this.name));
});
}
};
SyncArea.prototype.signal = function (command, parameters) {
var _this = this;
return this.invocations.request(function (id) {
_this.helper.send(new Events_1.SignalRequest(id, _this.name, command, parameters));
});
};
SyncArea.prototype.onSignalResponse = function (event) {
if (this.invocations.response(event.forId)) {
this.pushPatches();
}
};
SyncArea.prototype.onSignalError = function (event) {
if (this.invocations.error(event.forId, event.error)) {
this.pushPatches();
}
};
SyncArea.prototype.onSubscribe = function (event) {
this.ready = true;
this.invocations.response(event.forId);
this.config = event.config;
this.invocations.timeout = event.config.timeout;
this.helper.dispatch({ type: '@STATE_SYNC/SYNC_AREA_INIT', area: event.area, payload: event.model });
this.helper.dispatch({ type: '@STATE_SYNC/SYNC_AREA_STATUS', area: event.area, status: 'ready', ready: true });
};
// tslint:disable
SyncArea.prototype.onUnsubscribe = function (event) {
this.ready = false;
this.invocations.response(event.forId);
this.config = new SyncAreaConfig_1.default();
this.helper.dispatch({ type: '@STATE_SYNC/SYNC_AREA_STATUS', area: event.area, status: 'disconnected', ready: false });
this.helper.dispatch({ type: '@STATE_SYNC/SYNC_AREA_INIT', payload: this.initialState });
};
SyncArea.prototype.dispatchSyncPatch = function (event) {
this.helper.dispatch({ type: '@STATE_SYNC/SYNC_AREA_SERVER_PATCH', area: event.area, payload: event.patch });
};
SyncArea.prototype.onPatchResponse = function (event) {
if (this.invocations.response(event.forId)) {
this.pushPatches();
}
};
SyncArea.prototype.pushPatches = function () {
var _this = this;
this.patchQueue.forEach(function (cmd) {
_this.dispatchSyncPatch(cmd);
});
this.patchQueue = [];
};
SyncArea.prototype.onServerPatch = function (event) {
if (this.invocations.isEmpty()) {
this.dispatchSyncPatch(event);
}
else {
this.patchQueue.push(event);
}
};
SyncArea.prototype.onPatchAreaError = function (event) {
console.error(event);
};
SyncArea.prototype.onSubscribeError = function (event) {
console.error(event);
};
SyncArea.prototype.actionArrayInsertByKey = function (path, item, keyField) {
this.actionReduce(path, function (array) {
if (array) {
var tmp = array.concat([item]);
tmp.sort(function (a, b) {
var ak = a[keyField];
var bk = b[keyField];
if (ak < bk) {
return -1;
}
if (ak > bk) {
return 1;
}
return 0;
});
return tmp;
}
else {
return [item];
}
});
};
SyncArea.prototype.actionArrayReplaceByKey = function (path, data, keyField) {
this.actionReduce(path, function (array) {
var copy = array.slice();
for (var i = 0; i < copy.length; i++) {
var item = copy[i];
if (item[keyField] === data[keyField]) {
copy[i] = item;
}
}
return copy;
});
};
SyncArea.prototype.actionArrayRemoveByKey = function (path, keyField, value) {
this.actionReduce(path, function (array) {
var tmp = array.slice();
tmp.filter(function (item) { return item[keyField] == value; });
return tmp;
});
};
SyncArea.prototype.actionArrayRemoveByIndex = function (path, index) {
this.actionReduce(path, function (array) {
return array.splice(index, 1);
});
};
SyncArea.prototype.actionReplace = function (path, value) {
this.helper.dispatch({
type: '@STATE_SYNC/SYNC_AREA_LOCAL_PATCH', area: this.name, payload: [{
op: 'replace', path: path, value: value
}]
});
};
SyncArea.prototype.actionRemove = function (path, condition) {
try {
var value = find_1.default(this.local, path);
if (!condition || condition(value)) {
this.helper.dispatch({
type: '@STATE_SYNC/SYNC_AREA_LOCAL_PATCH', area: this.name, payload: [{
op: 'remove', path: path
}]
});
}
}
catch (e) {
console.error(e);
}
};
SyncArea.prototype.actionReduce = function (path, reducer) {
try {
var value = find_1.default(this.local, path);
this.actionReplace(path, reducer(value));
}
catch (e) {
console.error(e);
}
};
SyncArea.prototype.actionToggle = function (path) {
try {
var value = find_1.default(this.local, path);
this.actionReplace(path, !value);
}
catch (e) {
console.error(e);
}
};
SyncArea.prototype.reduce = function (state, action, ext, reducer) {
// initialization
if (state === undefined)
return this.initialState;
// sync
try {
var fit = (this.name === action.area) && action.type.indexOf('@STATE_SYNC/') === 0;
if (fit) {
switch (action.type) {
case '@STATE_SYNC/SYNC_AREA_INIT':
this.local = action.payload;
this.local.syncStateLastUpdateVersion = this.modelVersion++;
return this.local;
case '@STATE_SYNC/SYNC_AREA_SERVER_PATCH':
this.local = new Patch_1.Patch(action.payload).apply(state);
this.local.syncStateLastUpdateVersion = this.modelVersion++;
return this.local;
case '@STATE_SYNC/SYNC_AREA_LOCAL_PATCH':
try {
return this.detectChanges(state, this.local = new Patch_1.Patch(action.payload).apply(state));
}
catch (e) {
console.error('Local patch failed', state, action.payload);
}
}
}
}
catch (e) {
console.error('action failed:', action, e);
}
// pass action to normal reducers
return this.detectChanges(state, this.local = reducer ? reducer(state, action, ext) : state);
};
SyncArea.prototype.detectChanges = function (from, to) {
var _this = this;
if (this.config) {
// if state is the same - skip detection.
if (to === from) {
return to;
}
var patch_1 = jiff.diff(from, to);
var roots_1 = this.config.clientPush;
var prefix_1 = '/' + this.config.clientLocalPrefix;
patch_1 = patch_1
.filter(function (op) { return op.path.indexOf(prefix_1) < 0; })
.filter(function (op) { return op.op !== 'test'; })
.filter(function (op) { return roots_1.filter(function (root) { return op.path.indexOf(root) === 0; }).length > 0; });
if (patch_1.length > 0) {
this.invocations.request(function (id) {
_this.helper.send(new Events_1.PatchAreaRequest(id, _this.name, patch_1));
});
}
}
return to;
};
return SyncArea;
}());
exports.SyncArea = SyncArea;