jason-rails 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +34 -0
- data/README.md +6 -9
- data/app/assets/config/jason_engine_manifest.js +1 -0
- data/app/assets/images/jason/engine/.keep +0 -0
- data/app/assets/stylesheets/jason/engine/application.css +15 -0
- data/app/controllers/jason/api_controller.rb +36 -0
- data/app/helpers/jason/engine/application_helper.rb +6 -0
- data/app/jobs/jason/engine/application_job.rb +6 -0
- data/app/mailers/jason/engine/application_mailer.rb +8 -0
- data/app/models/jason/engine/application_record.rb +7 -0
- data/app/views/layouts/jason/engine/application.html.erb +15 -0
- data/client/babel.config.js +13 -0
- data/client/lib/JasonProvider.d.ts +5 -4
- data/client/lib/JasonProvider.js +30 -3
- data/client/lib/actionFactory.js +1 -1
- data/client/lib/createActions.d.ts +1 -1
- data/client/lib/createActions.js +2 -27
- data/client/lib/createJasonReducers.js +1 -0
- data/client/lib/createOptDis.d.ts +1 -0
- data/client/lib/createOptDis.js +45 -0
- data/client/lib/createPayloadHandler.d.ts +1 -1
- data/client/lib/createPayloadHandler.js +23 -6
- data/client/lib/deepCamelizeKeys.d.ts +1 -0
- data/client/lib/deepCamelizeKeys.js +23 -0
- data/client/lib/deepCamelizeKeys.test.d.ts +1 -0
- data/client/lib/deepCamelizeKeys.test.js +106 -0
- data/client/lib/index.d.ts +4 -4
- data/client/package.json +17 -4
- data/client/src/JasonProvider.tsx +33 -5
- data/client/src/actionFactory.ts +1 -1
- data/client/src/createActions.ts +2 -33
- data/client/src/createJasonReducers.ts +1 -0
- data/client/src/createOptDis.ts +47 -0
- data/client/src/createPayloadHandler.ts +26 -4
- data/client/src/deepCamelizeKeys.test.ts +113 -0
- data/client/src/deepCamelizeKeys.ts +17 -0
- data/client/yarn.lock +4539 -81
- data/config/routes.rb +4 -0
- data/jason-rails.gemspec +5 -0
- data/lib/jason.rb +7 -1
- data/lib/jason/api_model.rb +16 -0
- data/lib/jason/channel.rb +2 -2
- data/lib/jason/engine.rb +5 -0
- data/lib/jason/publisher.rb +38 -6
- data/lib/jason/subscription.rb +21 -24
- data/lib/jason/version.rb +1 -1
- metadata +81 -3
@@ -0,0 +1 @@
|
|
1
|
+
export default function deepCamelizeKeys(item: any, excludeIf?: (k: any) => boolean): any;
|
@@ -0,0 +1,23 @@
|
|
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
|
+
function deepCamelizeKeys(item, excludeIf = k => false) {
|
8
|
+
function camelizeKey(key) {
|
9
|
+
if (excludeIf(key))
|
10
|
+
return key;
|
11
|
+
return lodash_1.default.camelCase(key);
|
12
|
+
}
|
13
|
+
if (lodash_1.default.isArray(item)) {
|
14
|
+
return lodash_1.default.map(item, item => deepCamelizeKeys(item, excludeIf));
|
15
|
+
}
|
16
|
+
else if (lodash_1.default.isObject(item)) {
|
17
|
+
return lodash_1.default.mapValues(lodash_1.default.mapKeys(item, (v, k) => camelizeKey(k)), (v, k) => deepCamelizeKeys(v, excludeIf));
|
18
|
+
}
|
19
|
+
else {
|
20
|
+
return item;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
exports.default = deepCamelizeKeys;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,106 @@
|
|
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 deepCamelizeKeys_1 = __importDefault(require("./deepCamelizeKeys"));
|
7
|
+
test('scalar number', () => {
|
8
|
+
expect(deepCamelizeKeys_1.default(1)).toBe(1);
|
9
|
+
});
|
10
|
+
test('scalar number float', () => {
|
11
|
+
expect(deepCamelizeKeys_1.default(1.123)).toBe(1.123);
|
12
|
+
});
|
13
|
+
test('scalar string', () => {
|
14
|
+
expect(deepCamelizeKeys_1.default('test')).toBe('test');
|
15
|
+
});
|
16
|
+
test('scalar null', () => {
|
17
|
+
expect(deepCamelizeKeys_1.default(null)).toBe(null);
|
18
|
+
});
|
19
|
+
test('scalar boolean', () => {
|
20
|
+
expect(deepCamelizeKeys_1.default(true)).toBe(true);
|
21
|
+
});
|
22
|
+
test('object with existing camelized keys', () => {
|
23
|
+
expect(deepCamelizeKeys_1.default({ testMe: 'test' })).toStrictEqual({ testMe: 'test' });
|
24
|
+
});
|
25
|
+
test('array with existing camelized keys', () => {
|
26
|
+
expect(deepCamelizeKeys_1.default([{ testMe: 'test' }, { testMe2: 'test' }])).toStrictEqual([{ testMe: 'test' }, { testMe2: 'test' }]);
|
27
|
+
});
|
28
|
+
test('object with mixed keys', () => {
|
29
|
+
expect(deepCamelizeKeys_1.default({ testMe: 'test', test_2: 'dog', test_me2: true })).toStrictEqual({ testMe: 'test', test2: 'dog', testMe2: true });
|
30
|
+
});
|
31
|
+
test('array with mixed keys', () => {
|
32
|
+
expect(deepCamelizeKeys_1.default([
|
33
|
+
{ testMe: 'test', test_2: 'dog', test_me2: true },
|
34
|
+
{ testMe3: 'test', test_3: 'dog', test_me4: true }
|
35
|
+
])).toStrictEqual([
|
36
|
+
{ testMe: 'test', test2: 'dog', testMe2: true },
|
37
|
+
{ testMe3: 'test', test3: 'dog', testMe4: true }
|
38
|
+
]);
|
39
|
+
});
|
40
|
+
test('nested with object at top level', () => {
|
41
|
+
expect(deepCamelizeKeys_1.default({
|
42
|
+
test_me: {
|
43
|
+
test_me2: {
|
44
|
+
test_me3: [
|
45
|
+
{ test_it_out: '49' },
|
46
|
+
{ test_fun: 'what' }
|
47
|
+
]
|
48
|
+
}
|
49
|
+
}
|
50
|
+
})).toStrictEqual({
|
51
|
+
testMe: {
|
52
|
+
testMe2: {
|
53
|
+
testMe3: [
|
54
|
+
{ testItOut: '49' },
|
55
|
+
{ testFun: 'what' }
|
56
|
+
]
|
57
|
+
}
|
58
|
+
}
|
59
|
+
});
|
60
|
+
});
|
61
|
+
test('nested with object at top level', () => {
|
62
|
+
expect(deepCamelizeKeys_1.default([{
|
63
|
+
test_me: {
|
64
|
+
test_me2: {
|
65
|
+
test_me3: [
|
66
|
+
{ test_it_out: '49' },
|
67
|
+
{ test_fun: 'what' }
|
68
|
+
]
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}, {
|
72
|
+
test_it52: 'what?'
|
73
|
+
}])).toStrictEqual([{
|
74
|
+
testMe: {
|
75
|
+
testMe2: {
|
76
|
+
testMe3: [
|
77
|
+
{ testItOut: '49' },
|
78
|
+
{ testFun: 'what' }
|
79
|
+
]
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}, {
|
83
|
+
testIt52: 'what?'
|
84
|
+
}]);
|
85
|
+
});
|
86
|
+
test('excludes keys by function', () => {
|
87
|
+
expect(deepCamelizeKeys_1.default({
|
88
|
+
test_me: {
|
89
|
+
test_me2: {
|
90
|
+
test_me3: [
|
91
|
+
{ test_it_out: '49' },
|
92
|
+
{ test_fun: 'what' }
|
93
|
+
]
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}, k => (k === 'test_me2'))).toStrictEqual({
|
97
|
+
testMe: {
|
98
|
+
test_me2: {
|
99
|
+
testMe3: [
|
100
|
+
{ testItOut: '49' },
|
101
|
+
{ testFun: 'what' }
|
102
|
+
]
|
103
|
+
}
|
104
|
+
}
|
105
|
+
});
|
106
|
+
});
|
data/client/lib/index.d.ts
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import _useAct from './useAct';
|
2
2
|
import _useSub from './useSub';
|
3
3
|
export declare const JasonProvider: ({ reducers, middleware, extraActions, children }: {
|
4
|
-
reducers
|
5
|
-
middleware
|
6
|
-
extraActions
|
7
|
-
children
|
4
|
+
reducers?: any;
|
5
|
+
middleware?: any;
|
6
|
+
extraActions?: any;
|
7
|
+
children?: any;
|
8
8
|
}) => any;
|
9
9
|
export declare const useAct: typeof _useAct;
|
10
10
|
export declare const useSub: typeof _useSub;
|
data/client/package.json
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "@jamesr2323/jason",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.4.0",
|
4
4
|
"module": "./lib/index.js",
|
5
|
+
"types": "./lib/index.d.ts",
|
5
6
|
"scripts": {
|
6
|
-
"build": "tsc"
|
7
|
+
"build": "tsc",
|
8
|
+
"test": "jest"
|
7
9
|
},
|
8
10
|
"dependencies": {
|
9
11
|
"@rails/actioncable": "^6.0.3-4",
|
@@ -17,11 +19,22 @@
|
|
17
19
|
"uuid": "^8.3.1"
|
18
20
|
},
|
19
21
|
"devDependencies": {
|
22
|
+
"@babel/core": "^7.12.10",
|
23
|
+
"@babel/preset-env": "^7.12.11",
|
24
|
+
"@babel/preset-typescript": "^7.12.7",
|
25
|
+
"@reduxjs/toolkit": "^1.5.0",
|
26
|
+
"@types/jest": "^26.0.19",
|
27
|
+
"babel-jest": "^26.6.3",
|
28
|
+
"jest": "^26.6.3",
|
29
|
+
"react": "^16.8.3",
|
30
|
+
"react-dom": "^16.8.3",
|
31
|
+
"react-redux": "^7.2.2",
|
20
32
|
"typescript": "^4.1.2"
|
21
33
|
},
|
22
34
|
"peerDependencies": {
|
35
|
+
"@reduxjs/toolkit": "^1.5.0",
|
23
36
|
"react": "^16.8.3",
|
24
|
-
"react-
|
25
|
-
"
|
37
|
+
"react-dom": "^16.8.3",
|
38
|
+
"react-redux": "^7.2.2"
|
26
39
|
}
|
27
40
|
}
|
@@ -7,26 +7,54 @@ import { Provider } from 'react-redux'
|
|
7
7
|
import { createEntityAdapter, createSlice, createReducer, configureStore } from '@reduxjs/toolkit'
|
8
8
|
import createJasonReducers from './createJasonReducers'
|
9
9
|
import createPayloadHandler from './createPayloadHandler'
|
10
|
+
import createOptDis from './createOptDis'
|
10
11
|
import makeEager from './makeEager'
|
11
12
|
import { camelizeKeys } from 'humps'
|
12
13
|
import md5 from 'blueimp-md5'
|
13
14
|
import _ from 'lodash'
|
14
15
|
import React, { useState, useEffect } from 'react'
|
16
|
+
import { validate as isUuid } from 'uuid'
|
15
17
|
|
16
|
-
const JasonProvider = ({ reducers, middleware, extraActions, children }) => {
|
18
|
+
const JasonProvider = ({ reducers, middleware, extraActions, children }: { reducers?: any, middleware?: any, extraActions?: any, children?: React.FC }) => {
|
17
19
|
const [store, setStore] = useState(null)
|
18
20
|
const [value, setValue] = useState(null)
|
19
21
|
const [connected, setConnected] = useState(false)
|
20
22
|
|
21
23
|
const csrfToken = (document.querySelector("meta[name=csrf-token]") as any).content
|
22
24
|
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken
|
23
|
-
const restClient = applyCaseMiddleware(axios.create()
|
25
|
+
const restClient = applyCaseMiddleware(axios.create(), {
|
26
|
+
preservedKeys: (key) => {
|
27
|
+
return isUuid(key)
|
28
|
+
}
|
29
|
+
})
|
24
30
|
|
25
31
|
useEffect(() => {
|
26
32
|
restClient.get('/jason/api/schema')
|
27
33
|
.then(({ data: snakey_schema }) => {
|
28
34
|
const schema = camelizeKeys(snakey_schema)
|
29
35
|
|
36
|
+
const serverActionQueue = function() {
|
37
|
+
const queue: any[] = []
|
38
|
+
let inFlight = false
|
39
|
+
|
40
|
+
return {
|
41
|
+
addItem: (item) => queue.push(item),
|
42
|
+
getItem: () => {
|
43
|
+
if (inFlight) return false
|
44
|
+
|
45
|
+
const item = queue.shift()
|
46
|
+
if (item) {
|
47
|
+
inFlight = true
|
48
|
+
return item
|
49
|
+
}
|
50
|
+
return false
|
51
|
+
},
|
52
|
+
itemProcessed: () => inFlight = false,
|
53
|
+
fullySynced: () => queue.length === 0 && !inFlight,
|
54
|
+
getData: () => ({ queue, inFlight })
|
55
|
+
}
|
56
|
+
}()
|
57
|
+
|
30
58
|
const consumer = createConsumer()
|
31
59
|
const allReducers = {
|
32
60
|
...reducers,
|
@@ -67,7 +95,7 @@ const JasonProvider = ({ reducers, middleware, extraActions, children }) => {
|
|
67
95
|
console.log('Subscribe with', config, md5Hash)
|
68
96
|
|
69
97
|
_.map(config, (v, model) => {
|
70
|
-
payloadHandlers[`${model}:${md5Hash}`] = createPayloadHandler(store.dispatch, subscription, model, schema[model])
|
98
|
+
payloadHandlers[`${model}:${md5Hash}`] = createPayloadHandler(store.dispatch, serverActionQueue, subscription, model, schema[model])
|
71
99
|
})
|
72
100
|
subscription.send({ createSubscription: config })
|
73
101
|
|
@@ -81,8 +109,8 @@ const JasonProvider = ({ reducers, middleware, extraActions, children }) => {
|
|
81
109
|
delete payloadHandlers[`${model}:${md5Hash}`]
|
82
110
|
})
|
83
111
|
}
|
84
|
-
|
85
|
-
const actions = createActions(schema, store, restClient, extraActions)
|
112
|
+
const optDis = createOptDis(schema, store.dispatch, restClient, serverActionQueue)
|
113
|
+
const actions = createActions(schema, store, restClient, optDis, extraActions)
|
86
114
|
const eager = makeEager(schema)
|
87
115
|
|
88
116
|
console.log({ actions })
|
data/client/src/actionFactory.ts
CHANGED
@@ -24,7 +24,7 @@ export default (dis, store, entity, { extraActions = {}, hasPriority = false } =
|
|
24
24
|
return dis({ type: `${pluralize(entity)}/remove`, payload: id })
|
25
25
|
}
|
26
26
|
|
27
|
-
const extraActionsResolved = _.mapValues(extraActions, v => v(dis, store, entity))
|
27
|
+
const extraActionsResolved = extraActions ? _.mapValues(extraActions, v => v(dis, store, entity)) : {}
|
28
28
|
|
29
29
|
if (hasPriority) {
|
30
30
|
return { add, upsert, setAll, remove, movePriority, ...extraActionsResolved }
|
data/client/src/createActions.ts
CHANGED
@@ -1,39 +1,8 @@
|
|
1
1
|
import actionFactory from './actionFactory'
|
2
2
|
import pluralize from 'pluralize'
|
3
3
|
import _ from 'lodash'
|
4
|
-
import { v4 as uuidv4 } from 'uuid'
|
5
|
-
|
6
|
-
function enrich(type, payload) {
|
7
|
-
if (type.split('/')[1] === 'upsert' && !(type.split('/')[0] === 'session')) {
|
8
|
-
if (!payload.id) {
|
9
|
-
return { ...payload, id: uuidv4() }
|
10
|
-
}
|
11
|
-
}
|
12
|
-
return payload
|
13
|
-
}
|
14
|
-
|
15
|
-
function makeOptDis(schema, dispatch, restClient) {
|
16
|
-
const plurals = _.keys(schema).map(k => pluralize(k))
|
17
|
-
|
18
|
-
return function (action) {
|
19
|
-
const { type, payload } = action
|
20
|
-
const data = enrich(type, payload)
|
21
|
-
|
22
|
-
dispatch(action)
|
23
|
-
|
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
|
-
|
33
|
-
function createActions(schema, store, restClient, extraActions) {
|
34
|
-
const dis = store.dispatch;
|
35
|
-
const optDis = makeOptDis(schema, dis, restClient)
|
36
4
|
|
5
|
+
function createActions(schema, store, restClient, optDis, extraActions) {
|
37
6
|
const actions = _.fromPairs(_.map(schema, (config, model: string) => {
|
38
7
|
if (config.priorityScope) {
|
39
8
|
return [pluralize(model), actionFactory(optDis, store, model, { hasPriority: true })]
|
@@ -42,7 +11,7 @@ function createActions(schema, store, restClient, extraActions) {
|
|
42
11
|
}
|
43
12
|
}))
|
44
13
|
|
45
|
-
const extraActionsResolved = extraActions(optDis, store, restClient)
|
14
|
+
const extraActionsResolved = extraActions ? extraActions(optDis, store, restClient, actions) : {}
|
46
15
|
|
47
16
|
return _.merge(actions, extraActionsResolved)
|
48
17
|
}
|
@@ -16,6 +16,7 @@ function generateSlices(schema) {
|
|
16
16
|
add: adapter.addOne,
|
17
17
|
setAll: adapter.setAll,
|
18
18
|
remove: adapter.removeOne,
|
19
|
+
removeMany: adapter.removeMany,
|
19
20
|
movePriority: (s, { payload: { id, priority, parentFilter } }) => {
|
20
21
|
// Get IDs and insert our item at the new index
|
21
22
|
var affectedIds = _.orderBy(_.filter(_.values(s.entities), parentFilter).filter(e => e.id !== id), 'priority').map(e => e.id)
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import _ from 'lodash'
|
2
|
+
import pluralize from 'pluralize'
|
3
|
+
import { v4 as uuidv4 } from 'uuid'
|
4
|
+
|
5
|
+
function enrich(type, payload) {
|
6
|
+
if (type.split('/')[1] === 'upsert' && !(type.split('/')[0] === 'session')) {
|
7
|
+
if (!payload.id) {
|
8
|
+
return { ...payload, id: uuidv4() }
|
9
|
+
}
|
10
|
+
}
|
11
|
+
return payload
|
12
|
+
}
|
13
|
+
|
14
|
+
export default function createOptDis(schema, dispatch, restClient, serverActionQueue) {
|
15
|
+
const plurals = _.keys(schema).map(k => pluralize(k))
|
16
|
+
let inFlight = false
|
17
|
+
|
18
|
+
function enqueueServerAction (action) {
|
19
|
+
serverActionQueue.addItem(action)
|
20
|
+
}
|
21
|
+
|
22
|
+
function dispatchServerAction() {
|
23
|
+
const action = serverActionQueue.getItem()
|
24
|
+
if (!action) return
|
25
|
+
|
26
|
+
inFlight = true
|
27
|
+
restClient.post('/jason/api/action', action)
|
28
|
+
.then(serverActionQueue.itemProcessed)
|
29
|
+
.catch(e => {
|
30
|
+
dispatch({ type: 'upsertLocalUi', data: { error: JSON.stringify(e) } })
|
31
|
+
serverActionQueue.itemProcessed()
|
32
|
+
})
|
33
|
+
}
|
34
|
+
|
35
|
+
setInterval(dispatchServerAction, 10)
|
36
|
+
|
37
|
+
return function (action) {
|
38
|
+
const { type, payload } = action
|
39
|
+
const data = enrich(type, payload)
|
40
|
+
|
41
|
+
dispatch({ type, payload: data })
|
42
|
+
|
43
|
+
if (plurals.indexOf(type.split('/')[0]) > -1) {
|
44
|
+
enqueueServerAction({ type, payload: data })
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
@@ -1,16 +1,18 @@
|
|
1
1
|
import { apply_patch } from 'jsonpatch'
|
2
|
-
import
|
2
|
+
import deepCamelizeKeys from './deepCamelizeKeys'
|
3
3
|
import pluralize from 'pluralize'
|
4
4
|
import _ from 'lodash'
|
5
|
+
import { validate as isUuid } from 'uuid'
|
5
6
|
|
6
7
|
function diffSeconds(dt2, dt1) {
|
7
8
|
var diff =(dt2.getTime() - dt1.getTime()) / 1000
|
8
9
|
return Math.abs(Math.round(diff))
|
9
10
|
}
|
10
11
|
|
11
|
-
export default function createPayloadHandler(dispatch, subscription, model, config) {
|
12
|
+
export default function createPayloadHandler(dispatch, serverActionQueue, subscription, model, config) {
|
12
13
|
console.log({ model, config })
|
13
|
-
let payload =
|
14
|
+
let payload = [] as any[]
|
15
|
+
let previousPayload = [] as any[]
|
14
16
|
let idx = 0
|
15
17
|
let patchQueue = {}
|
16
18
|
|
@@ -23,20 +25,40 @@ export default function createPayloadHandler(dispatch, subscription, model, conf
|
|
23
25
|
subscription.send({ getPayload: { model, config } })
|
24
26
|
}
|
25
27
|
|
28
|
+
function camelizeKeys(item) {
|
29
|
+
return deepCamelizeKeys(item, key => isUuid(key))
|
30
|
+
}
|
31
|
+
|
26
32
|
const tGetPayload = _.throttle(getPayload, 10000)
|
27
33
|
|
28
34
|
function dispatchPayload() {
|
35
|
+
// We want to avoid updates from server overwriting changes to local state, so if there is a queue then wait.
|
36
|
+
if (!serverActionQueue.fullySynced()) {
|
37
|
+
console.log(serverActionQueue.getData())
|
38
|
+
setTimeout(dispatchPayload, 100)
|
39
|
+
return
|
40
|
+
}
|
41
|
+
|
29
42
|
const includeModels = (config.includeModels || []).map(m => _.camelCase(m))
|
30
43
|
|
31
44
|
console.log("Dispatching", { payload, includeModels })
|
32
45
|
|
33
46
|
includeModels.forEach(m => {
|
34
47
|
const subPayload = _.flatten(_.compact(camelizeKeys(payload).map(instance => instance[m])))
|
35
|
-
|
48
|
+
const previousSubPayload = _.flatten(_.compact(camelizeKeys(previousPayload).map(instance => instance[m])))
|
49
|
+
|
50
|
+
// Find IDs that were in the payload but are no longer
|
51
|
+
const idsToRemove = _.difference(previousSubPayload.map(i => i.id), subPayload.map(i => i.id))
|
52
|
+
|
36
53
|
dispatch({ type: `${pluralize(m)}/upsertMany`, payload: subPayload })
|
54
|
+
dispatch({ type: `${pluralize(m)}/removeMany`, payload: idsToRemove })
|
37
55
|
})
|
38
56
|
|
57
|
+
const idsToRemove = _.difference(previousPayload.map(i => i.id), payload.map(i => i.id))
|
58
|
+
|
39
59
|
dispatch({ type: `${pluralize(model)}/upsertMany`, payload: camelizeKeys(payload) })
|
60
|
+
dispatch({ type: `${pluralize(model)}/removeMany`, payload: idsToRemove })
|
61
|
+
previousPayload = payload
|
40
62
|
}
|
41
63
|
|
42
64
|
function processQueue() {
|