jason-rails 0.4.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.ruby-version +1 -0
- data/Gemfile.lock +152 -2
- data/README.md +117 -5
- data/app/controllers/jason/api/pusher_controller.rb +15 -0
- data/app/controllers/jason/api_controller.rb +44 -2
- data/client/lib/JasonContext.d.ts +6 -1
- data/client/lib/JasonContext.js +4 -1
- data/client/lib/JasonProvider.d.ts +2 -2
- data/client/lib/JasonProvider.js +5 -124
- data/client/lib/createJasonReducers.js +48 -3
- data/client/lib/createOptDis.js +0 -2
- data/client/lib/createPayloadHandler.d.ts +9 -1
- data/client/lib/createPayloadHandler.js +47 -55
- data/client/lib/createServerActionQueue.d.ts +10 -0
- data/client/lib/createServerActionQueue.js +48 -0
- data/client/lib/createServerActionQueue.test.d.ts +1 -0
- data/client/lib/createServerActionQueue.test.js +37 -0
- data/client/lib/createTransportAdapter.d.ts +5 -0
- data/client/lib/createTransportAdapter.js +20 -0
- data/client/lib/index.d.ts +5 -2
- data/client/lib/index.js +3 -1
- data/client/lib/makeEager.js +2 -2
- data/client/lib/pruneIdsMiddleware.d.ts +2 -0
- data/client/lib/pruneIdsMiddleware.js +24 -0
- data/client/lib/restClient.d.ts +2 -0
- data/client/lib/restClient.js +17 -0
- data/client/lib/transportAdapters/actionCableAdapter.d.ts +5 -0
- data/client/lib/transportAdapters/actionCableAdapter.js +35 -0
- data/client/lib/transportAdapters/pusherAdapter.d.ts +5 -0
- data/client/lib/transportAdapters/pusherAdapter.js +68 -0
- data/client/lib/useJason.d.ts +5 -0
- data/client/lib/useJason.js +94 -0
- data/client/lib/useJason.test.d.ts +1 -0
- data/client/lib/useJason.test.js +85 -0
- data/client/lib/useSub.d.ts +1 -1
- data/client/lib/useSub.js +6 -3
- data/client/package.json +5 -3
- data/client/src/JasonContext.ts +4 -1
- data/client/src/JasonProvider.tsx +5 -123
- data/client/src/createJasonReducers.ts +56 -3
- data/client/src/createOptDis.ts +0 -2
- data/client/src/createPayloadHandler.ts +53 -64
- data/client/src/createServerActionQueue.test.ts +42 -0
- data/client/src/createServerActionQueue.ts +47 -0
- data/client/src/createTransportAdapter.ts +13 -0
- data/client/src/index.ts +3 -1
- data/client/src/makeEager.ts +2 -2
- data/client/src/pruneIdsMiddleware.ts +24 -0
- data/client/src/restClient.ts +14 -0
- data/client/src/transportAdapters/actionCableAdapter.ts +38 -0
- data/client/src/transportAdapters/pusherAdapter.ts +72 -0
- data/client/src/useJason.test.ts +87 -0
- data/client/src/useJason.ts +110 -0
- data/client/src/useSub.ts +6 -3
- data/client/yarn.lock +71 -3
- data/config/routes.rb +5 -1
- data/jason-rails.gemspec +4 -0
- data/lib/jason.rb +61 -1
- data/lib/jason/api_model.rb +2 -12
- data/lib/jason/broadcaster.rb +19 -0
- data/lib/jason/channel.rb +50 -21
- data/lib/jason/graph_helper.rb +165 -0
- data/lib/jason/includes_helper.rb +108 -0
- data/lib/jason/lua_generator.rb +71 -0
- data/lib/jason/publisher.rb +82 -37
- data/lib/jason/publisher_old.rb +112 -0
- data/lib/jason/subscription.rb +349 -97
- data/lib/jason/subscription_old.rb +171 -0
- data/lib/jason/version.rb +1 -1
- metadata +80 -3
data/client/lib/JasonProvider.js
CHANGED
@@ -1,134 +1,15 @@
|
|
1
1
|
"use strict";
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
-
if (k2 === undefined) k2 = k;
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5
|
-
}) : (function(o, m, k, k2) {
|
6
|
-
if (k2 === undefined) k2 = k;
|
7
|
-
o[k2] = m[k];
|
8
|
-
}));
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
11
|
-
}) : function(o, v) {
|
12
|
-
o["default"] = v;
|
13
|
-
});
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
15
|
-
if (mod && mod.__esModule) return mod;
|
16
|
-
var result = {};
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
18
|
-
__setModuleDefault(result, mod);
|
19
|
-
return result;
|
20
|
-
};
|
21
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
22
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
23
4
|
};
|
24
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
25
|
-
const
|
26
|
-
const
|
27
|
-
const JasonContext_1 = __importDefault(require("./JasonContext"));
|
28
|
-
const axios_1 = __importDefault(require("axios"));
|
29
|
-
const axios_case_converter_1 = __importDefault(require("axios-case-converter"));
|
6
|
+
const react_1 = __importDefault(require("react"));
|
7
|
+
const useJason_1 = __importDefault(require("./useJason"));
|
30
8
|
const react_redux_1 = require("react-redux");
|
31
|
-
const
|
32
|
-
const createJasonReducers_1 = __importDefault(require("./createJasonReducers"));
|
33
|
-
const createPayloadHandler_1 = __importDefault(require("./createPayloadHandler"));
|
34
|
-
const createOptDis_1 = __importDefault(require("./createOptDis"));
|
35
|
-
const makeEager_1 = __importDefault(require("./makeEager"));
|
36
|
-
const humps_1 = require("humps");
|
37
|
-
const blueimp_md5_1 = __importDefault(require("blueimp-md5"));
|
38
|
-
const lodash_1 = __importDefault(require("lodash"));
|
39
|
-
const react_1 = __importStar(require("react"));
|
40
|
-
const uuid_1 = require("uuid");
|
9
|
+
const JasonContext_1 = __importDefault(require("./JasonContext"));
|
41
10
|
const JasonProvider = ({ reducers, middleware, extraActions, children }) => {
|
42
|
-
const [store,
|
43
|
-
|
44
|
-
const [connected, setConnected] = react_1.useState(false);
|
45
|
-
const csrfToken = document.querySelector("meta[name=csrf-token]").content;
|
46
|
-
axios_1.default.defaults.headers.common['X-CSRF-Token'] = csrfToken;
|
47
|
-
const restClient = axios_case_converter_1.default(axios_1.default.create(), {
|
48
|
-
preservedKeys: (key) => {
|
49
|
-
return uuid_1.validate(key);
|
50
|
-
}
|
51
|
-
});
|
52
|
-
react_1.useEffect(() => {
|
53
|
-
restClient.get('/jason/api/schema')
|
54
|
-
.then(({ data: snakey_schema }) => {
|
55
|
-
const schema = humps_1.camelizeKeys(snakey_schema);
|
56
|
-
const serverActionQueue = function () {
|
57
|
-
const queue = [];
|
58
|
-
let inFlight = false;
|
59
|
-
return {
|
60
|
-
addItem: (item) => queue.push(item),
|
61
|
-
getItem: () => {
|
62
|
-
if (inFlight)
|
63
|
-
return false;
|
64
|
-
const item = queue.shift();
|
65
|
-
if (item) {
|
66
|
-
inFlight = true;
|
67
|
-
return item;
|
68
|
-
}
|
69
|
-
return false;
|
70
|
-
},
|
71
|
-
itemProcessed: () => inFlight = false,
|
72
|
-
fullySynced: () => queue.length === 0 && !inFlight,
|
73
|
-
getData: () => ({ queue, inFlight })
|
74
|
-
};
|
75
|
-
}();
|
76
|
-
const consumer = actioncable_1.createConsumer();
|
77
|
-
const allReducers = Object.assign(Object.assign({}, reducers), createJasonReducers_1.default(schema));
|
78
|
-
console.log({ schema, allReducers });
|
79
|
-
const store = toolkit_1.configureStore({ reducer: allReducers, middleware });
|
80
|
-
let payloadHandlers = {};
|
81
|
-
function handlePayload(payload) {
|
82
|
-
const { model, md5Hash } = payload;
|
83
|
-
console.log({ md5Hash, fn: `${model}:${md5Hash}`, payloadHandlers, model: lodash_1.default.camelCase(model), payload });
|
84
|
-
const handler = payloadHandlers[`${lodash_1.default.camelCase(model)}:${md5Hash}`];
|
85
|
-
if (handler) {
|
86
|
-
handler(Object.assign(Object.assign({}, payload), { model: lodash_1.default.camelCase(model) }));
|
87
|
-
}
|
88
|
-
}
|
89
|
-
const subscription = (consumer.subscriptions.create({
|
90
|
-
channel: 'Jason::Channel'
|
91
|
-
}, {
|
92
|
-
connected: () => {
|
93
|
-
setConnected(true);
|
94
|
-
},
|
95
|
-
received: payload => {
|
96
|
-
console.log("Payload received", payload);
|
97
|
-
handlePayload(payload);
|
98
|
-
},
|
99
|
-
disconnected: () => console.warn('Disconnected from ActionCable')
|
100
|
-
}));
|
101
|
-
console.log('sending message');
|
102
|
-
subscription.send({ message: 'test' });
|
103
|
-
function createSubscription(config) {
|
104
|
-
const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
|
105
|
-
console.log('Subscribe with', config, md5Hash);
|
106
|
-
lodash_1.default.map(config, (v, model) => {
|
107
|
-
payloadHandlers[`${model}:${md5Hash}`] = createPayloadHandler_1.default(store.dispatch, serverActionQueue, subscription, model, schema[model]);
|
108
|
-
});
|
109
|
-
subscription.send({ createSubscription: config });
|
110
|
-
return () => removeSubscription(config);
|
111
|
-
}
|
112
|
-
function removeSubscription(config) {
|
113
|
-
subscription.send({ removeSubscription: config });
|
114
|
-
const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
|
115
|
-
lodash_1.default.map(config, (v, model) => {
|
116
|
-
delete payloadHandlers[`${model}:${md5Hash}`];
|
117
|
-
});
|
118
|
-
}
|
119
|
-
const optDis = createOptDis_1.default(schema, store.dispatch, restClient, serverActionQueue);
|
120
|
-
const actions = createActions_1.default(schema, store, restClient, optDis, extraActions);
|
121
|
-
const eager = makeEager_1.default(schema);
|
122
|
-
console.log({ actions });
|
123
|
-
setValue({
|
124
|
-
actions: actions,
|
125
|
-
subscribe: (config) => createSubscription(config),
|
126
|
-
eager
|
127
|
-
});
|
128
|
-
setStore(store);
|
129
|
-
});
|
130
|
-
}, []);
|
131
|
-
if (!(store && value && connected))
|
11
|
+
const [store, value] = useJason_1.default({ reducers, middleware, extraActions });
|
12
|
+
if (!(store && value))
|
132
13
|
return react_1.default.createElement("div", null); // Wait for async fetch of schema to complete
|
133
14
|
return react_1.default.createElement(react_redux_1.Provider, { store: store },
|
134
15
|
react_1.default.createElement(JasonContext_1.default.Provider, { value: value }, children));
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const toolkit_1 = require("@reduxjs/toolkit");
|
7
7
|
const pluralize_1 = __importDefault(require("pluralize"));
|
8
8
|
const lodash_1 = __importDefault(require("lodash"));
|
9
|
-
function generateSlices(
|
10
|
-
const sliceNames =
|
9
|
+
function generateSlices(models) {
|
10
|
+
const sliceNames = models.map(k => pluralize_1.default(k));
|
11
11
|
const adapter = toolkit_1.createEntityAdapter();
|
12
12
|
return lodash_1.default.fromPairs(lodash_1.default.map(sliceNames, name => {
|
13
13
|
return [name, toolkit_1.createSlice({
|
@@ -31,7 +31,52 @@ function generateSlices(schema) {
|
|
31
31
|
}).reducer];
|
32
32
|
}));
|
33
33
|
}
|
34
|
+
function generateJasonSlices(models) {
|
35
|
+
const initialState = lodash_1.default.fromPairs(lodash_1.default.map(models, (model_name) => {
|
36
|
+
return [model_name, {}];
|
37
|
+
}));
|
38
|
+
const modelSliceReducer = toolkit_1.createSlice({
|
39
|
+
name: 'jasonModels',
|
40
|
+
initialState,
|
41
|
+
reducers: {
|
42
|
+
setSubscriptionIds(s, a) {
|
43
|
+
const { payload } = a;
|
44
|
+
const { subscriptionId, model, ids } = payload;
|
45
|
+
console.log({ initialState });
|
46
|
+
s[model][subscriptionId] = ids;
|
47
|
+
},
|
48
|
+
addSubscriptionId(s, a) {
|
49
|
+
const { payload } = a;
|
50
|
+
const { subscriptionId, model, id } = payload;
|
51
|
+
s[model][subscriptionId] = lodash_1.default.union(s[model][subscriptionId] || [], [id]);
|
52
|
+
},
|
53
|
+
removeSubscriptionId(s, a) {
|
54
|
+
const { payload } = a;
|
55
|
+
const { subscriptionId, model, id } = payload;
|
56
|
+
s[model][subscriptionId] = lodash_1.default.remove(s[model][subscriptionId] || [], id);
|
57
|
+
},
|
58
|
+
removeSubscription(s, a) {
|
59
|
+
const { payload: { subscriptionId } } = a;
|
60
|
+
lodash_1.default.map(models, model => {
|
61
|
+
delete s[model][subscriptionId];
|
62
|
+
});
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}).reducer;
|
66
|
+
const jasonSliceReducer = toolkit_1.createSlice({
|
67
|
+
name: 'jason',
|
68
|
+
initialState: {
|
69
|
+
connected: false,
|
70
|
+
queueSize: 0
|
71
|
+
},
|
72
|
+
reducers: {
|
73
|
+
upsert: (s, a) => (Object.assign(Object.assign({}, s), a.payload))
|
74
|
+
}
|
75
|
+
}).reducer;
|
76
|
+
return { jason: jasonSliceReducer, jasonModels: modelSliceReducer };
|
77
|
+
}
|
34
78
|
function createJasonReducers(schema) {
|
35
|
-
|
79
|
+
const models = lodash_1.default.keys(schema);
|
80
|
+
return Object.assign(Object.assign({}, generateSlices(models)), generateJasonSlices(models));
|
36
81
|
}
|
37
82
|
exports.default = createJasonReducers;
|
data/client/lib/createOptDis.js
CHANGED
@@ -16,7 +16,6 @@ function enrich(type, payload) {
|
|
16
16
|
}
|
17
17
|
function createOptDis(schema, dispatch, restClient, serverActionQueue) {
|
18
18
|
const plurals = lodash_1.default.keys(schema).map(k => pluralize_1.default(k));
|
19
|
-
let inFlight = false;
|
20
19
|
function enqueueServerAction(action) {
|
21
20
|
serverActionQueue.addItem(action);
|
22
21
|
}
|
@@ -24,7 +23,6 @@ function createOptDis(schema, dispatch, restClient, serverActionQueue) {
|
|
24
23
|
const action = serverActionQueue.getItem();
|
25
24
|
if (!action)
|
26
25
|
return;
|
27
|
-
inFlight = true;
|
28
26
|
restClient.post('/jason/api/action', action)
|
29
27
|
.then(serverActionQueue.itemProcessed)
|
30
28
|
.catch(e => {
|
@@ -1 +1,9 @@
|
|
1
|
-
export default function createPayloadHandler(dispatch
|
1
|
+
export default function createPayloadHandler({ dispatch, serverActionQueue, transportAdapter, config }: {
|
2
|
+
dispatch: any;
|
3
|
+
serverActionQueue: any;
|
4
|
+
transportAdapter: any;
|
5
|
+
config: any;
|
6
|
+
}): {
|
7
|
+
handlePayload: (data: any) => void;
|
8
|
+
tearDown: () => void;
|
9
|
+
};
|
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
const jsonpatch_1 = require("jsonpatch");
|
7
6
|
const deepCamelizeKeys_1 = __importDefault(require("./deepCamelizeKeys"));
|
8
7
|
const pluralize_1 = __importDefault(require("pluralize"));
|
9
8
|
const lodash_1 = __importDefault(require("lodash"));
|
@@ -12,93 +11,86 @@ function diffSeconds(dt2, dt1) {
|
|
12
11
|
var diff = (dt2.getTime() - dt1.getTime()) / 1000;
|
13
12
|
return Math.abs(Math.round(diff));
|
14
13
|
}
|
15
|
-
function createPayloadHandler(dispatch, serverActionQueue,
|
16
|
-
|
17
|
-
let
|
18
|
-
let previousPayload = [];
|
19
|
-
let idx = 0;
|
14
|
+
function createPayloadHandler({ dispatch, serverActionQueue, transportAdapter, config }) {
|
15
|
+
const subscriptionId = uuid_1.v4();
|
16
|
+
let idx = {};
|
20
17
|
let patchQueue = {};
|
21
18
|
let lastCheckAt = new Date();
|
22
19
|
let updateDeadline = null;
|
23
20
|
let checkInterval;
|
24
21
|
function getPayload() {
|
25
|
-
|
26
|
-
subscription.send({ getPayload: { model, config } });
|
22
|
+
setTimeout(() => transportAdapter.getPayload(config), 1000);
|
27
23
|
}
|
28
24
|
function camelizeKeys(item) {
|
29
25
|
return deepCamelizeKeys_1.default(item, key => uuid_1.validate(key));
|
30
26
|
}
|
31
27
|
const tGetPayload = lodash_1.default.throttle(getPayload, 10000);
|
32
|
-
function
|
33
|
-
|
34
|
-
if (!serverActionQueue.fullySynced()) {
|
35
|
-
console.log(serverActionQueue.getData());
|
36
|
-
setTimeout(dispatchPayload, 100);
|
37
|
-
return;
|
38
|
-
}
|
39
|
-
const includeModels = (config.includeModels || []).map(m => lodash_1.default.camelCase(m));
|
40
|
-
console.log("Dispatching", { payload, includeModels });
|
41
|
-
includeModels.forEach(m => {
|
42
|
-
const subPayload = lodash_1.default.flatten(lodash_1.default.compact(camelizeKeys(payload).map(instance => instance[m])));
|
43
|
-
const previousSubPayload = lodash_1.default.flatten(lodash_1.default.compact(camelizeKeys(previousPayload).map(instance => instance[m])));
|
44
|
-
// Find IDs that were in the payload but are no longer
|
45
|
-
const idsToRemove = lodash_1.default.difference(previousSubPayload.map(i => i.id), subPayload.map(i => i.id));
|
46
|
-
dispatch({ type: `${pluralize_1.default(m)}/upsertMany`, payload: subPayload });
|
47
|
-
dispatch({ type: `${pluralize_1.default(m)}/removeMany`, payload: idsToRemove });
|
48
|
-
});
|
49
|
-
const idsToRemove = lodash_1.default.difference(previousPayload.map(i => i.id), payload.map(i => i.id));
|
50
|
-
dispatch({ type: `${pluralize_1.default(model)}/upsertMany`, payload: camelizeKeys(payload) });
|
51
|
-
dispatch({ type: `${pluralize_1.default(model)}/removeMany`, payload: idsToRemove });
|
52
|
-
previousPayload = payload;
|
53
|
-
}
|
54
|
-
function processQueue() {
|
55
|
-
console.log({ idx, patchQueue });
|
28
|
+
function processQueue(model) {
|
29
|
+
console.debug("processQueue", model, idx[model], patchQueue[model]);
|
56
30
|
lastCheckAt = new Date();
|
57
|
-
if (patchQueue[idx]) {
|
58
|
-
|
59
|
-
|
60
|
-
|
31
|
+
if (patchQueue[model][idx[model]]) {
|
32
|
+
if (!serverActionQueue.fullySynced()) {
|
33
|
+
console.debug(serverActionQueue.getData());
|
34
|
+
setTimeout(() => processQueue(model), 100);
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
const { payload, destroy, id, type } = patchQueue[model][idx[model]];
|
38
|
+
if (type === 'payload') {
|
39
|
+
dispatch({ type: `${pluralize_1.default(model)}/upsertMany`, payload });
|
40
|
+
const ids = payload.map(instance => instance.id);
|
41
|
+
dispatch({ type: `jasonModels/setSubscriptionIds`, payload: { model, subscriptionId, ids } });
|
61
42
|
}
|
62
|
-
|
63
|
-
|
43
|
+
else if (destroy) {
|
44
|
+
// Middleware will determine if this model should be removed if it isn't in any other subscriptions
|
45
|
+
dispatch({ type: `jasonModels/removeSubscriptionId`, payload: { model, subscriptionId, id } });
|
46
|
+
}
|
47
|
+
else {
|
48
|
+
dispatch({ type: `${pluralize_1.default(model)}/upsert`, payload });
|
49
|
+
dispatch({ type: `jasonModels/addSubscriptionId`, payload: { model, subscriptionId, id } });
|
50
|
+
}
|
51
|
+
delete patchQueue[model][idx[model]];
|
52
|
+
idx[model]++;
|
64
53
|
updateDeadline = null;
|
65
|
-
processQueue();
|
54
|
+
processQueue(model);
|
66
55
|
// If there are updates in the queue that are ahead of the index, some have arrived out of order
|
67
56
|
// Set a deadline for new updates before it declares the update missing and refetches.
|
68
57
|
}
|
69
|
-
else if (lodash_1.default.keys(patchQueue).length > 0 && !updateDeadline) {
|
58
|
+
else if (lodash_1.default.keys(patchQueue[model]).length > 0 && !updateDeadline) {
|
70
59
|
var t = new Date();
|
71
60
|
t.setSeconds(t.getSeconds() + 3);
|
72
61
|
updateDeadline = t;
|
73
|
-
setTimeout(processQueue, 3100);
|
62
|
+
setTimeout(() => processQueue(model), 3100);
|
74
63
|
// If more than 10 updates in queue, or deadline has passed, restart
|
75
64
|
}
|
76
|
-
else if (lodash_1.default.keys(patchQueue).length > 10 || (updateDeadline && diffSeconds(updateDeadline, new Date()) < 0)) {
|
65
|
+
else if (lodash_1.default.keys(patchQueue[model]).length > 10 || (updateDeadline && diffSeconds(updateDeadline, new Date()) < 0)) {
|
77
66
|
tGetPayload();
|
78
67
|
updateDeadline = null;
|
79
68
|
}
|
80
69
|
}
|
81
70
|
function handlePayload(data) {
|
82
|
-
const {
|
83
|
-
|
71
|
+
const { idx: newIdx, model: snake_model, type } = data;
|
72
|
+
const model = lodash_1.default.camelCase(snake_model);
|
73
|
+
idx[model] = idx[model] || 0;
|
74
|
+
patchQueue[model] = patchQueue[model] || {};
|
84
75
|
if (type === 'payload') {
|
85
|
-
|
86
|
-
return null;
|
87
|
-
payload = value;
|
88
|
-
dispatchPayload();
|
89
|
-
idx = newIdx + 1;
|
76
|
+
idx[model] = newIdx;
|
90
77
|
// Clear any old changes left in the queue
|
91
|
-
patchQueue = lodash_1.default.pick(patchQueue, lodash_1.default.keys(patchQueue).filter(k => k > newIdx + 1));
|
92
|
-
return;
|
78
|
+
patchQueue[model] = lodash_1.default.pick(patchQueue[model], lodash_1.default.keys(patchQueue[model]).filter(k => k > newIdx + 1));
|
93
79
|
}
|
94
|
-
patchQueue[newIdx] =
|
95
|
-
|
80
|
+
patchQueue[model][newIdx] = camelizeKeys(Object.assign(Object.assign({}, data), { model }));
|
81
|
+
console.debug("Added to queue", model, idx[model], camelizeKeys(Object.assign(Object.assign({}, data), { model })), serverActionQueue.getData());
|
82
|
+
processQueue(model);
|
96
83
|
if (diffSeconds((new Date()), lastCheckAt) >= 3) {
|
97
84
|
lastCheckAt = new Date();
|
98
|
-
console.
|
85
|
+
console.debug('Interval lost. Pulling from server');
|
99
86
|
tGetPayload();
|
100
87
|
}
|
101
88
|
}
|
102
|
-
|
89
|
+
tGetPayload();
|
90
|
+
// Clean up after ourselves
|
91
|
+
function tearDown() {
|
92
|
+
dispatch({ type: `jasonModels/removeSubscription`, payload: { subscriptionId } });
|
93
|
+
}
|
94
|
+
return { handlePayload, tearDown };
|
103
95
|
}
|
104
96
|
exports.default = createPayloadHandler;
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"use strict";
|
2
|
+
// A FIFO queue with deduping of actions whose effect will be cancelled by later actions
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
5
|
+
};
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
7
|
+
const lodash_1 = __importDefault(require("lodash"));
|
8
|
+
function createServerActionQueue() {
|
9
|
+
const queue = [];
|
10
|
+
let inFlight = false;
|
11
|
+
function addItem(item) {
|
12
|
+
// Check if there are any items ahead in the queue that this item would effectively overwrite.
|
13
|
+
// In that case we can remove them
|
14
|
+
// If this is an upsert && item ID is the same && current item attributes are a superset of the earlier item attributes
|
15
|
+
const { type, payload } = item;
|
16
|
+
if (type.split('/')[1] !== 'upsert') {
|
17
|
+
queue.push(item);
|
18
|
+
return;
|
19
|
+
}
|
20
|
+
lodash_1.default.remove(queue, item => {
|
21
|
+
const { type: itemType, payload: itemPayload } = item;
|
22
|
+
if (type !== itemType)
|
23
|
+
return false;
|
24
|
+
if (itemPayload.id !== payload.id)
|
25
|
+
return false;
|
26
|
+
// Check that all keys of itemPayload are in payload.
|
27
|
+
return lodash_1.default.difference(lodash_1.default.keys(itemPayload), lodash_1.default.keys(payload)).length === 0;
|
28
|
+
});
|
29
|
+
queue.push(item);
|
30
|
+
}
|
31
|
+
return {
|
32
|
+
addItem,
|
33
|
+
getItem: () => {
|
34
|
+
if (inFlight)
|
35
|
+
return false;
|
36
|
+
const item = queue.shift();
|
37
|
+
if (item) {
|
38
|
+
inFlight = true;
|
39
|
+
return item;
|
40
|
+
}
|
41
|
+
return false;
|
42
|
+
},
|
43
|
+
itemProcessed: () => inFlight = false,
|
44
|
+
fullySynced: () => queue.length === 0 && !inFlight,
|
45
|
+
getData: () => ({ queue, inFlight })
|
46
|
+
};
|
47
|
+
}
|
48
|
+
exports.default = createServerActionQueue;
|