@stream-io/video-client 1.0.0 → 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,20 @@
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
+
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)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **state:** handle participantUpdated event ([#1341](https://github.com/GetStream/stream-video-js/issues/1341)) ([96cb99f](https://github.com/GetStream/stream-video-js/commit/96cb99fe2b661e3f4899a7c16b4159cad7a085c6))
18
+
5
19
  ## [1.0.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-0.8.0...@stream-io/video-client-1.0.0) (2024-05-07)
6
20
 
7
21
 
@@ -933,6 +933,9 @@ var TrackUnpublishReason;
933
933
  TrackUnpublishReason[TrackUnpublishReason["MODERATION"] = 3] = "MODERATION";
934
934
  })(TrackUnpublishReason || (TrackUnpublishReason = {}));
935
935
  /**
936
+ * GoAwayReason represents the reason for the SFU to
937
+ * disconnect the client.
938
+ *
936
939
  * @generated from protobuf enum stream.video.sfu.models.GoAwayReason
937
940
  */
938
941
  var GoAwayReason;
@@ -950,6 +953,75 @@ var GoAwayReason;
950
953
  */
951
954
  GoAwayReason[GoAwayReason["REBALANCE"] = 2] = "REBALANCE";
952
955
  })(GoAwayReason || (GoAwayReason = {}));
956
+ /**
957
+ * CallEndedReason represents the reason for the call to end.
958
+ *
959
+ * @generated from protobuf enum stream.video.sfu.models.CallEndedReason
960
+ */
961
+ var CallEndedReason;
962
+ (function (CallEndedReason) {
963
+ /**
964
+ * @generated from protobuf enum value: CALL_ENDED_REASON_UNSPECIFIED = 0;
965
+ */
966
+ CallEndedReason[CallEndedReason["UNSPECIFIED"] = 0] = "UNSPECIFIED";
967
+ /**
968
+ * @generated from protobuf enum value: CALL_ENDED_REASON_ENDED = 1;
969
+ */
970
+ CallEndedReason[CallEndedReason["ENDED"] = 1] = "ENDED";
971
+ /**
972
+ * @generated from protobuf enum value: CALL_ENDED_REASON_LIVE_ENDED = 2;
973
+ */
974
+ CallEndedReason[CallEndedReason["LIVE_ENDED"] = 2] = "LIVE_ENDED";
975
+ /**
976
+ * @generated from protobuf enum value: CALL_ENDED_REASON_KICKED = 3;
977
+ */
978
+ CallEndedReason[CallEndedReason["KICKED"] = 3] = "KICKED";
979
+ })(CallEndedReason || (CallEndedReason = {}));
980
+ /**
981
+ * WebsocketReconnectStrategy defines the ws strategies available for handling reconnections.
982
+ *
983
+ * @generated from protobuf enum stream.video.sfu.models.WebsocketReconnectStrategy
984
+ */
985
+ var WebsocketReconnectStrategy;
986
+ (function (WebsocketReconnectStrategy) {
987
+ /**
988
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_UNSPECIFIED = 0;
989
+ */
990
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["UNSPECIFIED"] = 0] = "UNSPECIFIED";
991
+ /**
992
+ * Sent after reaching the maximum reconnection attempts, leading to permanent disconnect.
993
+ *
994
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_DISCONNECT = 1;
995
+ */
996
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["DISCONNECT"] = 1] = "DISCONNECT";
997
+ /**
998
+ * SDK should maintaining existing publisher/subscriber pc instances
999
+ * and establish a new WebSocket connection.
1000
+ *
1001
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_FAST = 2;
1002
+ */
1003
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["FAST"] = 2] = "FAST";
1004
+ /**
1005
+ * SDK should drop existing pc instances and creates a fresh WebSocket connection,
1006
+ * ensuring a clean state for the reconnection.
1007
+ *
1008
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_CLEAN = 3;
1009
+ */
1010
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["CLEAN"] = 3] = "CLEAN";
1011
+ /**
1012
+ * SDK should obtain new credentials from the coordinator, drops existing pc instances, and initializes
1013
+ * a completely new WebSocket connection, ensuring a comprehensive reset.
1014
+ *
1015
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_FULL = 4;
1016
+ */
1017
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["FULL"] = 4] = "FULL";
1018
+ /**
1019
+ * SDK should migrate to a new SFU instance
1020
+ *
1021
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_MIGRATE = 5;
1022
+ */
1023
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["MIGRATE"] = 5] = "MIGRATE";
1024
+ })(WebsocketReconnectStrategy || (WebsocketReconnectStrategy = {}));
953
1025
  // @generated message type with reflection information, may provide speed optimized methods
954
1026
  class CallState$Type extends MessageType {
955
1027
  constructor() {
@@ -2412,6 +2484,7 @@ var models = /*#__PURE__*/Object.freeze({
2412
2484
  __proto__: null,
2413
2485
  Browser: Browser,
2414
2486
  Call: Call$1,
2487
+ get CallEndedReason () { return CallEndedReason; },
2415
2488
  CallGrants: CallGrants,
2416
2489
  CallState: CallState$1,
2417
2490
  ClientDetails: ClientDetails,
@@ -2435,7 +2508,8 @@ var models = /*#__PURE__*/Object.freeze({
2435
2508
  get TrackUnpublishReason () { return TrackUnpublishReason; },
2436
2509
  VideoDimension: VideoDimension,
2437
2510
  VideoLayer: VideoLayer,
2438
- get VideoQuality () { return VideoQuality; }
2511
+ get VideoQuality () { return VideoQuality; },
2512
+ get WebsocketReconnectStrategy () { return WebsocketReconnectStrategy; }
2439
2513
  });
2440
2514
 
2441
2515
  /* eslint-disable */
@@ -3844,6 +3918,20 @@ class SfuEvent$Type extends MessageType {
3844
3918
  oneof: 'eventPayload',
3845
3919
  T: () => PinsChanged,
3846
3920
  },
3921
+ {
3922
+ no: 23,
3923
+ name: 'call_ended',
3924
+ kind: 'message',
3925
+ oneof: 'eventPayload',
3926
+ T: () => CallEnded,
3927
+ },
3928
+ {
3929
+ no: 24,
3930
+ name: 'participant_updated',
3931
+ kind: 'message',
3932
+ oneof: 'eventPayload',
3933
+ T: () => ParticipantUpdated,
3934
+ },
3847
3935
  ]);
3848
3936
  }
3849
3937
  create(value) {
@@ -3966,6 +4054,18 @@ class SfuEvent$Type extends MessageType {
3966
4054
  pinsUpdated: PinsChanged.internalBinaryRead(reader, reader.uint32(), options, message.eventPayload.pinsUpdated),
3967
4055
  };
3968
4056
  break;
4057
+ case /* stream.video.sfu.event.CallEnded call_ended */ 23:
4058
+ message.eventPayload = {
4059
+ oneofKind: 'callEnded',
4060
+ callEnded: CallEnded.internalBinaryRead(reader, reader.uint32(), options, message.eventPayload.callEnded),
4061
+ };
4062
+ break;
4063
+ case /* stream.video.sfu.event.ParticipantUpdated participant_updated */ 24:
4064
+ message.eventPayload = {
4065
+ oneofKind: 'participantUpdated',
4066
+ participantUpdated: ParticipantUpdated.internalBinaryRead(reader, reader.uint32(), options, message.eventPayload.participantUpdated),
4067
+ };
4068
+ break;
3969
4069
  default:
3970
4070
  let u = options.readUnknownField;
3971
4071
  if (u === 'throw')
@@ -4032,6 +4132,12 @@ class SfuEvent$Type extends MessageType {
4032
4132
  /* stream.video.sfu.event.PinsChanged pins_updated = 22; */
4033
4133
  if (message.eventPayload.oneofKind === 'pinsUpdated')
4034
4134
  PinsChanged.internalBinaryWrite(message.eventPayload.pinsUpdated, writer.tag(22, WireType.LengthDelimited).fork(), options).join();
4135
+ /* stream.video.sfu.event.CallEnded call_ended = 23; */
4136
+ if (message.eventPayload.oneofKind === 'callEnded')
4137
+ CallEnded.internalBinaryWrite(message.eventPayload.callEnded, writer.tag(23, WireType.LengthDelimited).fork(), options).join();
4138
+ /* stream.video.sfu.event.ParticipantUpdated participant_updated = 24; */
4139
+ if (message.eventPayload.oneofKind === 'participantUpdated')
4140
+ ParticipantUpdated.internalBinaryWrite(message.eventPayload.participantUpdated, writer.tag(24, WireType.LengthDelimited).fork(), options).join();
4035
4141
  let u = options.writeUnknownFields;
4036
4142
  if (u !== false)
4037
4143
  (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -4100,10 +4206,21 @@ class Error$Type extends MessageType {
4100
4206
  constructor() {
4101
4207
  super('stream.video.sfu.event.Error', [
4102
4208
  { no: 4, name: 'error', kind: 'message', T: () => Error$2 },
4209
+ {
4210
+ no: 5,
4211
+ name: 'reconnect_strategy',
4212
+ kind: 'enum',
4213
+ T: () => [
4214
+ 'stream.video.sfu.models.WebsocketReconnectStrategy',
4215
+ WebsocketReconnectStrategy,
4216
+ 'WEBSOCKET_RECONNECT_STRATEGY_',
4217
+ ],
4218
+ },
4103
4219
  ]);
4104
4220
  }
4105
4221
  create(value) {
4106
4222
  const message = globalThis.Object.create(this.messagePrototype);
4223
+ message.reconnectStrategy = 0;
4107
4224
  if (value !== undefined)
4108
4225
  reflectionMergePartial(this, message, value);
4109
4226
  return message;
@@ -4116,6 +4233,9 @@ class Error$Type extends MessageType {
4116
4233
  case /* stream.video.sfu.models.Error error */ 4:
4117
4234
  message.error = Error$2.internalBinaryRead(reader, reader.uint32(), options, message.error);
4118
4235
  break;
4236
+ case /* stream.video.sfu.models.WebsocketReconnectStrategy reconnect_strategy */ 5:
4237
+ message.reconnectStrategy = reader.int32();
4238
+ break;
4119
4239
  default:
4120
4240
  let u = options.readUnknownField;
4121
4241
  if (u === 'throw')
@@ -4131,6 +4251,9 @@ class Error$Type extends MessageType {
4131
4251
  /* stream.video.sfu.models.Error error = 4; */
4132
4252
  if (message.error)
4133
4253
  Error$2.internalBinaryWrite(message.error, writer.tag(4, WireType.LengthDelimited).fork(), options).join();
4254
+ /* stream.video.sfu.models.WebsocketReconnectStrategy reconnect_strategy = 5; */
4255
+ if (message.reconnectStrategy !== 0)
4256
+ writer.tag(5, WireType.Varint).int32(message.reconnectStrategy);
4134
4257
  let u = options.writeUnknownFields;
4135
4258
  if (u !== false)
4136
4259
  (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -4924,6 +5047,60 @@ class ParticipantLeft$Type extends MessageType {
4924
5047
  */
4925
5048
  const ParticipantLeft = new ParticipantLeft$Type();
4926
5049
  // @generated message type with reflection information, may provide speed optimized methods
5050
+ class ParticipantUpdated$Type extends MessageType {
5051
+ constructor() {
5052
+ super('stream.video.sfu.event.ParticipantUpdated', [
5053
+ { no: 1, name: 'call_cid', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
5054
+ { no: 2, name: 'participant', kind: 'message', T: () => Participant },
5055
+ ]);
5056
+ }
5057
+ create(value) {
5058
+ const message = globalThis.Object.create(this.messagePrototype);
5059
+ message.callCid = '';
5060
+ if (value !== undefined)
5061
+ reflectionMergePartial(this, message, value);
5062
+ return message;
5063
+ }
5064
+ internalBinaryRead(reader, length, options, target) {
5065
+ let message = target ?? this.create(), end = reader.pos + length;
5066
+ while (reader.pos < end) {
5067
+ let [fieldNo, wireType] = reader.tag();
5068
+ switch (fieldNo) {
5069
+ case /* string call_cid */ 1:
5070
+ message.callCid = reader.string();
5071
+ break;
5072
+ case /* stream.video.sfu.models.Participant participant */ 2:
5073
+ message.participant = Participant.internalBinaryRead(reader, reader.uint32(), options, message.participant);
5074
+ break;
5075
+ default:
5076
+ let u = options.readUnknownField;
5077
+ if (u === 'throw')
5078
+ throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
5079
+ let d = reader.skip(wireType);
5080
+ if (u !== false)
5081
+ (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
5082
+ }
5083
+ }
5084
+ return message;
5085
+ }
5086
+ internalBinaryWrite(message, writer, options) {
5087
+ /* string call_cid = 1; */
5088
+ if (message.callCid !== '')
5089
+ writer.tag(1, WireType.LengthDelimited).string(message.callCid);
5090
+ /* stream.video.sfu.models.Participant participant = 2; */
5091
+ if (message.participant)
5092
+ Participant.internalBinaryWrite(message.participant, writer.tag(2, WireType.LengthDelimited).fork(), options).join();
5093
+ let u = options.writeUnknownFields;
5094
+ if (u !== false)
5095
+ (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
5096
+ return writer;
5097
+ }
5098
+ }
5099
+ /**
5100
+ * @generated MessageType for protobuf message stream.video.sfu.event.ParticipantUpdated
5101
+ */
5102
+ const ParticipantUpdated = new ParticipantUpdated$Type();
5103
+ // @generated message type with reflection information, may provide speed optimized methods
4927
5104
  class SubscriberOffer$Type extends MessageType {
4928
5105
  constructor() {
4929
5106
  super('stream.video.sfu.event.SubscriberOffer', [
@@ -5874,6 +6051,62 @@ class GoAway$Type extends MessageType {
5874
6051
  * @generated MessageType for protobuf message stream.video.sfu.event.GoAway
5875
6052
  */
5876
6053
  const GoAway = new GoAway$Type();
6054
+ // @generated message type with reflection information, may provide speed optimized methods
6055
+ class CallEnded$Type extends MessageType {
6056
+ constructor() {
6057
+ super('stream.video.sfu.event.CallEnded', [
6058
+ {
6059
+ no: 1,
6060
+ name: 'reason',
6061
+ kind: 'enum',
6062
+ T: () => [
6063
+ 'stream.video.sfu.models.CallEndedReason',
6064
+ CallEndedReason,
6065
+ 'CALL_ENDED_REASON_',
6066
+ ],
6067
+ },
6068
+ ]);
6069
+ }
6070
+ create(value) {
6071
+ const message = globalThis.Object.create(this.messagePrototype);
6072
+ message.reason = 0;
6073
+ if (value !== undefined)
6074
+ reflectionMergePartial(this, message, value);
6075
+ return message;
6076
+ }
6077
+ internalBinaryRead(reader, length, options, target) {
6078
+ let message = target ?? this.create(), end = reader.pos + length;
6079
+ while (reader.pos < end) {
6080
+ let [fieldNo, wireType] = reader.tag();
6081
+ switch (fieldNo) {
6082
+ case /* stream.video.sfu.models.CallEndedReason reason */ 1:
6083
+ message.reason = reader.int32();
6084
+ break;
6085
+ default:
6086
+ let u = options.readUnknownField;
6087
+ if (u === 'throw')
6088
+ throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
6089
+ let d = reader.skip(wireType);
6090
+ if (u !== false)
6091
+ (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
6092
+ }
6093
+ }
6094
+ return message;
6095
+ }
6096
+ internalBinaryWrite(message, writer, options) {
6097
+ /* stream.video.sfu.models.CallEndedReason reason = 1; */
6098
+ if (message.reason !== 0)
6099
+ writer.tag(1, WireType.Varint).int32(message.reason);
6100
+ let u = options.writeUnknownFields;
6101
+ if (u !== false)
6102
+ (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
6103
+ return writer;
6104
+ }
6105
+ }
6106
+ /**
6107
+ * @generated MessageType for protobuf message stream.video.sfu.event.CallEnded
6108
+ */
6109
+ const CallEnded = new CallEnded$Type();
5877
6110
 
5878
6111
  var events = /*#__PURE__*/Object.freeze({
5879
6112
  __proto__: null,
@@ -5881,6 +6114,7 @@ var events = /*#__PURE__*/Object.freeze({
5881
6114
  AudioLevelChanged: AudioLevelChanged,
5882
6115
  AudioMediaRequest: AudioMediaRequest,
5883
6116
  AudioSender: AudioSender,
6117
+ CallEnded: CallEnded,
5884
6118
  CallGrantsUpdated: CallGrantsUpdated,
5885
6119
  ChangePublishQuality: ChangePublishQuality,
5886
6120
  ConnectionQualityChanged: ConnectionQualityChanged,
@@ -5897,6 +6131,7 @@ var events = /*#__PURE__*/Object.freeze({
5897
6131
  Migration: Migration,
5898
6132
  ParticipantJoined: ParticipantJoined,
5899
6133
  ParticipantLeft: ParticipantLeft,
6134
+ ParticipantUpdated: ParticipantUpdated,
5900
6135
  PinsChanged: PinsChanged,
5901
6136
  PublisherAnswer: PublisherAnswer,
5902
6137
  SfuEvent: SfuEvent,
@@ -6177,6 +6412,8 @@ const sfuEventKinds = {
6177
6412
  goAway: undefined,
6178
6413
  iceRestart: undefined,
6179
6414
  pinsUpdated: undefined,
6415
+ callEnded: undefined,
6416
+ participantUpdated: undefined,
6180
6417
  };
6181
6418
  const isSfuEvent = (eventName) => {
6182
6419
  return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
@@ -7116,9 +7353,11 @@ class CallState {
7116
7353
  this.setCurrentValue(this.thumbnailsSubject, call.thumbnails);
7117
7354
  };
7118
7355
  this.updateFromMemberRemoved = (event) => {
7356
+ this.updateFromCallResponse(event.call);
7119
7357
  this.setCurrentValue(this.membersSubject, (members) => members.filter((m) => event.members.indexOf(m.user_id) === -1));
7120
7358
  };
7121
7359
  this.updateFromMemberAdded = (event) => {
7360
+ this.updateFromCallResponse(event.call);
7122
7361
  this.setCurrentValue(this.membersSubject, (members) => [
7123
7362
  ...members,
7124
7363
  ...event.members,
@@ -7202,6 +7441,7 @@ class CallState {
7202
7441
  });
7203
7442
  };
7204
7443
  this.updateMembers = (event) => {
7444
+ this.updateFromCallResponse(event.call);
7205
7445
  this.setCurrentValue(this.membersSubject, (members) => members.map((member) => {
7206
7446
  const memberUpdate = event.members.find((m) => m.user_id === member.user_id);
7207
7447
  return memberUpdate ? memberUpdate : member;
@@ -9330,6 +9570,17 @@ const watchParticipantLeft = (state) => {
9330
9570
  state.setParticipants((participants) => participants.filter((p) => p.sessionId !== participant.sessionId));
9331
9571
  };
9332
9572
  };
9573
+ /**
9574
+ * An event responder which handles the `participantUpdated` event.
9575
+ */
9576
+ const watchParticipantUpdated = (state) => {
9577
+ return function onParticipantUpdated(e) {
9578
+ const { participant } = e;
9579
+ if (!participant)
9580
+ return;
9581
+ state.updateParticipant(participant.sessionId, participant);
9582
+ };
9583
+ };
9333
9584
  /**
9334
9585
  * An event responder which handles the `trackPublished` event.
9335
9586
  * The SFU will send this event when a participant publishes a track.
@@ -9431,6 +9682,7 @@ const registerEventHandlers = (call, state, dispatcher) => {
9431
9682
  watchParticipantCountChanged(dispatcher, state),
9432
9683
  call.on('participantJoined', watchParticipantJoined(state)),
9433
9684
  call.on('participantLeft', watchParticipantLeft(state)),
9685
+ call.on('participantUpdated', watchParticipantUpdated(state)),
9434
9686
  call.on('trackPublished', watchTrackPublished(state)),
9435
9687
  call.on('trackUnpublished', watchTrackUnpublished(state)),
9436
9688
  watchAudioLevelChanged(dispatcher, state),
@@ -10617,18 +10869,27 @@ class InputMediaDeviceManager {
10617
10869
  * Starts stream.
10618
10870
  */
10619
10871
  async enable() {
10620
- if (this.state.status === 'enabled')
10872
+ if (this.state.optimisticStatus === 'enabled') {
10873
+ await this.statusChangePromise;
10621
10874
  return;
10622
- this.enablePromise = this.unmuteStream();
10623
- try {
10624
- await this.enablePromise;
10625
- this.state.setStatus('enabled');
10626
- this.enablePromise = undefined;
10627
- }
10628
- catch (error) {
10629
- this.enablePromise = undefined;
10630
- throw error;
10631
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;
10632
10893
  }
10633
10894
  /**
10634
10895
  * Stops or pauses the stream based on state.disableMode
@@ -10636,19 +10897,28 @@ class InputMediaDeviceManager {
10636
10897
  */
10637
10898
  async disable(forceStop = false) {
10638
10899
  this.state.prevStatus = this.state.status;
10639
- if (!forceStop && this.state.status === 'disabled')
10900
+ if (!forceStop && this.state.optimisticStatus === 'disabled') {
10901
+ await this.statusChangePromise;
10640
10902
  return;
10641
- const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
10642
- this.disablePromise = this.muteStream(stopTracks);
10643
- try {
10644
- await this.disablePromise;
10645
- this.state.setStatus('disabled');
10646
- this.disablePromise = undefined;
10647
- }
10648
- catch (error) {
10649
- this.disablePromise = undefined;
10650
- throw error;
10651
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;
10652
10922
  }
10653
10923
  /**
10654
10924
  * If status was previously enabled, it will re-enable the device.
@@ -10664,7 +10934,7 @@ class InputMediaDeviceManager {
10664
10934
  * else it will disable it.
10665
10935
  */
10666
10936
  async toggle() {
10667
- if (this.state.status === 'enabled') {
10937
+ if (this.state.optimisticStatus === 'enabled') {
10668
10938
  return this.disable();
10669
10939
  }
10670
10940
  else {
@@ -10851,11 +11121,8 @@ class InputMediaDeviceManager {
10851
11121
  this.state.setMediaStream(stream, await rootStream);
10852
11122
  this.getTracks().forEach((track) => {
10853
11123
  track.addEventListener('ended', async () => {
10854
- if (this.enablePromise) {
10855
- await this.enablePromise;
10856
- }
10857
- if (this.disablePromise) {
10858
- await this.disablePromise;
11124
+ if (this.statusChangePromise) {
11125
+ await this.statusChangePromise;
10859
11126
  }
10860
11127
  if (this.state.status === 'enabled') {
10861
11128
  this.isTrackStoppedDueToTrackEnd = true;
@@ -10885,11 +11152,8 @@ class InputMediaDeviceManager {
10885
11152
  if (!deviceId) {
10886
11153
  return;
10887
11154
  }
10888
- if (this.enablePromise) {
10889
- await this.enablePromise;
10890
- }
10891
- if (this.disablePromise) {
10892
- await this.disablePromise;
11155
+ if (this.statusChangePromise) {
11156
+ await this.statusChangePromise;
10893
11157
  }
10894
11158
  let isDeviceDisconnected = false;
10895
11159
  let isDeviceReplaced = false;
@@ -10923,6 +11187,16 @@ class InputMediaDeviceManager {
10923
11187
  findDeviceInList(devices, deviceId) {
10924
11188
  return devices.find((d) => d.deviceId === deviceId && d.kind === this.mediaDeviceKind);
10925
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
+ }
10926
11200
  }
10927
11201
 
10928
11202
  class InputMediaDeviceManagerState {
@@ -10937,6 +11211,7 @@ class InputMediaDeviceManagerState {
10937
11211
  this.disableMode = disableMode;
10938
11212
  this.permissionName = permissionName;
10939
11213
  this.statusSubject = new BehaviorSubject(undefined);
11214
+ this.optimisticStatusSubject = new BehaviorSubject(undefined);
10940
11215
  this.mediaStreamSubject = new BehaviorSubject(undefined);
10941
11216
  this.selectedDeviceSubject = new BehaviorSubject(undefined);
10942
11217
  this.defaultConstraintsSubject = new BehaviorSubject(undefined);
@@ -10955,6 +11230,12 @@ class InputMediaDeviceManagerState {
10955
11230
  * An Observable that emits the device status
10956
11231
  */
10957
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());
10958
11239
  /**
10959
11240
  * The default constraints for the device.
10960
11241
  */
@@ -11022,6 +11303,12 @@ class InputMediaDeviceManagerState {
11022
11303
  get status() {
11023
11304
  return this.getCurrentValue(this.status$);
11024
11305
  }
11306
+ /**
11307
+ * The requested device status. Useful for optimistic UIs
11308
+ */
11309
+ get optimisticStatus() {
11310
+ return this.getCurrentValue(this.optimisticStatus$);
11311
+ }
11025
11312
  /**
11026
11313
  * The currently selected device
11027
11314
  */
@@ -11041,6 +11328,13 @@ class InputMediaDeviceManagerState {
11041
11328
  setStatus(status) {
11042
11329
  this.setCurrentValue(this.statusSubject, status);
11043
11330
  }
11331
+ /**
11332
+ * @internal
11333
+ * @param pendingStatus
11334
+ */
11335
+ setPendingStatus(pendingStatus) {
11336
+ this.setCurrentValue(this.optimisticStatusSubject, pendingStatus);
11337
+ }
11044
11338
  /**
11045
11339
  * Updates the `mediaStream` state variable.
11046
11340
  *
@@ -11160,9 +11454,9 @@ class CameraManager extends InputMediaDeviceManager {
11160
11454
  async selectTargetResolution(resolution) {
11161
11455
  this.targetResolution.height = resolution.height;
11162
11456
  this.targetResolution.width = resolution.width;
11163
- if (this.enablePromise) {
11457
+ if (this.statusChangePromise && this.state.optimisticStatus === 'enabled') {
11164
11458
  try {
11165
- await this.enablePromise;
11459
+ await this.statusChangePromise;
11166
11460
  }
11167
11461
  catch (error) {
11168
11462
  // couldn't enable device, target resolution will be applied the next time user attempts to start the device
@@ -13303,12 +13597,7 @@ class Call {
13303
13597
  }
13304
13598
  async initCamera(options) {
13305
13599
  // Wait for any in progress camera operation
13306
- if (this.camera.enablePromise) {
13307
- await this.camera.enablePromise;
13308
- }
13309
- if (this.camera.disablePromise) {
13310
- await this.camera.disablePromise;
13311
- }
13600
+ await this.camera.statusChangePromise;
13312
13601
  if (this.state.localParticipant?.videoStream ||
13313
13602
  !this.permissionsContext.hasPermission('send-video')) {
13314
13603
  return;
@@ -13345,12 +13634,7 @@ class Call {
13345
13634
  }
13346
13635
  async initMic(options) {
13347
13636
  // Wait for any in progress mic operation
13348
- if (this.microphone.enablePromise) {
13349
- await this.microphone.enablePromise;
13350
- }
13351
- if (this.microphone.disablePromise) {
13352
- await this.microphone.disablePromise;
13353
- }
13637
+ await this.microphone.statusChangePromise;
13354
13638
  if (this.state.localParticipant?.audioStream ||
13355
13639
  !this.permissionsContext.hasPermission('send-audio')) {
13356
13640
  return;
@@ -14893,7 +15177,7 @@ class StreamClient {
14893
15177
  });
14894
15178
  };
14895
15179
  this.getUserAgent = () => {
14896
- const version = "1.0.0" ;
15180
+ const version = "1.0.2" ;
14897
15181
  return (this.userAgent ||
14898
15182
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
14899
15183
  };