@stream-io/video-client 0.6.3 → 0.6.5

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ### [0.6.5](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-0.6.4...@stream-io/video-client-0.6.5) (2024-03-29)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * various bug fixes and improvements ([#1300](https://github.com/GetStream/stream-video-js/issues/1300)) ([a6186e2](https://github.com/GetStream/stream-video-js/commit/a6186e2406fd0b3e0aaa51a4222fa2e24e9dfac3))
11
+
12
+ ### [0.6.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-0.6.3...@stream-io/video-client-0.6.4) (2024-03-28)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **react-native:** improve error logging for speaker manager hook and improve usage of incall manager in SDK ([#1299](https://github.com/GetStream/stream-video-js/issues/1299)) ([9527c41](https://github.com/GetStream/stream-video-js/commit/9527c4176d4e46224ddec18e3fddfb404e0aaae5))
18
+
5
19
  ### [0.6.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-0.6.2...@stream-io/video-client-0.6.3) (2024-03-25)
6
20
 
7
21
 
@@ -6584,6 +6584,14 @@ var CallingState;
6584
6584
  */
6585
6585
  CallingState["OFFLINE"] = "offline";
6586
6586
  })(CallingState || (CallingState = {}));
6587
+ /**
6588
+ * Returns the default egress object - when no egress data is available.
6589
+ */
6590
+ const defaultEgress = {
6591
+ broadcasting: false,
6592
+ hls: { playlist_url: '' },
6593
+ rtmps: [],
6594
+ };
6587
6595
  /**
6588
6596
  * Holds the state of the current call.
6589
6597
  * @react You don't have to use this class directly, as we are exposing the state through Hooks.
@@ -6898,19 +6906,19 @@ class CallState {
6898
6906
  ]);
6899
6907
  };
6900
6908
  this.updateFromHLSBroadcastStopped = () => {
6901
- this.setCurrentValue(this.egressSubject, (egress) => ({
6909
+ this.setCurrentValue(this.egressSubject, (egress = defaultEgress) => ({
6902
6910
  ...egress,
6903
6911
  broadcasting: false,
6904
6912
  }));
6905
6913
  };
6906
6914
  this.updateFromHLSBroadcastingFailed = () => {
6907
- this.setCurrentValue(this.egressSubject, (egress) => ({
6915
+ this.setCurrentValue(this.egressSubject, (egress = defaultEgress) => ({
6908
6916
  ...egress,
6909
6917
  broadcasting: false,
6910
6918
  }));
6911
6919
  };
6912
6920
  this.updateFromHLSBroadcastStarted = (event) => {
6913
- this.setCurrentValue(this.egressSubject, (egress) => ({
6921
+ this.setCurrentValue(this.egressSubject, (egress = defaultEgress) => ({
6914
6922
  ...egress,
6915
6923
  broadcasting: true,
6916
6924
  hls: {
@@ -7352,7 +7360,9 @@ class StreamVideoWriteableStateStore {
7352
7360
  if (call.state.callingState === CallingState.LEFT)
7353
7361
  continue;
7354
7362
  logger('info', `User disconnected, leaving call: ${call.cid}`);
7355
- await call.leave().catch((err) => {
7363
+ await call
7364
+ .leave({ reason: 'client.disconnectUser() called' })
7365
+ .catch((err) => {
7356
7366
  logger('error', `Error leaving call: ${call.cid}`, err);
7357
7367
  });
7358
7368
  }
@@ -8604,10 +8614,10 @@ class StreamSfuClient {
8604
8614
  this.isFastReconnecting = false;
8605
8615
  this.pingIntervalInMs = 10 * 1000;
8606
8616
  this.unhealthyTimeoutInMs = this.pingIntervalInMs + 5 * 1000;
8607
- this.close = (code = StreamSfuClient.NORMAL_CLOSURE, reason = 'js-client: requested signal connection close') => {
8608
- this.logger('debug', 'Closing SFU WS connection', code, reason);
8617
+ this.close = (code, reason) => {
8618
+ this.logger('debug', `Closing SFU WS connection: ${code} - ${reason}`);
8609
8619
  if (this.signalWs.readyState !== this.signalWs.CLOSED) {
8610
- this.signalWs.close(code, reason);
8620
+ this.signalWs.close(code, `js-client: ${reason}`);
8611
8621
  }
8612
8622
  this.unsubscribeIceTrickle();
8613
8623
  clearInterval(this.keepAliveInterval);
@@ -8856,13 +8866,13 @@ const watchCallRejected = (call) => {
8856
8866
  .every((m) => rejectedBy[m.user_id]);
8857
8867
  if (everyoneElseRejected) {
8858
8868
  call.logger('info', 'everyone rejected, leaving the call');
8859
- await call.leave();
8869
+ await call.leave({ reason: 'ring: everyone rejected' });
8860
8870
  }
8861
8871
  }
8862
8872
  else {
8863
8873
  if (rejectedBy[eventCall.created_by.id]) {
8864
8874
  call.logger('info', 'call creator rejected, leaving call');
8865
- await call.leave();
8875
+ await call.leave({ reason: 'ring: creator rejected' });
8866
8876
  }
8867
8877
  }
8868
8878
  };
@@ -8876,7 +8886,7 @@ const watchCallEnded = (call) => {
8876
8886
  if (callingState === CallingState.RINGING ||
8877
8887
  callingState === CallingState.JOINED ||
8878
8888
  callingState === CallingState.JOINING) {
8879
- await call.leave();
8889
+ await call.leave({ reason: 'call.ended event received' });
8880
8890
  }
8881
8891
  };
8882
8892
  };
@@ -8952,7 +8962,7 @@ const watchLiveEnded = (dispatcher, call) => {
8952
8962
  if (e.error && e.error.code !== ErrorCode.LIVE_ENDED)
8953
8963
  return;
8954
8964
  if (!call.permissionsContext.hasPermission(OwnCapability.JOIN_BACKSTAGE)) {
8955
- call.leave().catch((err) => {
8965
+ call.leave({ reason: 'live ended' }).catch((err) => {
8956
8966
  logger$1('error', 'Failed to leave call after live ended', err);
8957
8967
  });
8958
8968
  }
@@ -10385,12 +10395,12 @@ class InputMediaDeviceManager {
10385
10395
  /**
10386
10396
  * Selects a device.
10387
10397
  *
10388
- * Note: this method is not supported in React Native
10398
+ * Note: This method is not supported in React Native
10389
10399
  * @param deviceId the device id to select.
10390
10400
  */
10391
10401
  async select(deviceId) {
10392
10402
  if (isReactNative()) {
10393
- throw new Error('This method is not supported in React Native');
10403
+ throw new Error('This method is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for reference.');
10394
10404
  }
10395
10405
  if (deviceId === this.state.selectedDevice) {
10396
10406
  return;
@@ -10598,13 +10608,14 @@ class InputMediaDeviceManagerState {
10598
10608
  return notifyGranted();
10599
10609
  }
10600
10610
  let permissionState;
10601
- const notify = () => subscriber.next(
10602
- // In Safari, the `change` event doesn't reliably emit and hence,
10603
- // permissionState stays in 'prompt' state forever.
10604
- // Instead of checking if a permission is granted, we check if it isn't denied
10605
- isSafari()
10606
- ? permissionState.state !== 'denied'
10607
- : permissionState.state === 'granted');
10611
+ const notify = () => {
10612
+ subscriber.next(
10613
+ // In some browsers, the 'change' event doesn't reliably emit and hence,
10614
+ // permissionState stays in 'prompt' state forever.
10615
+ // Typically, this happens when a user grants one-time permission.
10616
+ // Instead of checking if a permission is granted, we check if it isn't denied
10617
+ permissionState.state !== 'denied');
10618
+ };
10608
10619
  navigator.permissions
10609
10620
  .query({ name: this.permissionName })
10610
10621
  .then((permissionStatus) => {
@@ -11291,22 +11302,26 @@ class SpeakerManager {
11291
11302
  * Lists the available audio output devices
11292
11303
  *
11293
11304
  * Note: It prompts the user for a permission to use devices (if not already granted)
11305
+ * Note: This method is not supported in React Native
11294
11306
  *
11295
11307
  * @returns an Observable that will be updated if a device is connected or disconnected
11296
11308
  */
11297
11309
  listDevices() {
11310
+ if (isReactNative()) {
11311
+ throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
11312
+ }
11298
11313
  return getAudioOutputDevices();
11299
11314
  }
11300
11315
  /**
11301
11316
  * Select a device.
11302
11317
  *
11303
- * Note: this method is not supported in React Native
11318
+ * Note: This method is not supported in React Native
11304
11319
  *
11305
11320
  * @param deviceId empty string means the system default
11306
11321
  */
11307
11322
  select(deviceId) {
11308
11323
  if (isReactNative()) {
11309
- throw new Error('This feature is not supported in React Native');
11324
+ throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
11310
11325
  }
11311
11326
  this.state.setDevice(deviceId);
11312
11327
  }
@@ -11314,11 +11329,11 @@ class SpeakerManager {
11314
11329
  * Set the volume of the audio elements
11315
11330
  * @param volume a number between 0 and 1.
11316
11331
  *
11317
- * Note: this method is not supported in React Native
11332
+ * Note: This method is not supported in React Native
11318
11333
  */
11319
11334
  setVolume(volume) {
11320
11335
  if (isReactNative()) {
11321
- throw new Error('This feature is not supported in React Native');
11336
+ throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
11322
11337
  }
11323
11338
  if (volume && (volume < 0 || volume > 1)) {
11324
11339
  throw new Error('Volume must be between 0 and 1');
@@ -11328,14 +11343,14 @@ class SpeakerManager {
11328
11343
  /**
11329
11344
  * Set the volume of a participant.
11330
11345
  *
11331
- * Note: this method is not supported in React Native.
11346
+ * Note: This method is not supported in React Native.
11332
11347
  *
11333
11348
  * @param sessionId the participant's session id.
11334
11349
  * @param volume a number between 0 and 1. Set it to `undefined` to use the default volume.
11335
11350
  */
11336
11351
  setParticipantVolume(sessionId, volume) {
11337
11352
  if (isReactNative()) {
11338
- throw new Error('This feature is not supported in React Native');
11353
+ throw new Error('This feature is not supported in React Native. Please visit https://getstream.io/video/docs/reactnative/core/camera-and-microphone/#speaker-management for more details');
11339
11354
  }
11340
11355
  if (volume && (volume < 0 || volume > 1)) {
11341
11356
  throw new Error('Volume must be between 0 and 1, or undefined');
@@ -11426,7 +11441,7 @@ class Call {
11426
11441
  /**
11427
11442
  * Leave the call and stop the media streams that were published by the call.
11428
11443
  */
11429
- this.leave = async ({ reject = false } = {}) => {
11444
+ this.leave = async ({ reject = false, reason = 'user is leaving the call', } = {}) => {
11430
11445
  const callingState = this.state.callingState;
11431
11446
  if (callingState === CallingState.LEFT) {
11432
11447
  throw new Error('Cannot leave call that has already been left.');
@@ -11455,7 +11470,7 @@ class Call {
11455
11470
  this.subscriber = undefined;
11456
11471
  this.publisher?.close();
11457
11472
  this.publisher = undefined;
11458
- this.sfuClient?.close();
11473
+ this.sfuClient?.close(StreamSfuClient.NORMAL_CLOSURE, reason);
11459
11474
  this.sfuClient = undefined;
11460
11475
  this.dispatcher.offAll();
11461
11476
  this.state.setCallingState(CallingState.LEFT);
@@ -11630,7 +11645,7 @@ class Call {
11630
11645
  /**
11631
11646
  * A closure which hides away the re-connection logic.
11632
11647
  */
11633
- const reconnect = async (strategy = 'full') => {
11648
+ const reconnect = async (strategy, reason) => {
11634
11649
  const currentState = this.state.callingState;
11635
11650
  if (currentState === CallingState.MIGRATING ||
11636
11651
  currentState === CallingState.RECONNECTING) {
@@ -11652,7 +11667,7 @@ class Call {
11652
11667
  // we'll need it for restoring the previous publishing state later
11653
11668
  const localParticipant = this.state.localParticipant;
11654
11669
  if (strategy === 'fast') {
11655
- sfuClient.close(StreamSfuClient.ERROR_CONNECTION_BROKEN, 'js-client: attempting fast reconnect');
11670
+ sfuClient.close(StreamSfuClient.ERROR_CONNECTION_BROKEN, `attempting fast reconnect: ${reason}`);
11656
11671
  }
11657
11672
  else if (strategy === 'full') {
11658
11673
  // in migration or recovery scenarios, we don't want to
@@ -11668,7 +11683,7 @@ class Call {
11668
11683
  this.sfuStatsReporter?.stop();
11669
11684
  this.sfuStatsReporter = undefined;
11670
11685
  // clean up current connection
11671
- sfuClient.close(StreamSfuClient.NORMAL_CLOSURE, 'js-client: attempting full reconnect');
11686
+ sfuClient.close(StreamSfuClient.NORMAL_CLOSURE, `attempting full reconnect: ${reason}`);
11672
11687
  }
11673
11688
  await this.join({
11674
11689
  ...data,
@@ -11676,7 +11691,7 @@ class Call {
11676
11691
  });
11677
11692
  // clean up previous connection
11678
11693
  if (strategy === 'migrate') {
11679
- sfuClient.close(StreamSfuClient.NORMAL_CLOSURE, 'js-client: attempting migration');
11694
+ sfuClient.close(StreamSfuClient.NORMAL_CLOSURE, 'attempting migration');
11680
11695
  }
11681
11696
  this.logger('info', `[Rejoin]: Attempt ${this.reconnectAttempts} successful!`);
11682
11697
  // we shouldn't be republishing the streams if we're migrating
@@ -11715,7 +11730,7 @@ class Call {
11715
11730
  const unregisterGoAway = this.dispatcher.on('goAway', (event) => {
11716
11731
  const { reason } = event;
11717
11732
  this.logger('info', `[Migration]: Going away from SFU... Reason: ${GoAwayReason[reason]}`);
11718
- reconnect('migrate').catch((err) => {
11733
+ reconnect('migrate', GoAwayReason[reason]).catch((err) => {
11719
11734
  this.logger('warn', `[Migration]: Failed to migrate to another SFU.`, err);
11720
11735
  });
11721
11736
  });
@@ -11745,7 +11760,7 @@ class Call {
11745
11760
  if (this.reconnectAttempts < this.maxReconnectAttempts) {
11746
11761
  sfuClient.isFastReconnecting = this.reconnectAttempts === 0;
11747
11762
  const strategy = sfuClient.isFastReconnecting ? 'fast' : 'full';
11748
- reconnect(strategy).catch((err) => {
11763
+ reconnect(strategy, `SFU closed the WS with code: ${e.code}`).catch((err) => {
11749
11764
  this.logger('error', `[Rejoin]: ${strategy} rejoin failed for ${this.reconnectAttempts} times. Giving up.`, err);
11750
11765
  this.state.setCallingState(CallingState.RECONNECTING_FAILED);
11751
11766
  });
@@ -11773,7 +11788,7 @@ class Call {
11773
11788
  do {
11774
11789
  try {
11775
11790
  sfuClient.isFastReconnecting = isFirstReconnectAttempt;
11776
- await reconnect(isFirstReconnectAttempt ? 'fast' : 'full');
11791
+ await reconnect(isFirstReconnectAttempt ? 'fast' : 'full', 'Network: online');
11777
11792
  return; // break the loop if rejoin is successful
11778
11793
  }
11779
11794
  catch (err) {
@@ -11882,7 +11897,7 @@ class Call {
11882
11897
  }
11883
11898
  else if (previousSfuClient?.isFastReconnecting) {
11884
11899
  // reconnection wasn't possible, so we need to do a full rejoin
11885
- return await reconnect('full').catch((err) => {
11900
+ return await reconnect('full', 're-attempting').catch((err) => {
11886
11901
  this.logger('error', `[Rejoin]: Rejoin failed forced full rejoin.`, err);
11887
11902
  });
11888
11903
  }
@@ -11935,7 +11950,7 @@ class Call {
11935
11950
  // join failed, try to rejoin
11936
11951
  if (this.reconnectAttempts < this.maxReconnectAttempts) {
11937
11952
  this.logger('error', `[Rejoin]: Rejoin ${this.reconnectAttempts} failed.`, err);
11938
- await reconnect();
11953
+ await reconnect('full', 'previous attempt failed');
11939
11954
  this.logger('info', `[Rejoin]: Rejoin ${this.reconnectAttempts} successful!`);
11940
11955
  }
11941
11956
  else {
@@ -12454,7 +12469,7 @@ class Call {
12454
12469
  return;
12455
12470
  clearTimeout(this.dropTimeout);
12456
12471
  this.dropTimeout = setTimeout(() => {
12457
- this.leave().catch((err) => {
12472
+ this.leave({ reason: 'ring: timeout' }).catch((err) => {
12458
12473
  this.logger('error', 'Failed to drop call', err);
12459
12474
  });
12460
12475
  }, timeoutInMs);
@@ -12673,7 +12688,7 @@ class Call {
12673
12688
  const currentUserId = this.currentUserId;
12674
12689
  if (currentUserId && blockedUserIds.includes(currentUserId)) {
12675
12690
  this.logger('info', 'Leaving call because of being blocked');
12676
- await this.leave();
12691
+ await this.leave({ reason: 'user blocked' });
12677
12692
  }
12678
12693
  }));
12679
12694
  this.leaveCallHooks.add(
@@ -14354,7 +14369,7 @@ class StreamClient {
14354
14369
  });
14355
14370
  };
14356
14371
  this.getUserAgent = () => {
14357
- const version = "0.6.3" ;
14372
+ const version = "0.6.5" ;
14358
14373
  return (this.userAgent ||
14359
14374
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
14360
14375
  };
@@ -14565,7 +14580,7 @@ class StreamVideoClient {
14565
14580
  * Creates a new call.
14566
14581
  *
14567
14582
  * @param type the type of the call.
14568
- * @param id the id of the call, if not provided a unique random value is used
14583
+ * @param id the id of the call.
14569
14584
  */
14570
14585
  this.call = (type, id) => {
14571
14586
  return new Call({
@@ -14817,7 +14832,7 @@ class StreamVideoClient {
14817
14832
  // if `call.created` was received before `call.ring`.
14818
14833
  // In that case, we cleanup the already tracked call.
14819
14834
  const prevCall = this.writeableStateStore.findCall(call.type, call.id);
14820
- await prevCall?.leave();
14835
+ await prevCall?.leave({ reason: 'cleaning-up in call.ring' });
14821
14836
  // we create a new call
14822
14837
  const theCall = new Call({
14823
14838
  streamClient: this.streamClient,