jason-rails 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile.lock +152 -2
  4. data/app/controllers/jason/api_controller.rb +1 -1
  5. data/client/lib/JasonContext.d.ts +6 -1
  6. data/client/lib/JasonProvider.d.ts +2 -2
  7. data/client/lib/JasonProvider.js +5 -124
  8. data/client/lib/createJasonReducers.js +41 -3
  9. data/client/lib/createOptDis.js +0 -2
  10. data/client/lib/createPayloadHandler.d.ts +6 -1
  11. data/client/lib/createPayloadHandler.js +42 -54
  12. data/client/lib/createServerActionQueue.d.ts +10 -0
  13. data/client/lib/createServerActionQueue.js +48 -0
  14. data/client/lib/createServerActionQueue.test.d.ts +1 -0
  15. data/client/lib/createServerActionQueue.test.js +37 -0
  16. data/client/lib/index.d.ts +3 -2
  17. data/client/lib/pruneIdsMiddleware.d.ts +2 -0
  18. data/client/lib/pruneIdsMiddleware.js +26 -0
  19. data/client/lib/restClient.d.ts +2 -0
  20. data/client/lib/restClient.js +17 -0
  21. data/client/lib/useJason.d.ts +5 -0
  22. data/client/lib/useJason.js +99 -0
  23. data/client/lib/useJason.test.d.ts +1 -0
  24. data/client/lib/useJason.test.js +79 -0
  25. data/client/lib/useSub.js +1 -0
  26. data/client/package.json +4 -3
  27. data/client/src/JasonProvider.tsx +5 -123
  28. data/client/src/createJasonReducers.ts +49 -3
  29. data/client/src/createOptDis.ts +0 -2
  30. data/client/src/createPayloadHandler.ts +47 -63
  31. data/client/src/createServerActionQueue.test.ts +42 -0
  32. data/client/src/createServerActionQueue.ts +47 -0
  33. data/client/src/pruneIdsMiddleware.ts +24 -0
  34. data/client/src/restClient.ts +13 -0
  35. data/client/src/useJason.test.ts +81 -0
  36. data/client/src/useJason.ts +115 -0
  37. data/client/src/useSub.ts +1 -0
  38. data/client/yarn.lock +59 -3
  39. data/jason-rails.gemspec +4 -0
  40. data/lib/jason.rb +12 -0
  41. data/lib/jason/api_model.rb +2 -12
  42. data/lib/jason/channel.rb +43 -21
  43. data/lib/jason/lua_generator.rb +49 -0
  44. data/lib/jason/publisher.rb +76 -35
  45. data/lib/jason/publisher_old.rb +112 -0
  46. data/lib/jason/subscription.rb +322 -99
  47. data/lib/jason/subscription_old.rb +171 -0
  48. data/lib/jason/version.rb +1 -1
  49. metadata +67 -3
@@ -0,0 +1,10 @@
1
+ export default function createServerActionQueue(): {
2
+ addItem: (item: any) => void;
3
+ getItem: () => any;
4
+ itemProcessed: () => boolean;
5
+ fullySynced: () => boolean;
6
+ getData: () => {
7
+ queue: any[];
8
+ inFlight: boolean;
9
+ };
10
+ };
@@ -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;
@@ -0,0 +1,37 @@
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 createServerActionQueue_1 = __importDefault(require("./createServerActionQueue"));
7
+ test('Adding items', () => {
8
+ const serverActionQueue = createServerActionQueue_1.default();
9
+ serverActionQueue.addItem({ type: 'entity/add', payload: { id: 'abc', attribute: 1 } });
10
+ const item = serverActionQueue.getItem();
11
+ expect(item).toStrictEqual({ type: 'entity/add', payload: { id: 'abc', attribute: 1 } });
12
+ });
13
+ test('Deduping of items that will overwrite each other', () => {
14
+ const serverActionQueue = createServerActionQueue_1.default();
15
+ serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } });
16
+ serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 2 } });
17
+ serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 3 } });
18
+ const item = serverActionQueue.getItem();
19
+ expect(item).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 3 } });
20
+ });
21
+ test('Deduping of items with a superset', () => {
22
+ const serverActionQueue = createServerActionQueue_1.default();
23
+ serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } });
24
+ serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 2, attribute2: 'test' } });
25
+ const item = serverActionQueue.getItem();
26
+ expect(item).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 2, attribute2: 'test' } });
27
+ });
28
+ test("doesn't dedupe items with some attributes missing", () => {
29
+ const serverActionQueue = createServerActionQueue_1.default();
30
+ serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } });
31
+ serverActionQueue.addItem({ type: 'entity/upsert', payload: { id: 'abc', attribute2: 'test' } });
32
+ const item = serverActionQueue.getItem();
33
+ serverActionQueue.itemProcessed();
34
+ const item2 = serverActionQueue.getItem();
35
+ expect(item).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute: 1 } });
36
+ expect(item2).toStrictEqual({ type: 'entity/upsert', payload: { id: 'abc', attribute2: 'test' } });
37
+ });
@@ -1,10 +1,11 @@
1
+ /// <reference types="react" />
1
2
  import _useAct from './useAct';
2
3
  import _useSub from './useSub';
3
4
  export declare const JasonProvider: ({ reducers, middleware, extraActions, children }: {
4
5
  reducers?: any;
5
6
  middleware?: any;
6
7
  extraActions?: any;
7
- children?: any;
8
- }) => any;
8
+ children?: import("react").FC<{}> | undefined;
9
+ }) => JSX.Element;
9
10
  export declare const useAct: typeof _useAct;
10
11
  export declare const useSub: typeof _useSub;
@@ -0,0 +1,2 @@
1
+ declare const pruneIdsMiddleware: (schema: any) => (store: any) => (next: any) => (action: any) => any;
2
+ export default pruneIdsMiddleware;
@@ -0,0 +1,26 @@
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 pruneIdsMiddleware = schema => store => next => action => {
9
+ const { type } = action;
10
+ const result = next(action);
11
+ const state = store.getState();
12
+ if (type === 'jasonModels/setSubscriptionIds') {
13
+ // Check every model
14
+ lodash_1.default.map(lodash_1.default.keys(schema), model => {
15
+ let ids = [];
16
+ lodash_1.default.map(state.jasonModels[model], (subscribedIds, k) => {
17
+ ids = lodash_1.default.union(ids, subscribedIds);
18
+ });
19
+ // Find IDs currently in Redux that aren't in any subscription
20
+ const idsToRemove = lodash_1.default.difference(state[pluralize_1.default(model)].ids, ids);
21
+ store.dispatch({ type: `${pluralize_1.default(model)}/removeMany`, payload: idsToRemove });
22
+ });
23
+ }
24
+ return result;
25
+ };
26
+ exports.default = pruneIdsMiddleware;
@@ -0,0 +1,2 @@
1
+ declare const restClient: import("axios").AxiosInstance;
2
+ export default restClient;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ var _a;
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const axios_case_converter_1 = __importDefault(require("axios-case-converter"));
9
+ const uuid_1 = require("uuid");
10
+ const csrfToken = (_a = document === null || document === void 0 ? void 0 : document.querySelector("meta[name=csrf-token]")) === null || _a === void 0 ? void 0 : _a.content;
11
+ axios_1.default.defaults.headers.common['X-CSRF-Token'] = csrfToken;
12
+ const restClient = axios_case_converter_1.default(axios_1.default.create(), {
13
+ preservedKeys: (key) => {
14
+ return uuid_1.validate(key);
15
+ }
16
+ });
17
+ exports.default = restClient;
@@ -0,0 +1,5 @@
1
+ export default function useJason({ reducers, middleware, extraActions }: {
2
+ reducers?: any;
3
+ middleware?: any[];
4
+ extraActions?: any;
5
+ }): any[];
@@ -0,0 +1,99 @@
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 createActions_1 = __importDefault(require("./createActions"));
7
+ const createJasonReducers_1 = __importDefault(require("./createJasonReducers"));
8
+ const createPayloadHandler_1 = __importDefault(require("./createPayloadHandler"));
9
+ const createOptDis_1 = __importDefault(require("./createOptDis"));
10
+ const createServerActionQueue_1 = __importDefault(require("./createServerActionQueue"));
11
+ const restClient_1 = __importDefault(require("./restClient"));
12
+ const pruneIdsMiddleware_1 = __importDefault(require("./pruneIdsMiddleware"));
13
+ const actioncable_1 = require("@rails/actioncable");
14
+ const toolkit_1 = require("@reduxjs/toolkit");
15
+ const makeEager_1 = __importDefault(require("./makeEager"));
16
+ const humps_1 = require("humps");
17
+ const blueimp_md5_1 = __importDefault(require("blueimp-md5"));
18
+ const lodash_1 = __importDefault(require("lodash"));
19
+ const react_1 = require("react");
20
+ function useJason({ reducers, middleware = [], extraActions }) {
21
+ const [store, setStore] = react_1.useState(null);
22
+ const [value, setValue] = react_1.useState(null);
23
+ const [connected, setConnected] = react_1.useState(false);
24
+ react_1.useEffect(() => {
25
+ restClient_1.default.get('/jason/api/schema')
26
+ .then(({ data: snakey_schema }) => {
27
+ const schema = humps_1.camelizeKeys(snakey_schema);
28
+ console.debug({ schema });
29
+ const serverActionQueue = createServerActionQueue_1.default();
30
+ const consumer = actioncable_1.createConsumer();
31
+ const allReducers = Object.assign(Object.assign({}, reducers), createJasonReducers_1.default(schema));
32
+ console.debug({ allReducers });
33
+ const store = toolkit_1.configureStore({ reducer: allReducers, middleware: [...middleware, pruneIdsMiddleware_1.default(schema)] });
34
+ const dispatch = store.dispatch;
35
+ const optDis = createOptDis_1.default(schema, dispatch, restClient_1.default, serverActionQueue);
36
+ const actions = createActions_1.default(schema, store, restClient_1.default, optDis, extraActions);
37
+ const eager = makeEager_1.default(schema);
38
+ let payloadHandlers = {};
39
+ let configs = {};
40
+ function handlePayload(payload) {
41
+ const { md5Hash } = payload;
42
+ const handler = payloadHandlers[md5Hash];
43
+ if (handler) {
44
+ handler(payload);
45
+ }
46
+ else {
47
+ console.warn("Payload arrived with no handler", payload, payloadHandlers);
48
+ }
49
+ }
50
+ const subscription = (consumer.subscriptions.create({
51
+ channel: 'Jason::Channel'
52
+ }, {
53
+ connected: () => {
54
+ setConnected(true);
55
+ dispatch({ type: 'jason/upsert', payload: { connected: true } });
56
+ console.debug('Connected to ActionCable');
57
+ // When AC loses connection - all state is lost, so we need to re-initialize all subscriptions
58
+ lodash_1.default.values(configs).forEach(config => createSubscription(config));
59
+ },
60
+ received: payload => {
61
+ handlePayload(payload);
62
+ console.debug("ActionCable Payload received: ", payload);
63
+ },
64
+ disconnected: () => {
65
+ setConnected(false);
66
+ dispatch({ type: 'jason/upsert', payload: { connected: false } });
67
+ console.warn('Disconnected from ActionCable');
68
+ }
69
+ }));
70
+ function createSubscription(config) {
71
+ // We need the hash to be consistent in Ruby / Javascript
72
+ const hashableConfig = lodash_1.default(Object.assign({ conditions: {}, includes: {} }, config)).toPairs().sortBy(0).fromPairs().value();
73
+ const md5Hash = blueimp_md5_1.default(JSON.stringify(hashableConfig));
74
+ payloadHandlers[md5Hash] = createPayloadHandler_1.default({ dispatch, serverActionQueue, subscription, config });
75
+ configs[md5Hash] = hashableConfig;
76
+ setTimeout(() => subscription.send({ createSubscription: hashableConfig }), 500);
77
+ return {
78
+ remove: () => removeSubscription(hashableConfig),
79
+ md5Hash
80
+ };
81
+ }
82
+ function removeSubscription(config) {
83
+ subscription.send({ removeSubscription: config });
84
+ const md5Hash = blueimp_md5_1.default(JSON.stringify(config));
85
+ delete payloadHandlers[md5Hash];
86
+ delete configs[md5Hash];
87
+ }
88
+ setValue({
89
+ actions: actions,
90
+ subscribe: config => createSubscription(config),
91
+ eager,
92
+ handlePayload
93
+ });
94
+ setStore(store);
95
+ });
96
+ }, []);
97
+ return [store, value, connected];
98
+ }
99
+ exports.default = useJason;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const react_hooks_1 = require("@testing-library/react-hooks");
16
+ const useJason_1 = __importDefault(require("./useJason"));
17
+ const restClient_1 = __importDefault(require("./restClient"));
18
+ jest.mock('./restClient');
19
+ test('it works', () => __awaiter(void 0, void 0, void 0, function* () {
20
+ const resp = { data: { post: {} } };
21
+ // @ts-ignore
22
+ restClient_1.default.get.mockResolvedValue(resp);
23
+ const { result, waitForNextUpdate } = react_hooks_1.renderHook(() => useJason_1.default({ reducers: {
24
+ test: (s, a) => s || {}
25
+ } }));
26
+ yield waitForNextUpdate();
27
+ const [store, value, connected] = result.current;
28
+ const { handlePayload, subscribe } = value;
29
+ const subscription = subscribe({ post: {} });
30
+ handlePayload({
31
+ type: 'payload',
32
+ model: 'post',
33
+ payload: [{ id: 4, name: 'test' }],
34
+ md5Hash: subscription.md5Hash,
35
+ idx: 1
36
+ });
37
+ handlePayload({
38
+ id: 4,
39
+ model: 'post',
40
+ destroy: true,
41
+ md5Hash: subscription.md5Hash,
42
+ idx: 2
43
+ });
44
+ handlePayload({
45
+ id: 5,
46
+ model: 'post',
47
+ payload: { id: 5, name: 'test2' },
48
+ md5Hash: subscription.md5Hash,
49
+ idx: 3
50
+ });
51
+ }));
52
+ test('pruning IDs', () => __awaiter(void 0, void 0, void 0, function* () {
53
+ const resp = { data: { post: {} } };
54
+ // @ts-ignore
55
+ restClient_1.default.get.mockResolvedValue(resp);
56
+ const { result, waitForNextUpdate } = react_hooks_1.renderHook(() => useJason_1.default({ reducers: {
57
+ test: (s, a) => s || {}
58
+ } }));
59
+ yield waitForNextUpdate();
60
+ const [store, value, connected] = result.current;
61
+ const { handlePayload, subscribe } = value;
62
+ const subscription = subscribe({ post: {} });
63
+ handlePayload({
64
+ type: 'payload',
65
+ model: 'post',
66
+ payload: [{ id: 4, name: 'test' }],
67
+ md5Hash: subscription.md5Hash,
68
+ idx: 1
69
+ });
70
+ handlePayload({
71
+ type: 'payload',
72
+ model: 'post',
73
+ payload: [{ id: 5, name: 'test it out' }],
74
+ md5Hash: subscription.md5Hash,
75
+ idx: 2
76
+ });
77
+ // The ID 4 should have been pruned
78
+ expect(store.getState().posts.ids).toStrictEqual([5]);
79
+ }));
@@ -8,6 +8,7 @@ const react_1 = require("react");
8
8
  function useSub(config) {
9
9
  const subscribe = react_1.useContext(JasonContext_1.default).subscribe;
10
10
  react_1.useEffect(() => {
11
+ // @ts-ignore
11
12
  return subscribe(config);
12
13
  }, []);
13
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jamesr2323/jason",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "module": "./lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "scripts": {
@@ -23,11 +23,12 @@
23
23
  "@babel/preset-env": "^7.12.11",
24
24
  "@babel/preset-typescript": "^7.12.7",
25
25
  "@reduxjs/toolkit": "^1.5.0",
26
+ "@testing-library/react-hooks": "^5.0.3",
26
27
  "@types/jest": "^26.0.19",
27
28
  "babel-jest": "^26.6.3",
28
29
  "jest": "^26.6.3",
29
- "react": "^16.8.3",
30
- "react-dom": "^16.8.3",
30
+ "react": "^16.9.x",
31
+ "react-dom": "^16.9.x",
31
32
  "react-redux": "^7.2.2",
32
33
  "typescript": "^4.1.2"
33
34
  },
@@ -1,130 +1,12 @@
1
- import createActions from './createActions'
2
- import { createConsumer } from "@rails/actioncable"
3
- import JasonContext from './JasonContext'
4
- import axios from 'axios'
5
- import applyCaseMiddleware from 'axios-case-converter'
1
+ import React from 'react'
2
+ import useJason from './useJason'
6
3
  import { Provider } from 'react-redux'
7
- import { createEntityAdapter, createSlice, createReducer, configureStore } from '@reduxjs/toolkit'
8
- import createJasonReducers from './createJasonReducers'
9
- import createPayloadHandler from './createPayloadHandler'
10
- import createOptDis from './createOptDis'
11
- import makeEager from './makeEager'
12
- import { camelizeKeys } from 'humps'
13
- import md5 from 'blueimp-md5'
14
- import _ from 'lodash'
15
- import React, { useState, useEffect } from 'react'
16
- import { validate as isUuid } from 'uuid'
4
+ import JasonContext from './JasonContext'
17
5
 
18
6
  const JasonProvider = ({ reducers, middleware, extraActions, children }: { reducers?: any, middleware?: any, extraActions?: any, children?: React.FC }) => {
19
- const [store, setStore] = useState(null)
20
- const [value, setValue] = useState(null)
21
- const [connected, setConnected] = useState(false)
22
-
23
- const csrfToken = (document.querySelector("meta[name=csrf-token]") as any).content
24
- axios.defaults.headers.common['X-CSRF-Token'] = csrfToken
25
- const restClient = applyCaseMiddleware(axios.create(), {
26
- preservedKeys: (key) => {
27
- return isUuid(key)
28
- }
29
- })
30
-
31
- useEffect(() => {
32
- restClient.get('/jason/api/schema')
33
- .then(({ data: snakey_schema }) => {
34
- const schema = camelizeKeys(snakey_schema)
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
-
58
- const consumer = createConsumer()
59
- const allReducers = {
60
- ...reducers,
61
- ...createJasonReducers(schema)
62
- }
63
-
64
- console.log({ schema, allReducers })
65
- const store = configureStore({ reducer: allReducers, middleware })
66
-
67
- let payloadHandlers = {}
68
- function handlePayload(payload) {
69
- const { model, md5Hash } = payload
70
- console.log({ md5Hash, fn: `${model}:${md5Hash}`, payloadHandlers, model: _.camelCase(model), payload })
71
- const handler = payloadHandlers[`${_.camelCase(model)}:${md5Hash}`]
72
- if (handler) {
73
- handler({ ...payload, model: _.camelCase(model) })
74
- }
75
- }
76
-
77
- const subscription = (consumer.subscriptions.create({
78
- channel: 'Jason::Channel'
79
- }, {
80
- connected: () => {
81
- setConnected(true)
82
- },
83
- received: payload => {
84
- console.log("Payload received", payload)
85
- handlePayload(payload)
86
- },
87
- disconnected: () => console.warn('Disconnected from ActionCable')
88
- }));
89
-
90
- console.log('sending message')
91
- subscription.send({ message: 'test' })
92
-
93
- function createSubscription(config) {
94
- const md5Hash = md5(JSON.stringify(config))
95
- console.log('Subscribe with', config, md5Hash)
96
-
97
- _.map(config, (v, model) => {
98
- payloadHandlers[`${model}:${md5Hash}`] = createPayloadHandler(store.dispatch, serverActionQueue, subscription, model, schema[model])
99
- })
100
- subscription.send({ createSubscription: config })
101
-
102
- return () => removeSubscription(config)
103
- }
104
-
105
- function removeSubscription(config) {
106
- subscription.send({ removeSubscription: config })
107
- const md5Hash = md5(JSON.stringify(config))
108
- _.map(config, (v, model) => {
109
- delete payloadHandlers[`${model}:${md5Hash}`]
110
- })
111
- }
112
- const optDis = createOptDis(schema, store.dispatch, restClient, serverActionQueue)
113
- const actions = createActions(schema, store, restClient, optDis, extraActions)
114
- const eager = makeEager(schema)
115
-
116
- console.log({ actions })
117
-
118
- setValue({
119
- actions: actions,
120
- subscribe: (config) => createSubscription(config),
121
- eager
122
- })
123
- setStore(store)
124
- })
125
- }, [])
7
+ const [store, value, connected] = useJason({ reducers, middleware, extraActions })
126
8
 
127
- if(!(store && value && connected)) return <div /> // Wait for async fetch of schema to complete
9
+ if(!(store && value)) return <div /> // Wait for async fetch of schema to complete
128
10
 
129
11
  return <Provider store={store}>
130
12
  <JasonContext.Provider value={value}>{ children }</JasonContext.Provider>