@stream-io/video-client 1.0.1 → 1.0.3

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
+ ### [1.0.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.0.2...@stream-io/video-client-1.0.3) (2024-05-13)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * improve error handling across the SDK ([#1350](https://github.com/GetStream/stream-video-js/issues/1350)) ([ac0ae3b](https://github.com/GetStream/stream-video-js/commit/ac0ae3b7d5da91152d0f41a203b73e6c99c42ff9))
11
+
12
+ ### [1.0.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.0.1...@stream-io/video-client-1.0.2) (2024-05-13)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * optimistically toggle device status ([#1342](https://github.com/GetStream/stream-video-js/issues/1342)) ([2e4e470](https://github.com/GetStream/stream-video-js/commit/2e4e470347fce7c7499dd21a931e5dec74bf9618))
18
+
5
19
  ### [1.0.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.0.0...@stream-io/video-client-1.0.1) (2024-05-07)
6
20
 
7
21
 
@@ -8399,15 +8399,19 @@ class Publisher {
8399
8399
  return getPreferredCodecs('audio', preferredCodec ?? defaultAudioCodec, codecToRemove);
8400
8400
  }
8401
8401
  };
8402
- this.onIceCandidate = async (e) => {
8402
+ this.onIceCandidate = (e) => {
8403
8403
  const { candidate } = e;
8404
8404
  if (!candidate) {
8405
8405
  logger$3('debug', 'null ice candidate');
8406
8406
  return;
8407
8407
  }
8408
- await this.sfuClient.iceTrickle({
8408
+ this.sfuClient
8409
+ .iceTrickle({
8409
8410
  iceCandidate: getIceCandidate(candidate),
8410
8411
  peerType: PeerType.PUBLISHER_UNSPECIFIED,
8412
+ })
8413
+ .catch((err) => {
8414
+ logger$3('warn', `ICETrickle failed`, err);
8411
8415
  });
8412
8416
  };
8413
8417
  /**
@@ -8448,8 +8452,8 @@ class Publisher {
8448
8452
  }
8449
8453
  await this.negotiate({ iceRestart: true });
8450
8454
  };
8451
- this.onNegotiationNeeded = async () => {
8452
- await this.negotiate();
8455
+ this.onNegotiationNeeded = () => {
8456
+ this.negotiate().catch((err) => logger$3('warn', `Negotiation failed.`, err));
8453
8457
  };
8454
8458
  /**
8455
8459
  * Initiates a new offer/answer exchange with the currently connected SFU.
@@ -8639,10 +8643,12 @@ class Publisher {
8639
8643
  this.isDtxEnabled = isDtxEnabled;
8640
8644
  this.isRedEnabled = isRedEnabled;
8641
8645
  this.iceRestartDelay = iceRestartDelay;
8642
- this.unsubscribeOnIceRestart = dispatcher.on('iceRestart', async (iceRestart) => {
8646
+ this.unsubscribeOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
8643
8647
  if (iceRestart.peerType !== PeerType.PUBLISHER_UNSPECIFIED)
8644
8648
  return;
8645
- await this.restartIce();
8649
+ this.restartIce().catch((err) => {
8650
+ logger$3('warn', `ICERestart failed`, err);
8651
+ });
8646
8652
  });
8647
8653
  }
8648
8654
  }
@@ -8836,15 +8842,19 @@ class Subscriber {
8836
8842
  [streamKindProp]: primaryStream,
8837
8843
  });
8838
8844
  };
8839
- this.onIceCandidate = async (e) => {
8845
+ this.onIceCandidate = (e) => {
8840
8846
  const { candidate } = e;
8841
8847
  if (!candidate) {
8842
8848
  logger$2('debug', 'null ice candidate');
8843
8849
  return;
8844
8850
  }
8845
- await this.sfuClient.iceTrickle({
8851
+ this.sfuClient
8852
+ .iceTrickle({
8846
8853
  iceCandidate: getIceCandidate(candidate),
8847
8854
  peerType: PeerType.SUBSCRIBER,
8855
+ })
8856
+ .catch((err) => {
8857
+ logger$2('warn', `ICETrickle failed`, err);
8848
8858
  });
8849
8859
  };
8850
8860
  this.negotiate = async (subscriberOffer) => {
@@ -8916,13 +8926,17 @@ class Subscriber {
8916
8926
  this.state = state;
8917
8927
  this.iceRestartDelay = iceRestartDelay;
8918
8928
  this.pc = this.createPeerConnection(connectionConfig);
8919
- this.unregisterOnSubscriberOffer = dispatcher.on('subscriberOffer', async (subscriberOffer) => {
8920
- await this.negotiate(subscriberOffer);
8929
+ this.unregisterOnSubscriberOffer = dispatcher.on('subscriberOffer', (subscriberOffer) => {
8930
+ this.negotiate(subscriberOffer).catch((err) => {
8931
+ logger$2('warn', `Negotiation failed.`, err);
8932
+ });
8921
8933
  });
8922
- this.unregisterOnIceRestart = dispatcher.on('iceRestart', async (iceRestart) => {
8934
+ this.unregisterOnIceRestart = dispatcher.on('iceRestart', (iceRestart) => {
8923
8935
  if (iceRestart.peerType !== PeerType.SUBSCRIBER)
8924
8936
  return;
8925
- await this.restartIce();
8937
+ this.restartIce().catch((err) => {
8938
+ logger$2('warn', `ICERestart failed`, err);
8939
+ });
8926
8940
  });
8927
8941
  }
8928
8942
  }
@@ -10869,18 +10883,27 @@ class InputMediaDeviceManager {
10869
10883
  * Starts stream.
10870
10884
  */
10871
10885
  async enable() {
10872
- if (this.state.status === 'enabled')
10886
+ if (this.state.optimisticStatus === 'enabled') {
10887
+ await this.statusChangePromise;
10873
10888
  return;
10874
- this.enablePromise = this.unmuteStream();
10875
- try {
10876
- await this.enablePromise;
10877
- this.state.setStatus('enabled');
10878
- this.enablePromise = undefined;
10879
- }
10880
- catch (error) {
10881
- this.enablePromise = undefined;
10882
- throw error;
10883
10889
  }
10890
+ const signal = this.nextAbortableStatusChangeRequest('enabled');
10891
+ const doEnable = async () => {
10892
+ if (signal.aborted)
10893
+ return;
10894
+ try {
10895
+ await this.unmuteStream();
10896
+ this.state.setStatus('enabled');
10897
+ }
10898
+ finally {
10899
+ if (!signal.aborted)
10900
+ this.resetStatusChangeRequest();
10901
+ }
10902
+ };
10903
+ this.statusChangePromise = this.statusChangePromise
10904
+ ? this.statusChangePromise.then(doEnable)
10905
+ : doEnable();
10906
+ await this.statusChangePromise;
10884
10907
  }
10885
10908
  /**
10886
10909
  * Stops or pauses the stream based on state.disableMode
@@ -10888,19 +10911,28 @@ class InputMediaDeviceManager {
10888
10911
  */
10889
10912
  async disable(forceStop = false) {
10890
10913
  this.state.prevStatus = this.state.status;
10891
- if (!forceStop && this.state.status === 'disabled')
10914
+ if (!forceStop && this.state.optimisticStatus === 'disabled') {
10915
+ await this.statusChangePromise;
10892
10916
  return;
10893
- const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
10894
- this.disablePromise = this.muteStream(stopTracks);
10895
- try {
10896
- await this.disablePromise;
10897
- this.state.setStatus('disabled');
10898
- this.disablePromise = undefined;
10899
- }
10900
- catch (error) {
10901
- this.disablePromise = undefined;
10902
- throw error;
10903
10917
  }
10918
+ const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
10919
+ const signal = this.nextAbortableStatusChangeRequest('disabled');
10920
+ const doDisable = async () => {
10921
+ if (signal.aborted)
10922
+ return;
10923
+ try {
10924
+ await this.muteStream(stopTracks);
10925
+ this.state.setStatus('disabled');
10926
+ }
10927
+ finally {
10928
+ if (!signal.aborted)
10929
+ this.resetStatusChangeRequest();
10930
+ }
10931
+ };
10932
+ this.statusChangePromise = this.statusChangePromise
10933
+ ? this.statusChangePromise.then(doDisable)
10934
+ : doDisable();
10935
+ await this.statusChangePromise;
10904
10936
  }
10905
10937
  /**
10906
10938
  * If status was previously enabled, it will re-enable the device.
@@ -10916,7 +10948,7 @@ class InputMediaDeviceManager {
10916
10948
  * else it will disable it.
10917
10949
  */
10918
10950
  async toggle() {
10919
- if (this.state.status === 'enabled') {
10951
+ if (this.state.optimisticStatus === 'enabled') {
10920
10952
  return this.disable();
10921
10953
  }
10922
10954
  else {
@@ -11103,11 +11135,8 @@ class InputMediaDeviceManager {
11103
11135
  this.state.setMediaStream(stream, await rootStream);
11104
11136
  this.getTracks().forEach((track) => {
11105
11137
  track.addEventListener('ended', async () => {
11106
- if (this.enablePromise) {
11107
- await this.enablePromise;
11108
- }
11109
- if (this.disablePromise) {
11110
- await this.disablePromise;
11138
+ if (this.statusChangePromise) {
11139
+ await this.statusChangePromise;
11111
11140
  }
11112
11141
  if (this.state.status === 'enabled') {
11113
11142
  this.isTrackStoppedDueToTrackEnd = true;
@@ -11137,11 +11166,8 @@ class InputMediaDeviceManager {
11137
11166
  if (!deviceId) {
11138
11167
  return;
11139
11168
  }
11140
- if (this.enablePromise) {
11141
- await this.enablePromise;
11142
- }
11143
- if (this.disablePromise) {
11144
- await this.disablePromise;
11169
+ if (this.statusChangePromise) {
11170
+ await this.statusChangePromise;
11145
11171
  }
11146
11172
  let isDeviceDisconnected = false;
11147
11173
  let isDeviceReplaced = false;
@@ -11175,6 +11201,16 @@ class InputMediaDeviceManager {
11175
11201
  findDeviceInList(devices, deviceId) {
11176
11202
  return devices.find((d) => d.deviceId === deviceId && d.kind === this.mediaDeviceKind);
11177
11203
  }
11204
+ nextAbortableStatusChangeRequest(status) {
11205
+ this.statusChangeAbortController?.abort();
11206
+ this.statusChangeAbortController = new AbortController();
11207
+ this.state.setPendingStatus(status);
11208
+ return this.statusChangeAbortController.signal;
11209
+ }
11210
+ resetStatusChangeRequest() {
11211
+ this.statusChangePromise = undefined;
11212
+ this.statusChangeAbortController = undefined;
11213
+ }
11178
11214
  }
11179
11215
 
11180
11216
  class InputMediaDeviceManagerState {
@@ -11189,6 +11225,7 @@ class InputMediaDeviceManagerState {
11189
11225
  this.disableMode = disableMode;
11190
11226
  this.permissionName = permissionName;
11191
11227
  this.statusSubject = new BehaviorSubject(undefined);
11228
+ this.optimisticStatusSubject = new BehaviorSubject(undefined);
11192
11229
  this.mediaStreamSubject = new BehaviorSubject(undefined);
11193
11230
  this.selectedDeviceSubject = new BehaviorSubject(undefined);
11194
11231
  this.defaultConstraintsSubject = new BehaviorSubject(undefined);
@@ -11207,6 +11244,12 @@ class InputMediaDeviceManagerState {
11207
11244
  * An Observable that emits the device status
11208
11245
  */
11209
11246
  this.status$ = this.statusSubject.asObservable().pipe(distinctUntilChanged());
11247
+ /**
11248
+ * An Observable the reflects the requested device status. Useful for optimistic UIs
11249
+ */
11250
+ this.optimisticStatus$ = this.optimisticStatusSubject
11251
+ .asObservable()
11252
+ .pipe(distinctUntilChanged());
11210
11253
  /**
11211
11254
  * The default constraints for the device.
11212
11255
  */
@@ -11274,6 +11317,12 @@ class InputMediaDeviceManagerState {
11274
11317
  get status() {
11275
11318
  return this.getCurrentValue(this.status$);
11276
11319
  }
11320
+ /**
11321
+ * The requested device status. Useful for optimistic UIs
11322
+ */
11323
+ get optimisticStatus() {
11324
+ return this.getCurrentValue(this.optimisticStatus$);
11325
+ }
11277
11326
  /**
11278
11327
  * The currently selected device
11279
11328
  */
@@ -11293,6 +11342,13 @@ class InputMediaDeviceManagerState {
11293
11342
  setStatus(status) {
11294
11343
  this.setCurrentValue(this.statusSubject, status);
11295
11344
  }
11345
+ /**
11346
+ * @internal
11347
+ * @param pendingStatus
11348
+ */
11349
+ setPendingStatus(pendingStatus) {
11350
+ this.setCurrentValue(this.optimisticStatusSubject, pendingStatus);
11351
+ }
11296
11352
  /**
11297
11353
  * Updates the `mediaStream` state variable.
11298
11354
  *
@@ -11412,9 +11468,9 @@ class CameraManager extends InputMediaDeviceManager {
11412
11468
  async selectTargetResolution(resolution) {
11413
11469
  this.targetResolution.height = resolution.height;
11414
11470
  this.targetResolution.width = resolution.width;
11415
- if (this.enablePromise) {
11471
+ if (this.statusChangePromise && this.state.optimisticStatus === 'enabled') {
11416
11472
  try {
11417
- await this.enablePromise;
11473
+ await this.statusChangePromise;
11418
11474
  }
11419
11475
  catch (error) {
11420
11476
  // couldn't enable device, target resolution will be applied the next time user attempts to start the device
@@ -13446,7 +13502,9 @@ class Call {
13446
13502
  });
13447
13503
  this.leaveCallHooks.add(registerEventHandlers(this, this.state, this.dispatcher));
13448
13504
  this.registerEffects();
13449
- this.leaveCallHooks.add(createSubscription(this.trackSubscriptionsSubject.pipe(debounce((v) => timer(v.type)), map$1((v) => v.data)), (subscriptions) => this.sfuClient?.updateSubscriptions(subscriptions)));
13505
+ this.leaveCallHooks.add(createSubscription(this.trackSubscriptionsSubject.pipe(debounce((v) => timer(v.type)), map$1((v) => v.data)), (subscriptions) => this.sfuClient?.updateSubscriptions(subscriptions).catch((err) => {
13506
+ this.logger('debug', `Failed to update track subscriptions`, err);
13507
+ })));
13450
13508
  this.camera = new CameraManager(this);
13451
13509
  this.microphone = new MicrophoneManager(this);
13452
13510
  this.speaker = new SpeakerManager(this);
@@ -13555,12 +13613,7 @@ class Call {
13555
13613
  }
13556
13614
  async initCamera(options) {
13557
13615
  // Wait for any in progress camera operation
13558
- if (this.camera.enablePromise) {
13559
- await this.camera.enablePromise;
13560
- }
13561
- if (this.camera.disablePromise) {
13562
- await this.camera.disablePromise;
13563
- }
13616
+ await this.camera.statusChangePromise;
13564
13617
  if (this.state.localParticipant?.videoStream ||
13565
13618
  !this.permissionsContext.hasPermission('send-video')) {
13566
13619
  return;
@@ -13597,12 +13650,7 @@ class Call {
13597
13650
  }
13598
13651
  async initMic(options) {
13599
13652
  // Wait for any in progress mic operation
13600
- if (this.microphone.enablePromise) {
13601
- await this.microphone.enablePromise;
13602
- }
13603
- if (this.microphone.disablePromise) {
13604
- await this.microphone.disablePromise;
13605
- }
13653
+ await this.microphone.statusChangePromise;
13606
13654
  if (this.state.localParticipant?.audioStream ||
13607
13655
  !this.permissionsContext.hasPermission('send-audio')) {
13608
13656
  return;
@@ -15145,7 +15193,7 @@ class StreamClient {
15145
15193
  });
15146
15194
  };
15147
15195
  this.getUserAgent = () => {
15148
- const version = "1.0.1" ;
15196
+ const version = "1.0.3" ;
15149
15197
  return (this.userAgent ||
15150
15198
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
15151
15199
  };