@reactoo/watchtogether-sdk-js 2.7.74 → 2.7.76

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.
@@ -29,7 +29,7 @@
29
29
 
30
30
  <script type="module">
31
31
  import WatchTogetherSDK from '../dist/watchtogether-sdk.js';
32
-
32
+
33
33
  //https://studio.reactoo.com/room/edf441b3-7415-49c4-9557-273cb93bc746/LJj4W2Cz-nG3U-lb0R-TAaY-o7Thmb8xHSbE
34
34
 
35
35
  let roomId = "d841ad58-e5a6-4cb8-aeb8-c92499a213a4"; // It will create room automatically if not set
@@ -521,4 +521,4 @@
521
521
  </script>
522
522
 
523
523
  </body>
524
- </html>
524
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactoo/watchtogether-sdk-js",
3
- "version": "2.7.74",
3
+ "version": "2.7.76",
4
4
  "description": "Javascript SDK for Reactoo",
5
5
  "main": "dist/watchtogether-sdk.min.js",
6
6
  "module": "dist/watchtogether-sdk.min.js",
package/src/models/iot.js CHANGED
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  let iot = function () {
4
+
4
5
  let __currentTopics = new Set();
5
6
  let visibilityChangeHandler = null;
6
7
  let keepAliveIntervalId = null;
7
8
  let shouldBeConnected = false;
8
- let isReconnecting = false;
9
+ let isConnecting = false;
9
10
  let interruptCount = 0;
10
11
  let subscriptionFailureCount = 0;
11
12
 
@@ -20,12 +21,18 @@ let iot = function () {
20
21
  },
21
22
 
22
23
  __updateCredentials: () => {
24
+
25
+ if(isConnecting) {
26
+ this.__privates.iot.log('Already reconnecting, ignoring update credentials');
27
+ return Promise.resolve(this.iot.__promise);
28
+ }
29
+
23
30
  this.__privates.iot.log('Updating Credentials...');
24
31
  interruptCount = 0;
25
32
  subscriptionFailureCount = 0;
26
- isReconnecting = true;
27
- this.iot.$emit('isReconnecting', isReconnecting, true);
28
- return this.iot.getCredentials()
33
+ isConnecting = true;
34
+
35
+ this.iot.__promise = this.iot.getCredentials()
29
36
  .then(response => this.__privates.iot.updateWebSocketCredentials(
30
37
  response.data.credentials.accessKeyId,
31
38
  response.data.credentials.secretAccessKey,
@@ -33,23 +40,22 @@ let iot = function () {
33
40
  response.data.credentials.expiration
34
41
  ))
35
42
  .then((r) => {
36
- isReconnecting = false;
37
- this.iot.$emit('isReconnecting', isReconnecting, false);
43
+ isConnecting = false;
38
44
  this.__privates.iot.log('Credentials updated');
39
45
  return Promise.resolve(r);
40
46
  })
41
47
  .catch(error => {
42
- isReconnecting = false;
43
- this.iot.$emit('isReconnecting', isReconnecting, true);
48
+ isConnecting = false;
44
49
  this.__privates.iot.log('Failed to update credentials:', error);
45
50
  return Promise.reject(error);
46
51
  });
52
+
53
+ return this.iot.__promise;
47
54
  },
48
55
 
49
56
  getCredentials: () => {
50
57
  return this.__privates.auth.__client
51
58
  .then(client => client.apis.auth.iotSignIn({}, {requestBody: {suggestedTopics:true, domain: location.hostname}}))
52
-
53
59
  },
54
60
 
55
61
  onConnect: (data, connectionId) => {
@@ -57,7 +63,7 @@ let iot = function () {
57
63
  },
58
64
  onClosed: (data, connectionId) => {
59
65
  this.__privates.iot.log('MQTT client closed');
60
- if(shouldBeConnected && !isReconnecting) {
66
+ if(shouldBeConnected && !isConnecting) {
61
67
  this.__privates.iot.log('Connection unexpectedly closed, reconnecting...');
62
68
  this.iot.__createNewWorker();
63
69
  this.iot.__updateCredentials();
@@ -70,7 +76,7 @@ let iot = function () {
70
76
  this.__privates.iot.log('MQTT client disconnect');
71
77
  },
72
78
  onInterrupt: (data, connectionId) => {
73
- if(shouldBeConnected && !isReconnecting && interruptCount > 10) {
79
+ if(shouldBeConnected && !isConnecting && interruptCount > 10) {
74
80
  this.__privates.iot.log('Interrupt count exceeded, reconnecting...');
75
81
  this.iot.__createNewWorker();
76
82
  this.iot.__updateCredentials();
@@ -85,7 +91,7 @@ let iot = function () {
85
91
  this.__privates.iot.log('MQTT client connection_success');
86
92
  },
87
93
  onConnectionFailure: (data, connectionId) => {
88
- if(shouldBeConnected && !isReconnecting && interruptCount > 10) {
94
+ if(shouldBeConnected && !isConnecting && interruptCount > 10) {
89
95
  this.__privates.iot.log('Interrupt count exceeded, reconnecting...');
90
96
  this.iot.__createNewWorker();
91
97
  this.iot.__updateCredentials();
@@ -96,10 +102,17 @@ let iot = function () {
96
102
 
97
103
  iotLogin: (subscribeToSuggestedTopics = true) => {
98
104
 
105
+ if(isConnecting) {
106
+ this.__privates.iot.log('Already reconnecting, ignoring login');
107
+ return this.iot.__promise;
108
+ }
109
+
99
110
  interruptCount = 0;
100
111
  subscriptionFailureCount = 0;
101
112
  shouldBeConnected = true;
102
- this.__privates.iot.initWorker();
113
+ isConnecting = true;
114
+
115
+ this.iot.__createNewWorker();
103
116
  this.iot.__promise = new Promise((resolve, reject) => {
104
117
  this.iot.getCredentials()
105
118
  .then(response => {
@@ -116,8 +129,14 @@ let iot = function () {
116
129
  )
117
130
  ])
118
131
  })
119
- .then(resolve)
120
- .catch(reject)
132
+ .then((r) => {
133
+ isConnecting = false;
134
+ resolve(r);
135
+ })
136
+ .catch((e) => {
137
+ isConnecting = false;
138
+ reject(e);
139
+ })
121
140
  });
122
141
 
123
142
  __currentTopics.clear();
@@ -200,14 +219,14 @@ let iot = function () {
200
219
  __currentTopics.delete(topic);
201
220
 
202
221
  if(error?.cause === -1) {
203
- return Promise.reject('invalid_topic');
222
+ return Promise.reject('invalid_topic or worker not initialized');
204
223
  }
205
224
 
206
- if(!shouldBeConnected || isReconnecting) {
225
+ if(!shouldBeConnected || isConnecting) {
207
226
  return Promise.reject('subscription_failed');
208
227
  }
209
228
 
210
- if(shouldBeConnected && !isReconnecting && subscriptionFailureCount > 5) {
229
+ if(shouldBeConnected && !isConnecting && subscriptionFailureCount > 5) {
211
230
  this.__privates.iot.log('Subscription failure exceeded, reconnecting...');
212
231
  this.iot.__createNewWorker();
213
232
  this.iot.__updateCredentials();
@@ -268,7 +287,7 @@ let iot = function () {
268
287
  return Promise.resolve();
269
288
  }
270
289
 
271
- if(isReconnecting) {
290
+ if(isConnecting) {
272
291
  this.__privates.iot.log('Connection check failed, but we are already reconnecting...');
273
292
  return Promise.resolve();
274
293
  }
@@ -293,7 +312,7 @@ let iot = function () {
293
312
  return;
294
313
  }
295
314
 
296
- if(shouldBeConnected && !isReconnecting) {
315
+ if(shouldBeConnected && !isConnecting) {
297
316
  this.__privates.iot.log('Keepalive failed:', error);
298
317
  this.iot.__createNewWorker();
299
318
  this.iot.__updateCredentials();
@@ -36,12 +36,12 @@ let user = function () {
36
36
  .then(response => response.data.key)
37
37
  ;
38
38
  },
39
- uploadVideo: (roomId, files, privateAttributes = {}, customPrice) => {
39
+ uploadVideo: (roomId, files, privateAttributes = {}, customAttributes = {}, customPrice) => {
40
40
  let id = generateUUID();
41
41
  return this.__privates.auth.__client
42
42
  .then(client => Promise.all([client, client.apis.video.initiateVideoUpload({id, segmentCount: files.length || 0})]))
43
43
  .then(([client, response]) => Promise.all([client, response.data.signedUrlSegments.reduce((acc, url, index) => acc.then(() => client.http({url, method: response.data.httpMethod, headers: {"Content-Type":files[index].type}, body:files[index]})), Promise.resolve())]))
44
- .then(([client]) => client.apis.video.publishVideo({_id:id, roomId, customPrice}, {requestBody:{_id:id, roomId, privateAttributes, customPrice}}))
44
+ .then(([client]) => client.apis.video.publishVideo({_id:id, roomId, customPrice}, {requestBody:{_id:id, roomId, privateAttributes, customAttributes, customPrice}}))
45
45
  },
46
46
 
47
47
  publishVideo: (roomId, id, privateAttributes = {}, customPrice) => {
@@ -93,10 +93,11 @@ let user = function () {
93
93
  items: [...chainResponse.data.items, ...response.data.items],
94
94
  size: chainResponse.data.size + response.data.size,
95
95
  startKey: null,
96
+ users: [...chainResponse.data.users, ...(response.data.users ?? [])],
96
97
  },
97
98
  } : response);
98
99
  });
99
- }, Promise.resolve({data: {items: [], size: 0, startKey: null}}));
100
+ }, Promise.resolve({data: {items: [], size: 0, startKey: null, users: []}}));
100
101
  },
101
102
 
102
103
  deleteVideo: (id) => {
@@ -0,0 +1,191 @@
1
+ import { mqtt5, iot } from 'aws-iot-device-sdk-v2';
2
+ console.log('Worker: Starting up');
3
+
4
+ let connection = null;
5
+ let currentConnectionId = 1;
6
+
7
+ self.onmessage = function(event) {
8
+ const { type, params, topic, message, stamp } = event.data;
9
+ console.log(`Worker: Received message of type: ${type}`);
10
+
11
+ switch (type) {
12
+ case 'connect':
13
+ connect(params, stamp);
14
+ break;
15
+ case 'disconnect':
16
+ disconnect(stamp);
17
+ break;
18
+ case 'is_connected':
19
+ isConnected();
20
+ break;
21
+ case 'clear_topics':
22
+ // No action needed in the worker
23
+ break;
24
+ case 'subscribe':
25
+ subscribe(topic, stamp);
26
+ break;
27
+ case 'unsubscribe':
28
+ unsubscribe(topic, stamp);
29
+ break;
30
+ case 'send':
31
+ send(topic, message);
32
+ break;
33
+ }
34
+ };
35
+
36
+ function connect(params, stamp) {
37
+ console.log('Worker: Attempting to connect');
38
+ const { apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken } = params;
39
+
40
+ const configBuilder = iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets();
41
+
42
+ configBuilder.with_clean_session(true);
43
+ configBuilder.with_client_id(apiMqttClientId);
44
+ configBuilder.with_endpoint(apiMqttUrl);
45
+ configBuilder.with_credentials(region, accessKeyId, secretAccessKey, sessionToken);
46
+ configBuilder.with_keep_alive_seconds(30);
47
+ configBuilder.with_ping_timeout_ms(3000);
48
+ configBuilder.with_reconnect_max_sec(5);
49
+ configBuilder.with_reconnect_min_sec(1);
50
+
51
+ const config = configBuilder.build();
52
+ const client = new mqtt5.MqttClient(); // Use MQTT 5.0 client
53
+
54
+ connection = client.new_connection(config);
55
+ currentConnectionId++;
56
+
57
+ setupConnectionListeners(currentConnectionId);
58
+
59
+ connection.connect()
60
+ .then(() => {
61
+ console.log('Worker: Connection successful');
62
+ connection.start(); // Start the client to maintain connectivity
63
+ self.postMessage({ type: 'connect_result', data: {success: true, connectionId: currentConnectionId, stamp} });
64
+ })
65
+ .catch((error) => {
66
+ console.error('Worker: Connection failed', error);
67
+ self.postMessage({ type: 'connect_result', data: {success: false, error: error.message, stamp} });
68
+ });
69
+ }
70
+
71
+ function disconnect(stamp) {
72
+ if (connection) {
73
+ const connectionId = currentConnectionId;
74
+ connection.stop(); // Stop the client before disconnecting
75
+ connection.disconnect()
76
+ .then(() => {
77
+ if (connectionId !== currentConnectionId) {
78
+ console.log('Worker: Connection Id mismatch, ignoring disconnect result', connectionId, currentConnectionId);
79
+ return;
80
+ }
81
+ self.postMessage({ type: 'disconnect_result', data: {success: true, stamp} });
82
+ })
83
+ .catch((error) => {
84
+ if (connectionId !== currentConnectionId) {
85
+ console.log('Worker: Connection Id mismatch, ignoring disconnect result', connectionId, currentConnectionId);
86
+ return;
87
+ }
88
+ self.postMessage({ type: 'disconnect_result', data: { success: false, error: error.message, stamp} });
89
+ });
90
+ connection = null;
91
+ } else {
92
+ self.postMessage({ type: 'disconnect_result', data: {success: true, stamp} });
93
+ }
94
+ }
95
+
96
+ function isConnected() {
97
+ const connected = connection && connection.currentState === 0 && connection.desiredState === 0;
98
+ self.postMessage({ type: 'is_connected_result', data:{connected} });
99
+ }
100
+
101
+ function subscribe(topic, stamp) {
102
+ if (connection && connection.currentState === 0 && connection.desiredState === 0) {
103
+ connection.subscribe(topic, mqtt5.QoS.AtLeastOnce)
104
+ .then(() => {
105
+ self.postMessage({ type: 'subscribe_result', data: {success: true, stamp} });
106
+ })
107
+ .catch((error) => {
108
+ self.postMessage({ type: 'subscribe_result', data: {success: false, error: error.message, stamp} });
109
+ });
110
+ } else {
111
+ self.postMessage({ type: 'subscribe_result', data: {success: false, error: 'Not connected', stamp} });
112
+ }
113
+ }
114
+
115
+ function unsubscribe(topic, stamp) {
116
+ if (connection && connection.currentState === 0 && connection.desiredState === 0) {
117
+ connection.unsubscribe(topic)
118
+ .then(() => {
119
+ self.postMessage({ type: 'unsubscribe_result', data: {success: true, stamp} });
120
+ })
121
+ .catch((error) => {
122
+ self.postMessage({ type: 'unsubscribe_result', data: {success: false, error: error.message, stamp} });
123
+ });
124
+ } else {
125
+ self.postMessage({ type: 'unsubscribe_result', data: {success: false, error: 'Not connected', stamp} });
126
+ }
127
+ }
128
+
129
+ function send(topic, message) {
130
+ if (connection && connection.currentState === 0 && connection.desiredState === 0) {
131
+ connection.publish(topic, message, mqtt5.QoS.AtLeastOnce, false, {
132
+ // Example of adding user properties
133
+ userProperties: {
134
+ // Add your properties here
135
+ }
136
+ });
137
+ } else {
138
+ console.error('Cannot send message: Not connected');
139
+ }
140
+ }
141
+
142
+ function setupConnectionListeners(connectionId) {
143
+ connection.on(mqtt5.Mqtt5Client.ATTEMPTING_CONNECT, () => {
144
+ console.log('Worker: Attempting to connect');
145
+ });
146
+
147
+ connection.on(mqtt5.Mqtt5Client.CONNECTION_SUCCESS, (event) => {
148
+ if (connectionId !== currentConnectionId) {
149
+ console.log('Worker: Connection Id mismatch, ignoring connection success event', connectionId, currentConnectionId);
150
+ return;
151
+ }
152
+ self.postMessage({ type: 'connect', connectionId: connectionId, settings: event.settings });
153
+ });
154
+
155
+ connection.on(mqtt5.Mqtt5Client.CONNECTION_FAILURE, (event) => {
156
+ if (connectionId !== currentConnectionId) {
157
+ console.log('Worker: Connection Id mismatch, ignoring connection failure event', connectionId, currentConnectionId);
158
+ return;
159
+ }
160
+ self.postMessage({ type: 'connection_failure', error: event.error });
161
+ });
162
+
163
+ connection.on(mqtt5.Mqtt5Client.DISCONNECTION, (event) => {
164
+ if (connectionId !== currentConnectionId) {
165
+ console.log('Worker: Connection Id mismatch, ignoring disconnection event', connectionId, currentConnectionId);
166
+ return;
167
+ }
168
+ self.postMessage({ type: 'disconnect', error: event.error });
169
+ });
170
+
171
+ connection.on(mqtt5.Mqtt5Client.MESSAGE_RECEIVED, (event) => {
172
+ if (connectionId !== currentConnectionId) {
173
+ console.log('Worker: Connection Id mismatch, ignoring message event', connectionId, currentConnectionId);
174
+ return;
175
+ }
176
+ self.postMessage({ type: 'message', data: event.message });
177
+ });
178
+
179
+ // Additional events can be added as needed
180
+ }
181
+
182
+ // Ensure to call close when the worker is terminated or no longer needed
183
+ function cleanup() {
184
+ if (connection) {
185
+ connection.close(); // Clean up resources
186
+ connection = null;
187
+ }
188
+ }
189
+
190
+ // Add this at the end of the file
191
+ console.log('Worker: Setup complete');
@@ -12,6 +12,7 @@ class Iot {
12
12
  this.lastConnectParams = null;
13
13
  this.subscribedTopics = new Set();
14
14
  this.abortController = null;
15
+ this.worker = null;
15
16
 
16
17
  if (enableDebugFlag) {
17
18
  this.enableDebug();
@@ -103,12 +104,10 @@ class Iot {
103
104
  const stamp = new Date().getTime();
104
105
 
105
106
  const handleDisconnectResult = (event) => {
106
-
107
107
  if(event.stamp !== stamp) {
108
108
  this.log('disconnect event stamp mismatch', event.stamp, stamp);
109
109
  return;
110
110
  }
111
-
112
111
  clearTimeout(timeoutId);
113
112
  this.off('worker:disconnect_result', handleDisconnectResult);
114
113
  if (event.success) {
@@ -155,6 +154,10 @@ class Iot {
155
154
  this.log('iot subscribe', topic);
156
155
  if (typeof topic === 'string' && topic.trim() !== '') {
157
156
 
157
+ if(!this.subscribedTopics.has(topic)) {
158
+ this.subscribedTopics.add(topic);
159
+ }
160
+
158
161
  if(!this.worker) {
159
162
  this.log('Worker not initialized');
160
163
  return Promise.reject(new Error('Worker not initialized', {cause: -1}));
@@ -171,7 +174,7 @@ class Iot {
171
174
 
172
175
  this.off('worker:subscribe_result', handleSubscribeResult);
173
176
  if (event.success) {
174
- this.subscribedTopics.add(topic);
177
+
175
178
  resolve();
176
179
  } else {
177
180
  reject(new Error(event.error));
@@ -190,7 +193,7 @@ class Iot {
190
193
  unsubscribe(topic) {
191
194
  this.log('iot unsubscribe', topic);
192
195
  if (typeof topic === 'string' && topic.trim() !== '') {
193
-
196
+ this.subscribedTopics.delete(topic);
194
197
  if(!this.worker) {
195
198
  this.log('Worker not initialized');
196
199
  return Promise.reject(new Error('Worker not initialized', {cause: -1}));
@@ -209,7 +212,6 @@ class Iot {
209
212
 
210
213
  this.off('worker:unsubscribe_result', handleUnsubscribeResult);
211
214
  if (event.success) {
212
- this.subscribedTopics.delete(topic);
213
215
  resolve();
214
216
  } else {
215
217
  reject(new Error(event.error));
@@ -391,19 +393,15 @@ class Iot {
391
393
  reject(new Error('No subscribed topics available for connection check', {cause: -1}));
392
394
  return;
393
395
  }
394
-
395
396
  let suitableTopic = Array.from(this.subscribedTopics).find(topic => topic.indexOf('user') > -1);
396
-
397
397
  if (!suitableTopic) {
398
398
  reject(new Error('No suitable topic found for connection check', {cause: -1}));
399
399
  return;
400
400
  }
401
-
402
401
  const testMessage = {
403
402
  type: 'keep_alive',
404
403
  timestamp: Date.now()
405
404
  };
406
-
407
405
  const timeoutId = setTimeout(() => {
408
406
  this.off('message', checkMessageHandler);
409
407
  this.abortController.signal.removeEventListener('abort', abort);