@stream-io/video-client 1.0.1 → 1.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ### [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)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * optimistically toggle device status ([#1342](https://github.com/GetStream/stream-video-js/issues/1342)) ([2e4e470](https://github.com/GetStream/stream-video-js/commit/2e4e470347fce7c7499dd21a931e5dec74bf9618))
11
+
5
12
  ### [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
13
 
7
14
 
@@ -10869,18 +10869,27 @@ class InputMediaDeviceManager {
10869
10869
  * Starts stream.
10870
10870
  */
10871
10871
  async enable() {
10872
- if (this.state.status === 'enabled')
10872
+ if (this.state.optimisticStatus === 'enabled') {
10873
+ await this.statusChangePromise;
10873
10874
  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
10875
  }
10876
+ const signal = this.nextAbortableStatusChangeRequest('enabled');
10877
+ const doEnable = async () => {
10878
+ if (signal.aborted)
10879
+ return;
10880
+ try {
10881
+ await this.unmuteStream();
10882
+ this.state.setStatus('enabled');
10883
+ }
10884
+ finally {
10885
+ if (!signal.aborted)
10886
+ this.resetStatusChangeRequest();
10887
+ }
10888
+ };
10889
+ this.statusChangePromise = this.statusChangePromise
10890
+ ? this.statusChangePromise.then(doEnable)
10891
+ : doEnable();
10892
+ await this.statusChangePromise;
10884
10893
  }
10885
10894
  /**
10886
10895
  * Stops or pauses the stream based on state.disableMode
@@ -10888,19 +10897,28 @@ class InputMediaDeviceManager {
10888
10897
  */
10889
10898
  async disable(forceStop = false) {
10890
10899
  this.state.prevStatus = this.state.status;
10891
- if (!forceStop && this.state.status === 'disabled')
10900
+ if (!forceStop && this.state.optimisticStatus === 'disabled') {
10901
+ await this.statusChangePromise;
10892
10902
  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
10903
  }
10904
+ const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
10905
+ const signal = this.nextAbortableStatusChangeRequest('disabled');
10906
+ const doDisable = async () => {
10907
+ if (signal.aborted)
10908
+ return;
10909
+ try {
10910
+ await this.muteStream(stopTracks);
10911
+ this.state.setStatus('disabled');
10912
+ }
10913
+ finally {
10914
+ if (!signal.aborted)
10915
+ this.resetStatusChangeRequest();
10916
+ }
10917
+ };
10918
+ this.statusChangePromise = this.statusChangePromise
10919
+ ? this.statusChangePromise.then(doDisable)
10920
+ : doDisable();
10921
+ await this.statusChangePromise;
10904
10922
  }
10905
10923
  /**
10906
10924
  * If status was previously enabled, it will re-enable the device.
@@ -10916,7 +10934,7 @@ class InputMediaDeviceManager {
10916
10934
  * else it will disable it.
10917
10935
  */
10918
10936
  async toggle() {
10919
- if (this.state.status === 'enabled') {
10937
+ if (this.state.optimisticStatus === 'enabled') {
10920
10938
  return this.disable();
10921
10939
  }
10922
10940
  else {
@@ -11103,11 +11121,8 @@ class InputMediaDeviceManager {
11103
11121
  this.state.setMediaStream(stream, await rootStream);
11104
11122
  this.getTracks().forEach((track) => {
11105
11123
  track.addEventListener('ended', async () => {
11106
- if (this.enablePromise) {
11107
- await this.enablePromise;
11108
- }
11109
- if (this.disablePromise) {
11110
- await this.disablePromise;
11124
+ if (this.statusChangePromise) {
11125
+ await this.statusChangePromise;
11111
11126
  }
11112
11127
  if (this.state.status === 'enabled') {
11113
11128
  this.isTrackStoppedDueToTrackEnd = true;
@@ -11137,11 +11152,8 @@ class InputMediaDeviceManager {
11137
11152
  if (!deviceId) {
11138
11153
  return;
11139
11154
  }
11140
- if (this.enablePromise) {
11141
- await this.enablePromise;
11142
- }
11143
- if (this.disablePromise) {
11144
- await this.disablePromise;
11155
+ if (this.statusChangePromise) {
11156
+ await this.statusChangePromise;
11145
11157
  }
11146
11158
  let isDeviceDisconnected = false;
11147
11159
  let isDeviceReplaced = false;
@@ -11175,6 +11187,16 @@ class InputMediaDeviceManager {
11175
11187
  findDeviceInList(devices, deviceId) {
11176
11188
  return devices.find((d) => d.deviceId === deviceId && d.kind === this.mediaDeviceKind);
11177
11189
  }
11190
+ nextAbortableStatusChangeRequest(status) {
11191
+ this.statusChangeAbortController?.abort();
11192
+ this.statusChangeAbortController = new AbortController();
11193
+ this.state.setPendingStatus(status);
11194
+ return this.statusChangeAbortController.signal;
11195
+ }
11196
+ resetStatusChangeRequest() {
11197
+ this.statusChangePromise = undefined;
11198
+ this.statusChangeAbortController = undefined;
11199
+ }
11178
11200
  }
11179
11201
 
11180
11202
  class InputMediaDeviceManagerState {
@@ -11189,6 +11211,7 @@ class InputMediaDeviceManagerState {
11189
11211
  this.disableMode = disableMode;
11190
11212
  this.permissionName = permissionName;
11191
11213
  this.statusSubject = new BehaviorSubject(undefined);
11214
+ this.optimisticStatusSubject = new BehaviorSubject(undefined);
11192
11215
  this.mediaStreamSubject = new BehaviorSubject(undefined);
11193
11216
  this.selectedDeviceSubject = new BehaviorSubject(undefined);
11194
11217
  this.defaultConstraintsSubject = new BehaviorSubject(undefined);
@@ -11207,6 +11230,12 @@ class InputMediaDeviceManagerState {
11207
11230
  * An Observable that emits the device status
11208
11231
  */
11209
11232
  this.status$ = this.statusSubject.asObservable().pipe(distinctUntilChanged());
11233
+ /**
11234
+ * An Observable the reflects the requested device status. Useful for optimistic UIs
11235
+ */
11236
+ this.optimisticStatus$ = this.optimisticStatusSubject
11237
+ .asObservable()
11238
+ .pipe(distinctUntilChanged());
11210
11239
  /**
11211
11240
  * The default constraints for the device.
11212
11241
  */
@@ -11274,6 +11303,12 @@ class InputMediaDeviceManagerState {
11274
11303
  get status() {
11275
11304
  return this.getCurrentValue(this.status$);
11276
11305
  }
11306
+ /**
11307
+ * The requested device status. Useful for optimistic UIs
11308
+ */
11309
+ get optimisticStatus() {
11310
+ return this.getCurrentValue(this.optimisticStatus$);
11311
+ }
11277
11312
  /**
11278
11313
  * The currently selected device
11279
11314
  */
@@ -11293,6 +11328,13 @@ class InputMediaDeviceManagerState {
11293
11328
  setStatus(status) {
11294
11329
  this.setCurrentValue(this.statusSubject, status);
11295
11330
  }
11331
+ /**
11332
+ * @internal
11333
+ * @param pendingStatus
11334
+ */
11335
+ setPendingStatus(pendingStatus) {
11336
+ this.setCurrentValue(this.optimisticStatusSubject, pendingStatus);
11337
+ }
11296
11338
  /**
11297
11339
  * Updates the `mediaStream` state variable.
11298
11340
  *
@@ -11412,9 +11454,9 @@ class CameraManager extends InputMediaDeviceManager {
11412
11454
  async selectTargetResolution(resolution) {
11413
11455
  this.targetResolution.height = resolution.height;
11414
11456
  this.targetResolution.width = resolution.width;
11415
- if (this.enablePromise) {
11457
+ if (this.statusChangePromise && this.state.optimisticStatus === 'enabled') {
11416
11458
  try {
11417
- await this.enablePromise;
11459
+ await this.statusChangePromise;
11418
11460
  }
11419
11461
  catch (error) {
11420
11462
  // couldn't enable device, target resolution will be applied the next time user attempts to start the device
@@ -13555,12 +13597,7 @@ class Call {
13555
13597
  }
13556
13598
  async initCamera(options) {
13557
13599
  // 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
- }
13600
+ await this.camera.statusChangePromise;
13564
13601
  if (this.state.localParticipant?.videoStream ||
13565
13602
  !this.permissionsContext.hasPermission('send-video')) {
13566
13603
  return;
@@ -13597,12 +13634,7 @@ class Call {
13597
13634
  }
13598
13635
  async initMic(options) {
13599
13636
  // 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
- }
13637
+ await this.microphone.statusChangePromise;
13606
13638
  if (this.state.localParticipant?.audioStream ||
13607
13639
  !this.permissionsContext.hasPermission('send-audio')) {
13608
13640
  return;
@@ -15145,7 +15177,7 @@ class StreamClient {
15145
15177
  });
15146
15178
  };
15147
15179
  this.getUserAgent = () => {
15148
- const version = "1.0.1" ;
15180
+ const version = "1.0.2" ;
15149
15181
  return (this.userAgent ||
15150
15182
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
15151
15183
  };