jason-rails 0.3.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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;