@carbonorm/carbonreact 4.0.13 → 4.0.15
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.
- package/dist/CarbonReact.d.ts +7 -4
- package/dist/components/WebSocket/CarbonWebSocket.d.ts +2 -3
- package/dist/index.cjs.js +58 -33
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +58 -33
- package/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/CarbonReact.tsx +47 -31
- package/src/components/WebSocket/CarbonWebSocket.tsx +44 -25
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbonorm/carbonreact",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "4.0.
|
|
4
|
+
"version": "4.0.15",
|
|
5
5
|
"browser": "dist/index.umd.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
7
7
|
"main": "dist/index.cjs.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@carbonorm/carbonnode": "^2.0.
|
|
11
|
+
"@carbonorm/carbonnode": "^2.0.8",
|
|
12
12
|
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
|
13
13
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
|
14
14
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
package/src/CarbonReact.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { clearCache } from "@carbonorm/carbonnode";
|
|
2
1
|
import changed from "hoc/changed";
|
|
3
2
|
import { GlobalHistory } from "hoc/GlobalHistory";
|
|
4
3
|
import hexToRgb from "hoc/hexToRgb";
|
|
@@ -12,10 +11,7 @@ import CarbonWebSocket, { iCarbonWebSocketProps } from "./components/WebSocket/C
|
|
|
12
11
|
import updateRestfulObjectArrays, { iUpdateRestfulObjectArrays } from "./hoc/updateRestfulObjectArrays";
|
|
13
12
|
import deleteRestfulObjectArrays, { iDeleteRestfulObjectArrays } from "./hoc/deleteRestfulObjectArrays";
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
export type tStatefulApiData<T extends {
|
|
17
|
-
[key: string]: any
|
|
18
|
-
} = {}> = T[] | undefined | null;
|
|
14
|
+
export type tStatefulApiData<T extends { [key: string]: any } = {}> = T[] | undefined | null;
|
|
19
15
|
|
|
20
16
|
// our central container, single page application
|
|
21
17
|
export interface iCarbonReactState {
|
|
@@ -51,10 +47,11 @@ export function isJsonString(str: string) {
|
|
|
51
47
|
abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactState> extends Component<{
|
|
52
48
|
children?: ReactNode | ReactNode[],
|
|
53
49
|
instanceId?: string,
|
|
50
|
+
persistentState?: boolean,
|
|
54
51
|
websocket?: Omit<iCarbonWebSocketProps<P, S>, "instance"> | false
|
|
55
52
|
} & P, S> {
|
|
56
53
|
|
|
57
|
-
private static
|
|
54
|
+
private static allInstances = new Map<string, CarbonReact<any, any>>();
|
|
58
55
|
private static activeInstances = new Map<string, CarbonReact<any, any>>();
|
|
59
56
|
|
|
60
57
|
context: Context<S & iCarbonReactState> = createContext(this.state);
|
|
@@ -62,12 +59,28 @@ abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactSta
|
|
|
62
59
|
|
|
63
60
|
protected static _instance: ThisType<CarbonReact<any, any>>;
|
|
64
61
|
|
|
65
|
-
static getInstance<T extends CarbonReact<any, any>>(): T {
|
|
62
|
+
static getInstance<T extends CarbonReact<any, any>>(instanceId?: string): T {
|
|
63
|
+
|
|
64
|
+
const identifier = this.generateIdentifier(instanceId);
|
|
65
|
+
|
|
66
|
+
if (undefined !== instanceId) {
|
|
67
|
+
if (CarbonReact.activeInstances.has(identifier)) {
|
|
68
|
+
return CarbonReact.activeInstances.get(identifier) as T;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`No instance has been instantiated yet for class (${this.name}) with instanceId (${instanceId})`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!this._instance) {
|
|
74
|
+
throw new Error(`No instance has been instantiated yet for class (${this.name})`);
|
|
75
|
+
}
|
|
76
|
+
|
|
66
77
|
return this._instance as T;
|
|
67
78
|
}
|
|
79
|
+
|
|
68
80
|
static get instance() {
|
|
69
81
|
return this.getInstance();
|
|
70
82
|
}
|
|
83
|
+
|
|
71
84
|
static set instance(instance: CarbonReact<any, any>) {
|
|
72
85
|
this._instance = instance;
|
|
73
86
|
}
|
|
@@ -90,39 +103,50 @@ abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactSta
|
|
|
90
103
|
|
|
91
104
|
protected constructor(props: {
|
|
92
105
|
children?: ReactNode | ReactNode[];
|
|
93
|
-
shouldStatePersist?: boolean | undefined;
|
|
94
106
|
websocket?: boolean | iCarbonWebSocketProps<P, S> | undefined;
|
|
95
107
|
instanceId?: string; // Optional instanceId from props
|
|
108
|
+
persistentState?: boolean; // Optional persistentState from props
|
|
96
109
|
} & P) {
|
|
97
110
|
super(props);
|
|
98
111
|
|
|
99
|
-
const
|
|
100
|
-
const identifier = props.instanceId || target.name;
|
|
112
|
+
const identifier = this.generateIdentifier();
|
|
101
113
|
|
|
102
114
|
if (CarbonReact.activeInstances.has(identifier)) {
|
|
103
|
-
throw new Error(
|
|
115
|
+
throw new Error(`${identifier} instance already exists in the DOM! Each instance should have a unique instanceId.`);
|
|
104
116
|
}
|
|
105
117
|
|
|
118
|
+
// Register the new instance
|
|
106
119
|
CarbonReact.activeInstances.set(identifier, this);
|
|
107
120
|
|
|
108
|
-
|
|
121
|
+
if (props.persistentState && CarbonReact.allInstances.has(identifier)) {
|
|
122
|
+
// Reuse the state from the existing instance
|
|
123
|
+
this.state = CarbonReact.allInstances.get(identifier)!.state as S & iCarbonReactState;
|
|
124
|
+
} else {
|
|
125
|
+
this.state = initialCarbonReactState as unknown as S & iCarbonReactState;
|
|
126
|
+
CarbonReact.allInstances.set(identifier, this);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.target = new.target;
|
|
109
130
|
console.log('CarbonORM TSX CONSTRUCTOR');
|
|
110
131
|
|
|
111
|
-
Object.assign(target, {
|
|
132
|
+
Object.assign(this.target, {
|
|
112
133
|
_instance: this
|
|
113
134
|
});
|
|
135
|
+
}
|
|
114
136
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
137
|
+
private static generateIdentifier(instanceId?: string): string {
|
|
138
|
+
const className = this.name;
|
|
139
|
+
return instanceId ? `${className}-${instanceId}` : className;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private generateIdentifier(): string {
|
|
143
|
+
const className = (this.constructor as typeof CarbonReact).name;
|
|
144
|
+
return this.props.instanceId ? `${className}-${this.props.instanceId}` : className;
|
|
145
|
+
}
|
|
123
146
|
|
|
124
|
-
|
|
125
|
-
|
|
147
|
+
componentWillUnmount() {
|
|
148
|
+
const identifier = this.generateIdentifier();
|
|
149
|
+
CarbonReact.activeInstances.delete(identifier);
|
|
126
150
|
}
|
|
127
151
|
|
|
128
152
|
shouldComponentUpdate(
|
|
@@ -130,9 +154,6 @@ abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactSta
|
|
|
130
154
|
nextState: Readonly<S>,
|
|
131
155
|
_nextContext: any): boolean {
|
|
132
156
|
|
|
133
|
-
const identifier = this.props.instanceId || (this.constructor as typeof CarbonReact).name;
|
|
134
|
-
CarbonReact.persistentStateMap.set(identifier, nextState);
|
|
135
|
-
|
|
136
157
|
changed(this.constructor.name + ' (C6Api)', 'props', this.props, nextProps);
|
|
137
158
|
changed(this.constructor.name + ' (C6Api)', 'state', this.state, nextState);
|
|
138
159
|
|
|
@@ -179,11 +200,6 @@ abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactSta
|
|
|
179
200
|
<ToastContainer />
|
|
180
201
|
</>;
|
|
181
202
|
}
|
|
182
|
-
|
|
183
|
-
componentWillUnmount() {
|
|
184
|
-
const identifier = this.props.instanceId || (this.constructor as typeof CarbonReact).name;
|
|
185
|
-
CarbonReact.activeInstances.delete(identifier);
|
|
186
|
-
}
|
|
187
203
|
}
|
|
188
204
|
|
|
189
205
|
export default CarbonReact;
|
|
@@ -1,40 +1,44 @@
|
|
|
1
|
-
import CarbonReact, {iCarbonReactState, isJsonString} from "CarbonReact";
|
|
1
|
+
import CarbonReact, {iCarbonReactState, isJsonString, tStatefulApiData} from "CarbonReact";
|
|
2
2
|
import {addAlert} from "../Alert/Alert";
|
|
3
3
|
import {useEffectOnce} from "../../api/hoc/useEffectOnce";
|
|
4
|
-
import {
|
|
4
|
+
import {iC6Object} from "@carbonorm/carbonnode";
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
export interface iCarbonWebSocketProps<P,S extends iCarbonReactState> {
|
|
7
|
+
export interface iCarbonWebSocketProps<P, S extends iCarbonReactState> {
|
|
8
8
|
url?: string,
|
|
9
9
|
timeoutSeconds?: number,
|
|
10
10
|
heartbeatSeconds?: number,
|
|
11
|
-
instance: CarbonReact<P,S>,
|
|
12
|
-
|
|
13
|
-
WsLiveUpdates?: tC6RestApi,
|
|
11
|
+
instance: CarbonReact<P, S>,
|
|
12
|
+
C6?: iC6Object,
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* @function connect
|
|
18
17
|
* This function establishes a connection with the websocket and also ensures constant reconnection if connection closes
|
|
19
18
|
**/
|
|
20
|
-
export function initiateWebsocket<P,S extends iCarbonReactState>(props: iCarbonWebSocketProps<P,S>) {
|
|
19
|
+
export function initiateWebsocket<P, S extends iCarbonReactState>(props: iCarbonWebSocketProps<P, S>) {
|
|
21
20
|
|
|
22
21
|
let {
|
|
23
22
|
instance,
|
|
24
|
-
TABLES = undefined,
|
|
25
|
-
WsLiveUpdates = undefined,
|
|
26
23
|
url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/carbonorm/websocket',
|
|
27
24
|
timeoutSeconds = 250,
|
|
28
|
-
heartbeatSeconds = 60
|
|
25
|
+
heartbeatSeconds = 60,
|
|
26
|
+
C6
|
|
29
27
|
} = props;
|
|
30
28
|
|
|
29
|
+
const {
|
|
30
|
+
TABLES = undefined,
|
|
31
|
+
IMPORT = undefined,
|
|
32
|
+
} = C6 ?? {};
|
|
33
|
+
|
|
34
|
+
|
|
31
35
|
const {websocket} = instance.state;
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
if (!("WebSocket" in window)) {
|
|
35
39
|
|
|
36
40
|
// todo - store that this has been shown in the state
|
|
37
|
-
addAlert<P,S>({
|
|
41
|
+
addAlert<P, S>({
|
|
38
42
|
title: 'Browser does not support websockets, live updates will fail. You may need to refresh the page to see the newest content.',
|
|
39
43
|
text: 'Please use a modern browser.',
|
|
40
44
|
icon: 'warning',
|
|
@@ -105,14 +109,6 @@ export function initiateWebsocket<P,S extends iCarbonReactState>(props: iCarbonW
|
|
|
105
109
|
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
if (undefined === WsLiveUpdates) {
|
|
109
|
-
|
|
110
|
-
console.log('WebSocket updates without the WsLiveUpdates property passed will not automatically update the state.')
|
|
111
|
-
|
|
112
|
-
return;
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
112
|
if (parsedData?.REST) {
|
|
117
113
|
|
|
118
114
|
const TABLE_NAME: string = parsedData?.REST?.TABLE_NAME;
|
|
@@ -139,7 +135,7 @@ export function initiateWebsocket<P,S extends iCarbonReactState>(props: iCarbonW
|
|
|
139
135
|
|
|
140
136
|
const TABLE_NAME_SHORT = TABLE_NAME.substring(TABLE_PREFIX.length);
|
|
141
137
|
|
|
142
|
-
const currentCache: [] = instance.state[TABLE_NAME_SHORT]
|
|
138
|
+
const currentCache: tStatefulApiData<{ [key: string]: any }> = instance.state[TABLE_NAME_SHORT]
|
|
143
139
|
|
|
144
140
|
// just because we have a websocket update, doesn't mean we need the update
|
|
145
141
|
// check to see if the primary key is in the current cache
|
|
@@ -155,7 +151,8 @@ export function initiateWebsocket<P,S extends iCarbonReactState>(props: iCarbonW
|
|
|
155
151
|
|
|
156
152
|
const primaryKeyKeys = Object.keys(REQUEST_PRIMARY_KEY)
|
|
157
153
|
|
|
158
|
-
|
|
154
|
+
// todo - which direction should we filter
|
|
155
|
+
const elementsToUpdate = currentCache?.filter((row: any) => {
|
|
159
156
|
|
|
160
157
|
for (const element of primaryKeyKeys) {
|
|
161
158
|
|
|
@@ -174,7 +171,7 @@ export function initiateWebsocket<P,S extends iCarbonReactState>(props: iCarbonW
|
|
|
174
171
|
|
|
175
172
|
return true
|
|
176
173
|
|
|
177
|
-
})
|
|
174
|
+
}) ?? []
|
|
178
175
|
|
|
179
176
|
console.log('elementsToUpdate', elementsToUpdate)
|
|
180
177
|
|
|
@@ -192,8 +189,30 @@ export function initiateWebsocket<P,S extends iCarbonReactState>(props: iCarbonW
|
|
|
192
189
|
|
|
193
190
|
})
|
|
194
191
|
|
|
195
|
-
updatedElements.forEach((row: any) => {
|
|
196
|
-
|
|
192
|
+
updatedElements.forEach(async (row: any) => {
|
|
193
|
+
|
|
194
|
+
const RestRequests = await IMPORT?.(TABLE_NAME_SHORT)
|
|
195
|
+
|
|
196
|
+
const {
|
|
197
|
+
postState,
|
|
198
|
+
deleteState,
|
|
199
|
+
putState,
|
|
200
|
+
} = RestRequests;
|
|
201
|
+
|
|
202
|
+
switch (METHOD) {
|
|
203
|
+
case 'POST':
|
|
204
|
+
postState({}, row)
|
|
205
|
+
break;
|
|
206
|
+
case 'DELETE':
|
|
207
|
+
deleteState({}, row)
|
|
208
|
+
break;
|
|
209
|
+
case 'PUT':
|
|
210
|
+
putState({}, row)
|
|
211
|
+
break;
|
|
212
|
+
default:
|
|
213
|
+
console.error('Method not supported', METHOD)
|
|
214
|
+
}
|
|
215
|
+
|
|
197
216
|
})
|
|
198
217
|
|
|
199
218
|
}
|
|
@@ -286,7 +305,7 @@ export function initiateWebsocket<P,S extends iCarbonReactState>(props: iCarbonW
|
|
|
286
305
|
|
|
287
306
|
}
|
|
288
307
|
|
|
289
|
-
export default function <P,S extends iCarbonReactState>(props: iCarbonWebSocketProps<P,S>) {
|
|
308
|
+
export default function <P, S extends iCarbonReactState>(props: iCarbonWebSocketProps<P, S>) {
|
|
290
309
|
|
|
291
310
|
useEffectOnce(() => {
|
|
292
311
|
|