jason-rails 0.3.0 → 0.6.0

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.ruby-version +1 -0
  4. data/Gemfile.lock +184 -0
  5. data/README.md +118 -10
  6. data/app/controllers/jason/api/pusher_controller.rb +15 -0
  7. data/app/controllers/jason/api_controller.rb +78 -0
  8. data/client/babel.config.js +13 -0
  9. data/client/lib/JasonContext.d.ts +6 -1
  10. data/client/lib/JasonProvider.d.ts +6 -5
  11. data/client/lib/JasonProvider.js +5 -97
  12. data/client/lib/actionFactory.js +1 -1
  13. data/client/lib/createActions.d.ts +1 -1
  14. data/client/lib/createActions.js +2 -27
  15. data/client/lib/createJasonReducers.js +49 -3
  16. data/client/lib/createOptDis.d.ts +1 -0
  17. data/client/lib/createOptDis.js +43 -0
  18. data/client/lib/createPayloadHandler.d.ts +9 -1
  19. data/client/lib/createPayloadHandler.js +52 -43
  20. data/client/lib/createServerActionQueue.d.ts +10 -0
  21. data/client/lib/createServerActionQueue.js +48 -0
  22. data/client/lib/createServerActionQueue.test.d.ts +1 -0
  23. data/client/lib/createServerActionQueue.test.js +37 -0
  24. data/client/lib/createTransportAdapter.d.ts +5 -0
  25. data/client/lib/createTransportAdapter.js +20 -0
  26. data/client/lib/deepCamelizeKeys.d.ts +1 -0
  27. data/client/lib/deepCamelizeKeys.js +23 -0
  28. data/client/lib/deepCamelizeKeys.test.d.ts +1 -0
  29. data/client/lib/deepCamelizeKeys.test.js +106 -0
  30. data/client/lib/index.d.ts +6 -5
  31. data/client/lib/pruneIdsMiddleware.d.ts +2 -0
  32. data/client/lib/pruneIdsMiddleware.js +24 -0
  33. data/client/lib/restClient.d.ts +2 -0
  34. data/client/lib/restClient.js +17 -0
  35. data/client/lib/transportAdapters/actionCableAdapter.d.ts +5 -0
  36. data/client/lib/transportAdapters/actionCableAdapter.js +35 -0
  37. data/client/lib/transportAdapters/pusherAdapter.d.ts +5 -0
  38. data/client/lib/transportAdapters/pusherAdapter.js +68 -0
  39. data/client/lib/useJason.d.ts +5 -0
  40. data/client/lib/useJason.js +94 -0
  41. data/client/lib/useJason.test.d.ts +1 -0
  42. data/client/lib/useJason.test.js +85 -0
  43. data/client/lib/useSub.d.ts +1 -1
  44. data/client/lib/useSub.js +6 -3
  45. data/client/package.json +19 -4
  46. data/client/src/JasonProvider.tsx +6 -96
  47. data/client/src/actionFactory.ts +1 -1
  48. data/client/src/createActions.ts +2 -33
  49. data/client/src/createJasonReducers.ts +57 -3
  50. data/client/src/createOptDis.ts +45 -0
  51. data/client/src/createPayloadHandler.ts +58 -47
  52. data/client/src/createServerActionQueue.test.ts +42 -0
  53. data/client/src/createServerActionQueue.ts +47 -0
  54. data/client/src/createTransportAdapter.ts +13 -0
  55. data/client/src/deepCamelizeKeys.test.ts +113 -0
  56. data/client/src/deepCamelizeKeys.ts +17 -0
  57. data/client/src/pruneIdsMiddleware.ts +24 -0
  58. data/client/src/restClient.ts +14 -0
  59. data/client/src/transportAdapters/actionCableAdapter.ts +38 -0
  60. data/client/src/transportAdapters/pusherAdapter.ts +72 -0
  61. data/client/src/useJason.test.ts +87 -0
  62. data/client/src/useJason.ts +110 -0
  63. data/client/src/useSub.ts +6 -3
  64. data/client/yarn.lock +4607 -81
  65. data/config/routes.rb +8 -0
  66. data/jason-rails.gemspec +9 -0
  67. data/lib/jason.rb +40 -1
  68. data/lib/jason/api_model.rb +15 -9
  69. data/lib/jason/broadcaster.rb +19 -0
  70. data/lib/jason/channel.rb +50 -21
  71. data/lib/jason/engine.rb +5 -0
  72. data/lib/jason/graph_helper.rb +165 -0
  73. data/lib/jason/includes_helper.rb +108 -0
  74. data/lib/jason/lua_generator.rb +71 -0
  75. data/lib/jason/publisher.rb +103 -30
  76. data/lib/jason/publisher_old.rb +112 -0
  77. data/lib/jason/subscription.rb +352 -101
  78. data/lib/jason/subscription_old.rb +171 -0
  79. data/lib/jason/version.rb +1 -1
  80. metadata +151 -4
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ presets: [
3
+ [
4
+ '@babel/preset-env',
5
+ {
6
+ targets: {
7
+ node: 'current',
8
+ },
9
+ },
10
+ ],
11
+ '@babel/preset-typescript'
12
+ ],
13
+ };
@@ -1,2 +1,7 @@
1
- declare const context: any;
1
+ /// <reference types="react" />
2
+ declare const context: import("react").Context<{
3
+ actions: any;
4
+ subscribe: null;
5
+ eager: null;
6
+ }>;
2
7
  export default context;
@@ -1,7 +1,8 @@
1
+ import React from 'react';
1
2
  declare const JasonProvider: ({ reducers, middleware, extraActions, children }: {
2
- reducers: any;
3
- middleware: any;
4
- extraActions: any;
5
- children: any;
6
- }) => any;
3
+ reducers?: any;
4
+ middleware?: any;
5
+ extraActions?: any;
6
+ children?: React.FC<{}> | undefined;
7
+ }) => JSX.Element;
7
8
  export default JasonProvider;
@@ -1,107 +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 createActions_1 = __importDefault(require("./createActions"));
26
- const actioncable_1 = require("@rails/actioncable");
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 toolkit_1 = require("@reduxjs/toolkit");
32
- const createJasonReducers_1 = __importDefault(require("./createJasonReducers"));
33
- const createPayloadHandler_1 = __importDefault(require("./createPayloadHandler"));
34
- const makeEager_1 = __importDefault(require("./makeEager"));
35
- const humps_1 = require("humps");
36
- const blueimp_md5_1 = __importDefault(require("blueimp-md5"));
37
- const lodash_1 = __importDefault(require("lodash"));
38
- const react_1 = __importStar(require("react"));
9
+ const JasonContext_1 = __importDefault(require("./JasonContext"));
39
10
  const JasonProvider = ({ reducers, middleware, extraActions, children }) => {
40
- const [store, setStore] = react_1.useState(null);
41
- const [value, setValue] = react_1.useState(null);
42
- const [connected, setConnected] = react_1.useState(false);
43
- const csrfToken = document.querySelector("meta[name=csrf-token]").content;
44
- axios_1.default.defaults.headers.common['X-CSRF-Token'] = csrfToken;
45
- const restClient = axios_case_converter_1.default(axios_1.default.create());
46
- react_1.useEffect(() => {
47
- restClient.get('/jason/api/schema')
48
- .then(({ data: snakey_schema }) => {
49
- const schema = humps_1.camelizeKeys(snakey_schema);
50
- const consumer = actioncable_1.createConsumer();
51
- const allReducers = Object.assign(Object.assign({}, reducers), createJasonReducers_1.default(schema));
52
- console.log({ schema, allReducers });
53
- const store = toolkit_1.configureStore({ reducer: allReducers, middleware });
54
- let payloadHandlers = {};
55
- function handlePayload(payload) {
56
- const { model, md5Hash } = payload;
57
- console.log({ md5Hash, fn: `${model}:${md5Hash}`, payloadHandlers, model: lodash_1.default.camelCase(model), payload });
58
- const handler = payloadHandlers[`${lodash_1.default.camelCase(model)}:${md5Hash}`];
59
- if (handler) {
60
- handler(Object.assign(Object.assign({}, payload), { model: lodash_1.default.camelCase(model) }));
61
- }
62
- }
63
- const subscription = (consumer.subscriptions.create({
64
- channel: 'Jason::Channel'
65
- }, {
66
- connected: () => {
67
- setConnected(true);
68
- },
69
- received: payload => {
70
- console.log("Payload received", payload);
71
- handlePayload(payload);
72
- },
73
- disconnected: () => console.warn('Disconnected from ActionCable')
74
- }));
75
- console.log('sending message');
76
- subscription.send({ message: 'test' });
77
- function createSubscription(config) {
78
- const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
79
- console.log('Subscribe with', config, md5Hash);
80
- lodash_1.default.map(config, (v, model) => {
81
- payloadHandlers[`${model}:${md5Hash}`] = createPayloadHandler_1.default(store.dispatch, subscription, model, schema[model]);
82
- });
83
- subscription.send({ createSubscription: config });
84
- return () => removeSubscription(config);
85
- }
86
- function removeSubscription(config) {
87
- subscription.send({ removeSubscription: config });
88
- const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
89
- lodash_1.default.map(config, (v, model) => {
90
- delete payloadHandlers[`${model}:${md5Hash}`];
91
- });
92
- }
93
- const actions = createActions_1.default(schema, store, restClient, extraActions);
94
- const eager = makeEager_1.default(schema);
95
- console.log({ actions });
96
- setValue({
97
- actions: actions,
98
- subscribe: (config) => createSubscription(config),
99
- eager
100
- });
101
- setStore(store);
102
- });
103
- }, []);
104
- if (!(store && value && connected))
11
+ const [store, value] = useJason_1.default({ reducers, middleware, extraActions });
12
+ if (!(store && value))
105
13
  return react_1.default.createElement("div", null); // Wait for async fetch of schema to complete
106
14
  return react_1.default.createElement(react_redux_1.Provider, { store: store },
107
15
  react_1.default.createElement(JasonContext_1.default.Provider, { value: value }, children));
@@ -23,7 +23,7 @@ exports.default = (dis, store, entity, { extraActions = {}, hasPriority = false
23
23
  function remove(id) {
24
24
  return dis({ type: `${pluralize_1.default(entity)}/remove`, payload: id });
25
25
  }
26
- const extraActionsResolved = lodash_1.default.mapValues(extraActions, v => v(dis, store, entity));
26
+ const extraActionsResolved = extraActions ? lodash_1.default.mapValues(extraActions, v => v(dis, store, entity)) : {};
27
27
  if (hasPriority) {
28
28
  return Object.assign({ add, upsert, setAll, remove, movePriority }, extraActionsResolved);
29
29
  }
@@ -1,2 +1,2 @@
1
- declare function createActions(schema: any, store: any, restClient: any, extraActions: any): any;
1
+ declare function createActions(schema: any, store: any, restClient: any, optDis: any, extraActions: any): any;
2
2
  export default createActions;
@@ -6,32 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const actionFactory_1 = __importDefault(require("./actionFactory"));
7
7
  const pluralize_1 = __importDefault(require("pluralize"));
8
8
  const lodash_1 = __importDefault(require("lodash"));
9
- const uuid_1 = require("uuid");
10
- function enrich(type, payload) {
11
- if (type.split('/')[1] === 'upsert' && !(type.split('/')[0] === 'session')) {
12
- if (!payload.id) {
13
- return Object.assign(Object.assign({}, payload), { id: uuid_1.v4() });
14
- }
15
- }
16
- return payload;
17
- }
18
- function makeOptDis(schema, dispatch, restClient) {
19
- const plurals = lodash_1.default.keys(schema).map(k => pluralize_1.default(k));
20
- return function (action) {
21
- const { type, payload } = action;
22
- const data = enrich(type, payload);
23
- dispatch(action);
24
- if (plurals.indexOf(type.split('/')[0]) > -1) {
25
- return restClient.post('/jason/api/action', { type, payload: data })
26
- .catch(e => {
27
- dispatch({ type: 'upsertLocalUi', data: { error: JSON.stringify(e) } });
28
- });
29
- }
30
- };
31
- }
32
- function createActions(schema, store, restClient, extraActions) {
33
- const dis = store.dispatch;
34
- const optDis = makeOptDis(schema, dis, restClient);
9
+ function createActions(schema, store, restClient, optDis, extraActions) {
35
10
  const actions = lodash_1.default.fromPairs(lodash_1.default.map(schema, (config, model) => {
36
11
  if (config.priorityScope) {
37
12
  return [pluralize_1.default(model), actionFactory_1.default(optDis, store, model, { hasPriority: true })];
@@ -40,7 +15,7 @@ function createActions(schema, store, restClient, extraActions) {
40
15
  return [pluralize_1.default(model), actionFactory_1.default(optDis, store, model)];
41
16
  }
42
17
  }));
43
- const extraActionsResolved = extraActions(optDis, store, restClient);
18
+ const extraActionsResolved = extraActions ? extraActions(optDis, store, restClient, actions) : {};
44
19
  return lodash_1.default.merge(actions, extraActionsResolved);
45
20
  }
46
21
  exports.default = createActions;
@@ -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(schema) {
10
- const sliceNames = schema.map(k => pluralize_1.default(k));
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({
@@ -19,6 +19,7 @@ function generateSlices(schema) {
19
19
  add: adapter.addOne,
20
20
  setAll: adapter.setAll,
21
21
  remove: adapter.removeOne,
22
+ removeMany: adapter.removeMany,
22
23
  movePriority: (s, { payload: { id, priority, parentFilter } }) => {
23
24
  // Get IDs and insert our item at the new index
24
25
  var affectedIds = lodash_1.default.orderBy(lodash_1.default.filter(lodash_1.default.values(s.entities), parentFilter).filter(e => e.id !== id), 'priority').map(e => e.id);
@@ -30,7 +31,52 @@ function generateSlices(schema) {
30
31
  }).reducer];
31
32
  }));
32
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
+ }
33
78
  function createJasonReducers(schema) {
34
- return generateSlices(lodash_1.default.keys(schema));
79
+ const models = lodash_1.default.keys(schema);
80
+ return Object.assign(Object.assign({}, generateSlices(models)), generateJasonSlices(models));
35
81
  }
36
82
  exports.default = createJasonReducers;
@@ -0,0 +1 @@
1
+ export default function createOptDis(schema: any, dispatch: any, restClient: any, serverActionQueue: any): (action: any) => void;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const lodash_1 = __importDefault(require("lodash"));
7
+ const pluralize_1 = __importDefault(require("pluralize"));
8
+ const uuid_1 = require("uuid");
9
+ function enrich(type, payload) {
10
+ if (type.split('/')[1] === 'upsert' && !(type.split('/')[0] === 'session')) {
11
+ if (!payload.id) {
12
+ return Object.assign(Object.assign({}, payload), { id: uuid_1.v4() });
13
+ }
14
+ }
15
+ return payload;
16
+ }
17
+ function createOptDis(schema, dispatch, restClient, serverActionQueue) {
18
+ const plurals = lodash_1.default.keys(schema).map(k => pluralize_1.default(k));
19
+ function enqueueServerAction(action) {
20
+ serverActionQueue.addItem(action);
21
+ }
22
+ function dispatchServerAction() {
23
+ const action = serverActionQueue.getItem();
24
+ if (!action)
25
+ return;
26
+ restClient.post('/jason/api/action', action)
27
+ .then(serverActionQueue.itemProcessed)
28
+ .catch(e => {
29
+ dispatch({ type: 'upsertLocalUi', data: { error: JSON.stringify(e) } });
30
+ serverActionQueue.itemProcessed();
31
+ });
32
+ }
33
+ setInterval(dispatchServerAction, 10);
34
+ return function (action) {
35
+ const { type, payload } = action;
36
+ const data = enrich(type, payload);
37
+ dispatch({ type, payload: data });
38
+ if (plurals.indexOf(type.split('/')[0]) > -1) {
39
+ enqueueServerAction({ type, payload: data });
40
+ }
41
+ };
42
+ }
43
+ exports.default = createOptDis;
@@ -1 +1,9 @@
1
- export default function createPayloadHandler(dispatch: any, subscription: any, model: any, config: any): (data: any) => null | undefined;
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,85 +3,94 @@ 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
- const humps_1 = require("humps");
6
+ const deepCamelizeKeys_1 = __importDefault(require("./deepCamelizeKeys"));
8
7
  const pluralize_1 = __importDefault(require("pluralize"));
9
8
  const lodash_1 = __importDefault(require("lodash"));
9
+ const uuid_1 = require("uuid");
10
10
  function diffSeconds(dt2, dt1) {
11
11
  var diff = (dt2.getTime() - dt1.getTime()) / 1000;
12
12
  return Math.abs(Math.round(diff));
13
13
  }
14
- function createPayloadHandler(dispatch, subscription, model, config) {
15
- console.log({ model, config });
16
- let payload = {};
17
- let idx = 0;
14
+ function createPayloadHandler({ dispatch, serverActionQueue, transportAdapter, config }) {
15
+ const subscriptionId = uuid_1.v4();
16
+ let idx = {};
18
17
  let patchQueue = {};
19
18
  let lastCheckAt = new Date();
20
19
  let updateDeadline = null;
21
20
  let checkInterval;
22
21
  function getPayload() {
23
- console.log({ getPayload: model, subscription });
24
- subscription.send({ getPayload: { model, config } });
22
+ setTimeout(() => transportAdapter.getPayload(config), 1000);
25
23
  }
26
- const tGetPayload = lodash_1.default.throttle(getPayload, 10000);
27
- function dispatchPayload() {
28
- const includeModels = (config.includeModels || []).map(m => lodash_1.default.camelCase(m));
29
- console.log("Dispatching", { payload, includeModels });
30
- includeModels.forEach(m => {
31
- const subPayload = lodash_1.default.flatten(lodash_1.default.compact(humps_1.camelizeKeys(payload).map(instance => instance[m])));
32
- console.log({ type: `${pluralize_1.default(m)}/upsertMany`, payload: subPayload });
33
- dispatch({ type: `${pluralize_1.default(m)}/upsertMany`, payload: subPayload });
34
- });
35
- dispatch({ type: `${pluralize_1.default(model)}/upsertMany`, payload: humps_1.camelizeKeys(payload) });
24
+ function camelizeKeys(item) {
25
+ return deepCamelizeKeys_1.default(item, key => uuid_1.validate(key));
36
26
  }
37
- function processQueue() {
38
- console.log({ idx, patchQueue });
27
+ const tGetPayload = lodash_1.default.throttle(getPayload, 10000);
28
+ function processQueue(model) {
29
+ console.debug("processQueue", model, idx[model], patchQueue[model]);
39
30
  lastCheckAt = new Date();
40
- if (patchQueue[idx]) {
41
- payload = jsonpatch_1.apply_patch(payload, patchQueue[idx]);
42
- if (patchQueue[idx]) {
43
- dispatchPayload();
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 } });
44
42
  }
45
- delete patchQueue[idx];
46
- idx++;
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]++;
47
53
  updateDeadline = null;
48
- processQueue();
54
+ processQueue(model);
49
55
  // If there are updates in the queue that are ahead of the index, some have arrived out of order
50
56
  // Set a deadline for new updates before it declares the update missing and refetches.
51
57
  }
52
- else if (lodash_1.default.keys(patchQueue).length > 0 && !updateDeadline) {
58
+ else if (lodash_1.default.keys(patchQueue[model]).length > 0 && !updateDeadline) {
53
59
  var t = new Date();
54
60
  t.setSeconds(t.getSeconds() + 3);
55
61
  updateDeadline = t;
56
- setTimeout(processQueue, 3100);
62
+ setTimeout(() => processQueue(model), 3100);
57
63
  // If more than 10 updates in queue, or deadline has passed, restart
58
64
  }
59
- 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)) {
60
66
  tGetPayload();
61
67
  updateDeadline = null;
62
68
  }
63
69
  }
64
70
  function handlePayload(data) {
65
- const { value, idx: newIdx, diff, latency, type } = data;
66
- console.log({ data });
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] || {};
67
75
  if (type === 'payload') {
68
- if (!value)
69
- return null;
70
- payload = value;
71
- dispatchPayload();
72
- idx = newIdx + 1;
76
+ idx[model] = newIdx;
73
77
  // Clear any old changes left in the queue
74
- patchQueue = lodash_1.default.pick(patchQueue, lodash_1.default.keys(patchQueue).filter(k => k > newIdx + 1));
75
- return;
78
+ patchQueue[model] = lodash_1.default.pick(patchQueue[model], lodash_1.default.keys(patchQueue[model]).filter(k => k > newIdx + 1));
76
79
  }
77
- patchQueue[newIdx] = diff;
78
- processQueue();
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);
79
83
  if (diffSeconds((new Date()), lastCheckAt) >= 3) {
80
84
  lastCheckAt = new Date();
81
- console.log('Interval lost. Pulling from server');
85
+ console.debug('Interval lost. Pulling from server');
82
86
  tGetPayload();
83
87
  }
84
88
  }
85
- return handlePayload;
89
+ tGetPayload();
90
+ // Clean up after ourselves
91
+ function tearDown() {
92
+ dispatch({ type: `jasonModels/removeSubscription`, payload: { subscriptionId } });
93
+ }
94
+ return { handlePayload, tearDown };
86
95
  }
87
96
  exports.default = createPayloadHandler;