@weavy/uikit-react 18.0.1 → 18.0.2

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.
@@ -1,4 +1,4 @@
1
- import { IWeavyClient, WeavyClientOptions } from '../types/types';
1
+ import { IWeavyClient, WeavyClientOptions } from "../types/types";
2
2
  export default class WeavyClient implements IWeavyClient {
3
3
  url: string;
4
4
  connection: import("@microsoft/signalr").HubConnection;
@@ -6,6 +6,7 @@ export default class WeavyClient implements IWeavyClient {
6
6
  groups: string[];
7
7
  connectionEvents: any[];
8
8
  isConnectionStarted: any;
9
+ private signalRAccessTokenRefresh;
9
10
  token: string;
10
11
  tokenPromise: Promise<string> | null;
11
12
  EVENT_NAMESPACE: string;
@@ -13,11 +14,12 @@ export default class WeavyClient implements IWeavyClient {
13
14
  EVENT_RECONNECTING: string;
14
15
  EVENT_RECONNECTED: string;
15
16
  constructor(options: WeavyClientOptions);
17
+ connect(): Promise<void>;
16
18
  get(url: string, retry?: boolean): Promise<Response>;
17
19
  post(url: string, method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", body: string | FormData, contentType?: string, retry?: boolean): Promise<Response>;
18
20
  upload(url: string, method: "POST" | "PUT" | "PATCH", body: string | FormData, contentType?: string, onProgress?: (progress: number) => void, retry?: boolean): Promise<Response>;
19
21
  getToken(refresh: boolean): Promise<string>;
20
- tokenFactoryInternal(refresh?: boolean, fromSR?: boolean): Promise<string>;
22
+ tokenFactoryInternal(refresh?: boolean): Promise<string>;
21
23
  subscribe(group: string, event: string, callback: any): Promise<void>;
22
24
  unsubscribe(group: string, event: string, callback: any): Promise<void>;
23
25
  destroy(): void;
package/dist/index.d.ts CHANGED
@@ -68,6 +68,7 @@ declare class WeavyClient implements IWeavyClient {
68
68
  groups: string[];
69
69
  connectionEvents: any[];
70
70
  isConnectionStarted: any;
71
+ private signalRAccessTokenRefresh;
71
72
  token: string;
72
73
  tokenPromise: Promise<string> | null;
73
74
  EVENT_NAMESPACE: string;
@@ -75,11 +76,12 @@ declare class WeavyClient implements IWeavyClient {
75
76
  EVENT_RECONNECTING: string;
76
77
  EVENT_RECONNECTED: string;
77
78
  constructor(options: WeavyClientOptions);
79
+ connect(): Promise<void>;
78
80
  get(url: string, retry?: boolean): Promise<Response>;
79
81
  post(url: string, method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", body: string | FormData, contentType?: string, retry?: boolean): Promise<Response>;
80
82
  upload(url: string, method: "POST" | "PUT" | "PATCH", body: string | FormData, contentType?: string, onProgress?: (progress: number) => void, retry?: boolean): Promise<Response>;
81
83
  getToken(refresh: boolean): Promise<string>;
82
- tokenFactoryInternal(refresh?: boolean, fromSR?: boolean): Promise<string>;
84
+ tokenFactoryInternal(refresh?: boolean): Promise<string>;
83
85
  subscribe(group: string, event: string, callback: any): Promise<void>;
84
86
  unsubscribe(group: string, event: string, callback: any): Promise<void>;
85
87
  destroy(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weavy/uikit-react",
3
- "version": "18.0.1",
3
+ "version": "18.0.2",
4
4
  "author": "Weavy",
5
5
  "description": "React UI-kit for Weavy",
6
6
  "homepage": "https://github.com/weavy/weavy-uikit-react",
@@ -1,206 +1,261 @@
1
- import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';
2
- import { IWeavyClient, WeavyClientOptions } from '../types/types';
1
+ import { HubConnectionBuilder, LogLevel } from "@microsoft/signalr";
2
+ import { IWeavyClient, WeavyClientOptions } from "../types/types";
3
3
 
4
4
  export default class WeavyClient implements IWeavyClient {
5
- url;
6
- connection;
7
- tokenFactory;
8
- groups: string[] = [];
9
- connectionEvents: any[] = [];
10
- isConnectionStarted: any;
11
- token: string = "";
12
- tokenPromise: Promise<string> | null;
13
- EVENT_NAMESPACE = ".connection";
14
- EVENT_CLOSE = "close";
15
- EVENT_RECONNECTING = "reconnecting";
16
- EVENT_RECONNECTED = "reconnected";
17
-
18
-
19
- constructor(options: WeavyClientOptions) {
20
- this.url = options.url;
21
- this.tokenFactory = options.tokenFactory;
22
- this.tokenPromise = null;
23
- this.connection = new HubConnectionBuilder()
24
- .configureLogging(LogLevel.None)
25
- .withUrl(this.url + "/hubs/rtm", {
26
- accessTokenFactory: () => { return this.tokenFactoryInternal.call(this, true, true) }
27
- })
28
- .withAutomaticReconnect()
29
- .build();
30
-
5
+ url;
6
+ connection;
7
+ tokenFactory;
8
+ groups: string[] = [];
9
+ connectionEvents: any[] = [];
10
+ isConnectionStarted: any;
11
+ private signalRAccessTokenRefresh = false;
12
+
13
+ token: string = "";
14
+ tokenPromise: Promise<string> | null;
15
+ EVENT_NAMESPACE = ".connection";
16
+ EVENT_CLOSE = "close";
17
+ EVENT_RECONNECTING = "reconnecting";
18
+ EVENT_RECONNECTED = "reconnected";
19
+
20
+ constructor(options: WeavyClientOptions) {
21
+ this.url = options.url;
22
+ this.tokenFactory = options.tokenFactory;
23
+ this.tokenPromise = null;
24
+ this.connection = new HubConnectionBuilder()
25
+ .configureLogging(LogLevel.None)
26
+ .withUrl(this.url + "/hubs/rtm", {
27
+ accessTokenFactory: async () => {
28
+ if (this.signalRAccessTokenRefresh) {
29
+ console.error("SignalR retrying with refreshed token.");
30
+ const token = await this.tokenFactoryInternal(true);
31
+ this.signalRAccessTokenRefresh = false;
32
+ return token;
33
+ } else {
34
+ //console.error("first attempt")
35
+ const token = await this.tokenFactoryInternal();
36
+ return token;
37
+ }
38
+ },
39
+ })
40
+ .withAutomaticReconnect()
41
+ .build();
42
+
43
+ this.connect();
44
+
45
+ this.connection.onclose((error) =>
46
+ this.triggerHandler(this.EVENT_CLOSE, error)
47
+ );
48
+ this.connection.onreconnecting((error) =>
49
+ this.triggerHandler(this.EVENT_RECONNECTING, error)
50
+ );
51
+ this.connection.onreconnected((connectionId) =>
52
+ this.triggerHandler(this.EVENT_RECONNECTED, connectionId)
53
+ );
54
+ }
55
+
56
+ async connect() {
57
+ try {
58
+ this.isConnectionStarted = this.connection.start();
59
+ await this.isConnectionStarted;
60
+ } catch (e) {
61
+ console.log("Could not start SignalR.");
62
+ if (!this.signalRAccessTokenRefresh) {
63
+ this.signalRAccessTokenRefresh = true;
31
64
  this.isConnectionStarted = this.connection.start();
32
-
33
- this.connection.onclose(error => this.triggerHandler(this.EVENT_CLOSE, error));
34
- this.connection.onreconnecting(error => this.triggerHandler(this.EVENT_RECONNECTING, error));
35
- this.connection.onreconnected(connectionId => this.triggerHandler(this.EVENT_RECONNECTED, connectionId));
36
-
65
+ await this.isConnectionStarted;
66
+ }
37
67
  }
38
68
 
39
- async get(url: string, retry: boolean = true): Promise<Response> {
40
- //const token = await this.tokenFactoryInternal();
41
- //console.log("GET:", url, " - t:", token);
42
- const response = await fetch(this.url + url, {
43
- headers: {
44
- "content-type": "application/json",
45
- "Authorization": "Bearer " + await this.tokenFactoryInternal()
46
- }
47
- });
48
-
49
- if (!response.ok) {
50
- if ((response.status === 401 || response.status === 403) && retry) {
51
- await this.tokenFactoryInternal(true);
52
- return await this.get(url, false);
53
- }
54
-
55
- console.error(`Error calling endpoint ${url}`, response)
56
- }
57
-
58
- return response;
69
+ this.signalRAccessTokenRefresh = false;
70
+ console.log("SignalR connected.");
71
+ }
72
+
73
+ async get(url: string, retry: boolean = true): Promise<Response> {
74
+ //const token = await this.tokenFactoryInternal();
75
+ //console.log("GET:", url, " - t:", token);
76
+ const response = await fetch(this.url + url, {
77
+ headers: {
78
+ "content-type": "application/json",
79
+ Authorization: "Bearer " + (await this.tokenFactoryInternal()),
80
+ },
81
+ });
82
+
83
+ if (!response.ok) {
84
+ if ((response.status === 401 || response.status === 403) && retry) {
85
+ await this.tokenFactoryInternal(true);
86
+ return await this.get(url, false);
87
+ }
88
+
89
+ console.error(`Error calling endpoint ${url}`, response);
59
90
  }
60
91
 
61
- async post(url: string, method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", body: string | FormData, contentType: string = "application/json", retry: boolean = true): Promise<Response> {
62
- let headers: HeadersInit = {
63
- "Authorization": "Bearer " + await this.tokenFactoryInternal()
64
- };
65
-
66
- if(contentType !== ""){
67
- headers["content-type"] = contentType
68
- }
69
- const response = await fetch(this.url + url, {
70
- method: method,
71
- body: body,
72
- headers: headers
73
- });
74
-
75
- if (!response.ok) {
76
- if ((response.status === 401 || response.status === 403) && retry) {
77
- await this.tokenFactoryInternal(true);
78
- return await this.post(url, method, body, contentType, false);
79
- }
80
-
81
- //console.error(`Error calling endpoint ${url}`, response)
82
- }
83
-
84
- return response;
92
+ return response;
93
+ }
94
+
95
+ async post(
96
+ url: string,
97
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
98
+ body: string | FormData,
99
+ contentType: string = "application/json",
100
+ retry: boolean = true
101
+ ): Promise<Response> {
102
+ let headers: HeadersInit = {
103
+ Authorization: "Bearer " + (await this.tokenFactoryInternal()),
85
104
  };
86
105
 
87
-
88
- async upload (url: string, method: "POST" | "PUT" | "PATCH", body: string | FormData, contentType: string = "application/json", onProgress?: (progress: number) => void, retry: boolean = true) {
89
- const client = this as WeavyClient;
90
- const token = await client.tokenFactoryInternal();
91
-
92
- return await new Promise<Response>(function (resolve, reject) {
93
- // XMLHttpRequest instead of fetch because we want to track progress
94
- let xhr = new XMLHttpRequest();
95
- xhr.open(method, client.url + url, true);
96
- xhr.setRequestHeader("Authorization", "Bearer " + token)
97
- if (contentType !== ""){
98
- xhr.setRequestHeader("content-type", contentType);
99
- }
100
- if (onProgress) {
101
- xhr.upload.addEventListener("progress", (e: ProgressEvent<EventTarget>) => {
102
- onProgress((e.loaded / e.total) * 100 || 100)
103
- })
104
- }
105
-
106
- xhr.onload = (evt: ProgressEvent<EventTarget>) => {
107
- if (retry && (xhr.status === 401 || xhr.status === 401)) {
108
- client.tokenFactoryInternal(true)
109
- .then(() => client.upload(url, method, body, contentType, onProgress, false))
110
- .then(resolve)
111
- .catch(reject);
112
- } else {
113
- resolve(new Response(xhr.response, { status: xhr.status, statusText: xhr.statusText }));
114
- }
115
- };
116
- xhr.onerror = reject;
117
- xhr.send(body);
118
- });
106
+ if (contentType !== "") {
107
+ headers["content-type"] = contentType;
119
108
  }
120
-
121
- async getToken(refresh: boolean) {
122
- if (!this.token || refresh) {
123
- this.token = await this.tokenFactory(true);
124
- }
125
- return this.token;
109
+ const response = await fetch(this.url + url, {
110
+ method: method,
111
+ body: body,
112
+ headers: headers,
113
+ });
114
+
115
+ if (!response.ok) {
116
+ if ((response.status === 401 || response.status === 403) && retry) {
117
+ await this.tokenFactoryInternal(true);
118
+ return await this.post(url, method, body, contentType, false);
119
+ }
120
+
121
+ //console.error(`Error calling endpoint ${url}`, response)
126
122
  }
127
123
 
128
- async tokenFactoryInternal(refresh: boolean = false, fromSR: boolean = false): Promise<string> {
129
-
130
- if(this.token && !refresh) return this.token;
131
-
132
- if(!this.tokenPromise){
133
-
134
- this.tokenPromise = this.tokenFactory(refresh);
135
- let token = await this.tokenPromise;
136
-
137
- this.tokenPromise = null;
138
- this.token = token;
139
- return this.token;
140
- } else{
141
- //console.log("Already a promise in action, wait for it to resolve...")
142
- return this.tokenPromise;
124
+ return response;
125
+ }
126
+
127
+ async upload(
128
+ url: string,
129
+ method: "POST" | "PUT" | "PATCH",
130
+ body: string | FormData,
131
+ contentType: string = "application/json",
132
+ onProgress?: (progress: number) => void,
133
+ retry: boolean = true
134
+ ) {
135
+ const client = this as WeavyClient;
136
+ const token = await client.tokenFactoryInternal();
137
+
138
+ return await new Promise<Response>(function (resolve, reject) {
139
+ // XMLHttpRequest instead of fetch because we want to track progress
140
+ let xhr = new XMLHttpRequest();
141
+ xhr.open(method, client.url + url, true);
142
+ xhr.setRequestHeader("Authorization", "Bearer " + token);
143
+ if (contentType !== "") {
144
+ xhr.setRequestHeader("content-type", contentType);
145
+ }
146
+ if (onProgress) {
147
+ xhr.upload.addEventListener(
148
+ "progress",
149
+ (e: ProgressEvent<EventTarget>) => {
150
+ onProgress((e.loaded / e.total) * 100 || 100);
151
+ }
152
+ );
153
+ }
154
+
155
+ xhr.onload = (evt: ProgressEvent<EventTarget>) => {
156
+ if (retry && (xhr.status === 401 || xhr.status === 401)) {
157
+ client
158
+ .tokenFactoryInternal(true)
159
+ .then(() =>
160
+ client.upload(url, method, body, contentType, onProgress, false)
161
+ )
162
+ .then(resolve)
163
+ .catch(reject);
164
+ } else {
165
+ resolve(
166
+ new Response(xhr.response, {
167
+ status: xhr.status,
168
+ statusText: xhr.statusText,
169
+ })
170
+ );
143
171
  }
172
+ };
173
+ xhr.onerror = reject;
174
+ xhr.send(body);
175
+ });
176
+ }
177
+
178
+ async getToken(refresh: boolean) {
179
+ if (!this.token || refresh) {
180
+ this.token = await this.tokenFactory(true);
144
181
  }
182
+ return this.token;
183
+ }
184
+
185
+ async tokenFactoryInternal(refresh: boolean = false): Promise<string> {
186
+ if (this.token && !refresh) return this.token;
187
+
188
+ if (!this.tokenPromise) {
189
+ this.tokenPromise = this.tokenFactory(refresh);
190
+ let token = await this.tokenPromise;
191
+
192
+ this.tokenPromise = null;
193
+ this.token = token;
194
+ return this.token;
195
+ } else {
196
+ //console.log("Already a promise in action, wait for it to resolve...")
197
+ return this.tokenPromise;
198
+ }
199
+ }
200
+
201
+ async subscribe(group: string, event: string, callback: any) {
202
+ await this.isConnectionStarted;
203
+
204
+ try {
205
+ var name = group ? group + ":" + event : event;
206
+ await this.connection.invoke("Subscribe", name);
207
+ this.groups.push(name);
208
+ this.connection.on(name, callback);
209
+ } catch (err: any) {
210
+ console.warn("Error in Subscribe:", err);
211
+ }
212
+ }
145
213
 
146
- async subscribe(group: string, event: string, callback: any) {
147
- await this.isConnectionStarted;
148
-
149
- try {
150
- var name = group ? group + ":" + event : event;
151
- await this.connection.invoke("Subscribe", name);
152
- this.groups.push(name);
153
- this.connection.on(name, callback);
154
- } catch (err: any) {
155
- console.warn("Error in Subscribe:", err)
156
- }
214
+ async unsubscribe(group: string, event: string, callback: any) {
215
+ await this.isConnectionStarted;
216
+ var name = group ? group + ":" + event : event;
157
217
 
158
- }
218
+ // get first occurence of group name and remove it
219
+ const index = this.groups.findIndex((e) => e === name);
220
+ if (index !== -1) {
221
+ this.groups = this.groups.splice(index, 1);
159
222
 
160
- async unsubscribe(group: string, event: string, callback: any) {
161
- await this.isConnectionStarted;
162
- var name = group ? group + ":" + event : event;
163
-
164
- // get first occurence of group name and remove it
165
- const index = this.groups.findIndex(e => e === name);
166
- if (index !== -1) {
167
- this.groups = this.groups.splice(index, 1);
168
-
169
- try {
170
- // if no more groups, remove from server
171
- if (!this.groups.find(e => e === name)) {
172
- await this.connection.invoke("Unsubscribe", name);
173
- }
174
-
175
- } catch (err: any) {
176
- console.warn("Error in Unsubscribe:", err)
177
- }
223
+ try {
224
+ // if no more groups, remove from server
225
+ if (!this.groups.find((e) => e === name)) {
226
+ await this.connection.invoke("Unsubscribe", name);
178
227
  }
179
-
180
- this.connection.off(name, callback);
228
+ } catch (err: any) {
229
+ console.warn("Error in Unsubscribe:", err);
230
+ }
181
231
  }
182
232
 
183
- destroy(){
184
- this.connection.stop();
185
- }
233
+ this.connection.off(name, callback);
234
+ }
186
235
 
187
- triggerHandler(name: string, ...data: any) {
188
- name = name.endsWith(this.EVENT_NAMESPACE) ? name : name + this.EVENT_NAMESPACE;
189
- let event = new CustomEvent(name, { cancelable: false });
236
+ destroy() {
237
+ this.connection.stop();
238
+ }
190
239
 
191
- console.debug("triggerHandler", name);
240
+ triggerHandler(name: string, ...data: any) {
241
+ name = name.endsWith(this.EVENT_NAMESPACE)
242
+ ? name
243
+ : name + this.EVENT_NAMESPACE;
244
+ let event = new CustomEvent(name, { cancelable: false });
192
245
 
193
- this.connectionEvents.forEach((eventHandler) => {
194
- if (eventHandler.name === name) {
195
- eventHandler.handler(event, ...data);
196
- }
197
- });
246
+ console.debug("triggerHandler", name);
198
247
 
199
- if (name === this.EVENT_RECONNECTED + this.EVENT_NAMESPACE) {
200
- // re-add to signalr groups after reconnect
201
- for (var i = 0; i < this.groups.length; i++) {
202
- this.connection.invoke("Subscribe", this.groups[i]);
203
- }
204
- }
248
+ this.connectionEvents.forEach((eventHandler) => {
249
+ if (eventHandler.name === name) {
250
+ eventHandler.handler(event, ...data);
251
+ }
252
+ });
253
+
254
+ if (name === this.EVENT_RECONNECTED + this.EVENT_NAMESPACE) {
255
+ // re-add to signalr groups after reconnect
256
+ for (var i = 0; i < this.groups.length; i++) {
257
+ this.connection.invoke("Subscribe", this.groups[i]);
258
+ }
205
259
  }
260
+ }
206
261
  }