@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/dist/index.cjs.js CHANGED
@@ -954,6 +954,9 @@ var TrackUnpublishReason;
954
954
  TrackUnpublishReason[TrackUnpublishReason["MODERATION"] = 3] = "MODERATION";
955
955
  })(TrackUnpublishReason || (TrackUnpublishReason = {}));
956
956
  /**
957
+ * GoAwayReason represents the reason for the SFU to
958
+ * disconnect the client.
959
+ *
957
960
  * @generated from protobuf enum stream.video.sfu.models.GoAwayReason
958
961
  */
959
962
  var GoAwayReason;
@@ -971,6 +974,75 @@ var GoAwayReason;
971
974
  */
972
975
  GoAwayReason[GoAwayReason["REBALANCE"] = 2] = "REBALANCE";
973
976
  })(GoAwayReason || (GoAwayReason = {}));
977
+ /**
978
+ * CallEndedReason represents the reason for the call to end.
979
+ *
980
+ * @generated from protobuf enum stream.video.sfu.models.CallEndedReason
981
+ */
982
+ var CallEndedReason;
983
+ (function (CallEndedReason) {
984
+ /**
985
+ * @generated from protobuf enum value: CALL_ENDED_REASON_UNSPECIFIED = 0;
986
+ */
987
+ CallEndedReason[CallEndedReason["UNSPECIFIED"] = 0] = "UNSPECIFIED";
988
+ /**
989
+ * @generated from protobuf enum value: CALL_ENDED_REASON_ENDED = 1;
990
+ */
991
+ CallEndedReason[CallEndedReason["ENDED"] = 1] = "ENDED";
992
+ /**
993
+ * @generated from protobuf enum value: CALL_ENDED_REASON_LIVE_ENDED = 2;
994
+ */
995
+ CallEndedReason[CallEndedReason["LIVE_ENDED"] = 2] = "LIVE_ENDED";
996
+ /**
997
+ * @generated from protobuf enum value: CALL_ENDED_REASON_KICKED = 3;
998
+ */
999
+ CallEndedReason[CallEndedReason["KICKED"] = 3] = "KICKED";
1000
+ })(CallEndedReason || (CallEndedReason = {}));
1001
+ /**
1002
+ * WebsocketReconnectStrategy defines the ws strategies available for handling reconnections.
1003
+ *
1004
+ * @generated from protobuf enum stream.video.sfu.models.WebsocketReconnectStrategy
1005
+ */
1006
+ var WebsocketReconnectStrategy;
1007
+ (function (WebsocketReconnectStrategy) {
1008
+ /**
1009
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_UNSPECIFIED = 0;
1010
+ */
1011
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["UNSPECIFIED"] = 0] = "UNSPECIFIED";
1012
+ /**
1013
+ * Sent after reaching the maximum reconnection attempts, leading to permanent disconnect.
1014
+ *
1015
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_DISCONNECT = 1;
1016
+ */
1017
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["DISCONNECT"] = 1] = "DISCONNECT";
1018
+ /**
1019
+ * SDK should maintaining existing publisher/subscriber pc instances
1020
+ * and establish a new WebSocket connection.
1021
+ *
1022
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_FAST = 2;
1023
+ */
1024
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["FAST"] = 2] = "FAST";
1025
+ /**
1026
+ * SDK should drop existing pc instances and creates a fresh WebSocket connection,
1027
+ * ensuring a clean state for the reconnection.
1028
+ *
1029
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_CLEAN = 3;
1030
+ */
1031
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["CLEAN"] = 3] = "CLEAN";
1032
+ /**
1033
+ * SDK should obtain new credentials from the coordinator, drops existing pc instances, and initializes
1034
+ * a completely new WebSocket connection, ensuring a comprehensive reset.
1035
+ *
1036
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_FULL = 4;
1037
+ */
1038
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["FULL"] = 4] = "FULL";
1039
+ /**
1040
+ * SDK should migrate to a new SFU instance
1041
+ *
1042
+ * @generated from protobuf enum value: WEBSOCKET_RECONNECT_STRATEGY_MIGRATE = 5;
1043
+ */
1044
+ WebsocketReconnectStrategy[WebsocketReconnectStrategy["MIGRATE"] = 5] = "MIGRATE";
1045
+ })(WebsocketReconnectStrategy || (WebsocketReconnectStrategy = {}));
974
1046
  // @generated message type with reflection information, may provide speed optimized methods
975
1047
  class CallState$Type extends runtime.MessageType {
976
1048
  constructor() {
@@ -2433,6 +2505,7 @@ var models = /*#__PURE__*/Object.freeze({
2433
2505
  __proto__: null,
2434
2506
  Browser: Browser,
2435
2507
  Call: Call$1,
2508
+ get CallEndedReason () { return CallEndedReason; },
2436
2509
  CallGrants: CallGrants,
2437
2510
  CallState: CallState$1,
2438
2511
  ClientDetails: ClientDetails,
@@ -2456,7 +2529,8 @@ var models = /*#__PURE__*/Object.freeze({
2456
2529
  get TrackUnpublishReason () { return TrackUnpublishReason; },
2457
2530
  VideoDimension: VideoDimension,
2458
2531
  VideoLayer: VideoLayer,
2459
- get VideoQuality () { return VideoQuality; }
2532
+ get VideoQuality () { return VideoQuality; },
2533
+ get WebsocketReconnectStrategy () { return WebsocketReconnectStrategy; }
2460
2534
  });
2461
2535
 
2462
2536
  /* eslint-disable */
@@ -3865,6 +3939,20 @@ class SfuEvent$Type extends runtime.MessageType {
3865
3939
  oneof: 'eventPayload',
3866
3940
  T: () => PinsChanged,
3867
3941
  },
3942
+ {
3943
+ no: 23,
3944
+ name: 'call_ended',
3945
+ kind: 'message',
3946
+ oneof: 'eventPayload',
3947
+ T: () => CallEnded,
3948
+ },
3949
+ {
3950
+ no: 24,
3951
+ name: 'participant_updated',
3952
+ kind: 'message',
3953
+ oneof: 'eventPayload',
3954
+ T: () => ParticipantUpdated,
3955
+ },
3868
3956
  ]);
3869
3957
  }
3870
3958
  create(value) {
@@ -3987,6 +4075,18 @@ class SfuEvent$Type extends runtime.MessageType {
3987
4075
  pinsUpdated: PinsChanged.internalBinaryRead(reader, reader.uint32(), options, message.eventPayload.pinsUpdated),
3988
4076
  };
3989
4077
  break;
4078
+ case /* stream.video.sfu.event.CallEnded call_ended */ 23:
4079
+ message.eventPayload = {
4080
+ oneofKind: 'callEnded',
4081
+ callEnded: CallEnded.internalBinaryRead(reader, reader.uint32(), options, message.eventPayload.callEnded),
4082
+ };
4083
+ break;
4084
+ case /* stream.video.sfu.event.ParticipantUpdated participant_updated */ 24:
4085
+ message.eventPayload = {
4086
+ oneofKind: 'participantUpdated',
4087
+ participantUpdated: ParticipantUpdated.internalBinaryRead(reader, reader.uint32(), options, message.eventPayload.participantUpdated),
4088
+ };
4089
+ break;
3990
4090
  default:
3991
4091
  let u = options.readUnknownField;
3992
4092
  if (u === 'throw')
@@ -4053,6 +4153,12 @@ class SfuEvent$Type extends runtime.MessageType {
4053
4153
  /* stream.video.sfu.event.PinsChanged pins_updated = 22; */
4054
4154
  if (message.eventPayload.oneofKind === 'pinsUpdated')
4055
4155
  PinsChanged.internalBinaryWrite(message.eventPayload.pinsUpdated, writer.tag(22, runtime.WireType.LengthDelimited).fork(), options).join();
4156
+ /* stream.video.sfu.event.CallEnded call_ended = 23; */
4157
+ if (message.eventPayload.oneofKind === 'callEnded')
4158
+ CallEnded.internalBinaryWrite(message.eventPayload.callEnded, writer.tag(23, runtime.WireType.LengthDelimited).fork(), options).join();
4159
+ /* stream.video.sfu.event.ParticipantUpdated participant_updated = 24; */
4160
+ if (message.eventPayload.oneofKind === 'participantUpdated')
4161
+ ParticipantUpdated.internalBinaryWrite(message.eventPayload.participantUpdated, writer.tag(24, runtime.WireType.LengthDelimited).fork(), options).join();
4056
4162
  let u = options.writeUnknownFields;
4057
4163
  if (u !== false)
4058
4164
  (u == true ? runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -4121,10 +4227,21 @@ class Error$Type extends runtime.MessageType {
4121
4227
  constructor() {
4122
4228
  super('stream.video.sfu.event.Error', [
4123
4229
  { no: 4, name: 'error', kind: 'message', T: () => Error$2 },
4230
+ {
4231
+ no: 5,
4232
+ name: 'reconnect_strategy',
4233
+ kind: 'enum',
4234
+ T: () => [
4235
+ 'stream.video.sfu.models.WebsocketReconnectStrategy',
4236
+ WebsocketReconnectStrategy,
4237
+ 'WEBSOCKET_RECONNECT_STRATEGY_',
4238
+ ],
4239
+ },
4124
4240
  ]);
4125
4241
  }
4126
4242
  create(value) {
4127
4243
  const message = globalThis.Object.create(this.messagePrototype);
4244
+ message.reconnectStrategy = 0;
4128
4245
  if (value !== undefined)
4129
4246
  runtime.reflectionMergePartial(this, message, value);
4130
4247
  return message;
@@ -4137,6 +4254,9 @@ class Error$Type extends runtime.MessageType {
4137
4254
  case /* stream.video.sfu.models.Error error */ 4:
4138
4255
  message.error = Error$2.internalBinaryRead(reader, reader.uint32(), options, message.error);
4139
4256
  break;
4257
+ case /* stream.video.sfu.models.WebsocketReconnectStrategy reconnect_strategy */ 5:
4258
+ message.reconnectStrategy = reader.int32();
4259
+ break;
4140
4260
  default:
4141
4261
  let u = options.readUnknownField;
4142
4262
  if (u === 'throw')
@@ -4152,6 +4272,9 @@ class Error$Type extends runtime.MessageType {
4152
4272
  /* stream.video.sfu.models.Error error = 4; */
4153
4273
  if (message.error)
4154
4274
  Error$2.internalBinaryWrite(message.error, writer.tag(4, runtime.WireType.LengthDelimited).fork(), options).join();
4275
+ /* stream.video.sfu.models.WebsocketReconnectStrategy reconnect_strategy = 5; */
4276
+ if (message.reconnectStrategy !== 0)
4277
+ writer.tag(5, runtime.WireType.Varint).int32(message.reconnectStrategy);
4155
4278
  let u = options.writeUnknownFields;
4156
4279
  if (u !== false)
4157
4280
  (u == true ? runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -4945,6 +5068,60 @@ class ParticipantLeft$Type extends runtime.MessageType {
4945
5068
  */
4946
5069
  const ParticipantLeft = new ParticipantLeft$Type();
4947
5070
  // @generated message type with reflection information, may provide speed optimized methods
5071
+ class ParticipantUpdated$Type extends runtime.MessageType {
5072
+ constructor() {
5073
+ super('stream.video.sfu.event.ParticipantUpdated', [
5074
+ { no: 1, name: 'call_cid', kind: 'scalar', T: 9 /*ScalarType.STRING*/ },
5075
+ { no: 2, name: 'participant', kind: 'message', T: () => Participant },
5076
+ ]);
5077
+ }
5078
+ create(value) {
5079
+ const message = globalThis.Object.create(this.messagePrototype);
5080
+ message.callCid = '';
5081
+ if (value !== undefined)
5082
+ runtime.reflectionMergePartial(this, message, value);
5083
+ return message;
5084
+ }
5085
+ internalBinaryRead(reader, length, options, target) {
5086
+ let message = target ?? this.create(), end = reader.pos + length;
5087
+ while (reader.pos < end) {
5088
+ let [fieldNo, wireType] = reader.tag();
5089
+ switch (fieldNo) {
5090
+ case /* string call_cid */ 1:
5091
+ message.callCid = reader.string();
5092
+ break;
5093
+ case /* stream.video.sfu.models.Participant participant */ 2:
5094
+ message.participant = Participant.internalBinaryRead(reader, reader.uint32(), options, message.participant);
5095
+ break;
5096
+ default:
5097
+ let u = options.readUnknownField;
5098
+ if (u === 'throw')
5099
+ throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
5100
+ let d = reader.skip(wireType);
5101
+ if (u !== false)
5102
+ (u === true ? runtime.UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
5103
+ }
5104
+ }
5105
+ return message;
5106
+ }
5107
+ internalBinaryWrite(message, writer, options) {
5108
+ /* string call_cid = 1; */
5109
+ if (message.callCid !== '')
5110
+ writer.tag(1, runtime.WireType.LengthDelimited).string(message.callCid);
5111
+ /* stream.video.sfu.models.Participant participant = 2; */
5112
+ if (message.participant)
5113
+ Participant.internalBinaryWrite(message.participant, writer.tag(2, runtime.WireType.LengthDelimited).fork(), options).join();
5114
+ let u = options.writeUnknownFields;
5115
+ if (u !== false)
5116
+ (u == true ? runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
5117
+ return writer;
5118
+ }
5119
+ }
5120
+ /**
5121
+ * @generated MessageType for protobuf message stream.video.sfu.event.ParticipantUpdated
5122
+ */
5123
+ const ParticipantUpdated = new ParticipantUpdated$Type();
5124
+ // @generated message type with reflection information, may provide speed optimized methods
4948
5125
  class SubscriberOffer$Type extends runtime.MessageType {
4949
5126
  constructor() {
4950
5127
  super('stream.video.sfu.event.SubscriberOffer', [
@@ -5895,6 +6072,62 @@ class GoAway$Type extends runtime.MessageType {
5895
6072
  * @generated MessageType for protobuf message stream.video.sfu.event.GoAway
5896
6073
  */
5897
6074
  const GoAway = new GoAway$Type();
6075
+ // @generated message type with reflection information, may provide speed optimized methods
6076
+ class CallEnded$Type extends runtime.MessageType {
6077
+ constructor() {
6078
+ super('stream.video.sfu.event.CallEnded', [
6079
+ {
6080
+ no: 1,
6081
+ name: 'reason',
6082
+ kind: 'enum',
6083
+ T: () => [
6084
+ 'stream.video.sfu.models.CallEndedReason',
6085
+ CallEndedReason,
6086
+ 'CALL_ENDED_REASON_',
6087
+ ],
6088
+ },
6089
+ ]);
6090
+ }
6091
+ create(value) {
6092
+ const message = globalThis.Object.create(this.messagePrototype);
6093
+ message.reason = 0;
6094
+ if (value !== undefined)
6095
+ runtime.reflectionMergePartial(this, message, value);
6096
+ return message;
6097
+ }
6098
+ internalBinaryRead(reader, length, options, target) {
6099
+ let message = target ?? this.create(), end = reader.pos + length;
6100
+ while (reader.pos < end) {
6101
+ let [fieldNo, wireType] = reader.tag();
6102
+ switch (fieldNo) {
6103
+ case /* stream.video.sfu.models.CallEndedReason reason */ 1:
6104
+ message.reason = reader.int32();
6105
+ break;
6106
+ default:
6107
+ let u = options.readUnknownField;
6108
+ if (u === 'throw')
6109
+ throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
6110
+ let d = reader.skip(wireType);
6111
+ if (u !== false)
6112
+ (u === true ? runtime.UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
6113
+ }
6114
+ }
6115
+ return message;
6116
+ }
6117
+ internalBinaryWrite(message, writer, options) {
6118
+ /* stream.video.sfu.models.CallEndedReason reason = 1; */
6119
+ if (message.reason !== 0)
6120
+ writer.tag(1, runtime.WireType.Varint).int32(message.reason);
6121
+ let u = options.writeUnknownFields;
6122
+ if (u !== false)
6123
+ (u == true ? runtime.UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
6124
+ return writer;
6125
+ }
6126
+ }
6127
+ /**
6128
+ * @generated MessageType for protobuf message stream.video.sfu.event.CallEnded
6129
+ */
6130
+ const CallEnded = new CallEnded$Type();
5898
6131
 
5899
6132
  var events = /*#__PURE__*/Object.freeze({
5900
6133
  __proto__: null,
@@ -5902,6 +6135,7 @@ var events = /*#__PURE__*/Object.freeze({
5902
6135
  AudioLevelChanged: AudioLevelChanged,
5903
6136
  AudioMediaRequest: AudioMediaRequest,
5904
6137
  AudioSender: AudioSender,
6138
+ CallEnded: CallEnded,
5905
6139
  CallGrantsUpdated: CallGrantsUpdated,
5906
6140
  ChangePublishQuality: ChangePublishQuality,
5907
6141
  ConnectionQualityChanged: ConnectionQualityChanged,
@@ -5918,6 +6152,7 @@ var events = /*#__PURE__*/Object.freeze({
5918
6152
  Migration: Migration,
5919
6153
  ParticipantJoined: ParticipantJoined,
5920
6154
  ParticipantLeft: ParticipantLeft,
6155
+ ParticipantUpdated: ParticipantUpdated,
5921
6156
  PinsChanged: PinsChanged,
5922
6157
  PublisherAnswer: PublisherAnswer,
5923
6158
  SfuEvent: SfuEvent,
@@ -6198,6 +6433,8 @@ const sfuEventKinds = {
6198
6433
  goAway: undefined,
6199
6434
  iceRestart: undefined,
6200
6435
  pinsUpdated: undefined,
6436
+ callEnded: undefined,
6437
+ participantUpdated: undefined,
6201
6438
  };
6202
6439
  const isSfuEvent = (eventName) => {
6203
6440
  return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
@@ -7137,9 +7374,11 @@ class CallState {
7137
7374
  this.setCurrentValue(this.thumbnailsSubject, call.thumbnails);
7138
7375
  };
7139
7376
  this.updateFromMemberRemoved = (event) => {
7377
+ this.updateFromCallResponse(event.call);
7140
7378
  this.setCurrentValue(this.membersSubject, (members) => members.filter((m) => event.members.indexOf(m.user_id) === -1));
7141
7379
  };
7142
7380
  this.updateFromMemberAdded = (event) => {
7381
+ this.updateFromCallResponse(event.call);
7143
7382
  this.setCurrentValue(this.membersSubject, (members) => [
7144
7383
  ...members,
7145
7384
  ...event.members,
@@ -7223,6 +7462,7 @@ class CallState {
7223
7462
  });
7224
7463
  };
7225
7464
  this.updateMembers = (event) => {
7465
+ this.updateFromCallResponse(event.call);
7226
7466
  this.setCurrentValue(this.membersSubject, (members) => members.map((member) => {
7227
7467
  const memberUpdate = event.members.find((m) => m.user_id === member.user_id);
7228
7468
  return memberUpdate ? memberUpdate : member;
@@ -9351,6 +9591,17 @@ const watchParticipantLeft = (state) => {
9351
9591
  state.setParticipants((participants) => participants.filter((p) => p.sessionId !== participant.sessionId));
9352
9592
  };
9353
9593
  };
9594
+ /**
9595
+ * An event responder which handles the `participantUpdated` event.
9596
+ */
9597
+ const watchParticipantUpdated = (state) => {
9598
+ return function onParticipantUpdated(e) {
9599
+ const { participant } = e;
9600
+ if (!participant)
9601
+ return;
9602
+ state.updateParticipant(participant.sessionId, participant);
9603
+ };
9604
+ };
9354
9605
  /**
9355
9606
  * An event responder which handles the `trackPublished` event.
9356
9607
  * The SFU will send this event when a participant publishes a track.
@@ -9452,6 +9703,7 @@ const registerEventHandlers = (call, state, dispatcher) => {
9452
9703
  watchParticipantCountChanged(dispatcher, state),
9453
9704
  call.on('participantJoined', watchParticipantJoined(state)),
9454
9705
  call.on('participantLeft', watchParticipantLeft(state)),
9706
+ call.on('participantUpdated', watchParticipantUpdated(state)),
9455
9707
  call.on('trackPublished', watchTrackPublished(state)),
9456
9708
  call.on('trackUnpublished', watchTrackUnpublished(state)),
9457
9709
  watchAudioLevelChanged(dispatcher, state),
@@ -10638,18 +10890,27 @@ class InputMediaDeviceManager {
10638
10890
  * Starts stream.
10639
10891
  */
10640
10892
  async enable() {
10641
- if (this.state.status === 'enabled')
10893
+ if (this.state.optimisticStatus === 'enabled') {
10894
+ await this.statusChangePromise;
10642
10895
  return;
10643
- this.enablePromise = this.unmuteStream();
10644
- try {
10645
- await this.enablePromise;
10646
- this.state.setStatus('enabled');
10647
- this.enablePromise = undefined;
10648
- }
10649
- catch (error) {
10650
- this.enablePromise = undefined;
10651
- throw error;
10652
10896
  }
10897
+ const signal = this.nextAbortableStatusChangeRequest('enabled');
10898
+ const doEnable = async () => {
10899
+ if (signal.aborted)
10900
+ return;
10901
+ try {
10902
+ await this.unmuteStream();
10903
+ this.state.setStatus('enabled');
10904
+ }
10905
+ finally {
10906
+ if (!signal.aborted)
10907
+ this.resetStatusChangeRequest();
10908
+ }
10909
+ };
10910
+ this.statusChangePromise = this.statusChangePromise
10911
+ ? this.statusChangePromise.then(doEnable)
10912
+ : doEnable();
10913
+ await this.statusChangePromise;
10653
10914
  }
10654
10915
  /**
10655
10916
  * Stops or pauses the stream based on state.disableMode
@@ -10657,19 +10918,28 @@ class InputMediaDeviceManager {
10657
10918
  */
10658
10919
  async disable(forceStop = false) {
10659
10920
  this.state.prevStatus = this.state.status;
10660
- if (!forceStop && this.state.status === 'disabled')
10921
+ if (!forceStop && this.state.optimisticStatus === 'disabled') {
10922
+ await this.statusChangePromise;
10661
10923
  return;
10662
- const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
10663
- this.disablePromise = this.muteStream(stopTracks);
10664
- try {
10665
- await this.disablePromise;
10666
- this.state.setStatus('disabled');
10667
- this.disablePromise = undefined;
10668
- }
10669
- catch (error) {
10670
- this.disablePromise = undefined;
10671
- throw error;
10672
10924
  }
10925
+ const stopTracks = forceStop || this.state.disableMode === 'stop-tracks';
10926
+ const signal = this.nextAbortableStatusChangeRequest('disabled');
10927
+ const doDisable = async () => {
10928
+ if (signal.aborted)
10929
+ return;
10930
+ try {
10931
+ await this.muteStream(stopTracks);
10932
+ this.state.setStatus('disabled');
10933
+ }
10934
+ finally {
10935
+ if (!signal.aborted)
10936
+ this.resetStatusChangeRequest();
10937
+ }
10938
+ };
10939
+ this.statusChangePromise = this.statusChangePromise
10940
+ ? this.statusChangePromise.then(doDisable)
10941
+ : doDisable();
10942
+ await this.statusChangePromise;
10673
10943
  }
10674
10944
  /**
10675
10945
  * If status was previously enabled, it will re-enable the device.
@@ -10685,7 +10955,7 @@ class InputMediaDeviceManager {
10685
10955
  * else it will disable it.
10686
10956
  */
10687
10957
  async toggle() {
10688
- if (this.state.status === 'enabled') {
10958
+ if (this.state.optimisticStatus === 'enabled') {
10689
10959
  return this.disable();
10690
10960
  }
10691
10961
  else {
@@ -10872,11 +11142,8 @@ class InputMediaDeviceManager {
10872
11142
  this.state.setMediaStream(stream, await rootStream);
10873
11143
  this.getTracks().forEach((track) => {
10874
11144
  track.addEventListener('ended', async () => {
10875
- if (this.enablePromise) {
10876
- await this.enablePromise;
10877
- }
10878
- if (this.disablePromise) {
10879
- await this.disablePromise;
11145
+ if (this.statusChangePromise) {
11146
+ await this.statusChangePromise;
10880
11147
  }
10881
11148
  if (this.state.status === 'enabled') {
10882
11149
  this.isTrackStoppedDueToTrackEnd = true;
@@ -10906,11 +11173,8 @@ class InputMediaDeviceManager {
10906
11173
  if (!deviceId) {
10907
11174
  return;
10908
11175
  }
10909
- if (this.enablePromise) {
10910
- await this.enablePromise;
10911
- }
10912
- if (this.disablePromise) {
10913
- await this.disablePromise;
11176
+ if (this.statusChangePromise) {
11177
+ await this.statusChangePromise;
10914
11178
  }
10915
11179
  let isDeviceDisconnected = false;
10916
11180
  let isDeviceReplaced = false;
@@ -10944,6 +11208,16 @@ class InputMediaDeviceManager {
10944
11208
  findDeviceInList(devices, deviceId) {
10945
11209
  return devices.find((d) => d.deviceId === deviceId && d.kind === this.mediaDeviceKind);
10946
11210
  }
11211
+ nextAbortableStatusChangeRequest(status) {
11212
+ this.statusChangeAbortController?.abort();
11213
+ this.statusChangeAbortController = new AbortController();
11214
+ this.state.setPendingStatus(status);
11215
+ return this.statusChangeAbortController.signal;
11216
+ }
11217
+ resetStatusChangeRequest() {
11218
+ this.statusChangePromise = undefined;
11219
+ this.statusChangeAbortController = undefined;
11220
+ }
10947
11221
  }
10948
11222
 
10949
11223
  class InputMediaDeviceManagerState {
@@ -10958,6 +11232,7 @@ class InputMediaDeviceManagerState {
10958
11232
  this.disableMode = disableMode;
10959
11233
  this.permissionName = permissionName;
10960
11234
  this.statusSubject = new rxjs.BehaviorSubject(undefined);
11235
+ this.optimisticStatusSubject = new rxjs.BehaviorSubject(undefined);
10961
11236
  this.mediaStreamSubject = new rxjs.BehaviorSubject(undefined);
10962
11237
  this.selectedDeviceSubject = new rxjs.BehaviorSubject(undefined);
10963
11238
  this.defaultConstraintsSubject = new rxjs.BehaviorSubject(undefined);
@@ -10976,6 +11251,12 @@ class InputMediaDeviceManagerState {
10976
11251
  * An Observable that emits the device status
10977
11252
  */
10978
11253
  this.status$ = this.statusSubject.asObservable().pipe(rxjs.distinctUntilChanged());
11254
+ /**
11255
+ * An Observable the reflects the requested device status. Useful for optimistic UIs
11256
+ */
11257
+ this.optimisticStatus$ = this.optimisticStatusSubject
11258
+ .asObservable()
11259
+ .pipe(rxjs.distinctUntilChanged());
10979
11260
  /**
10980
11261
  * The default constraints for the device.
10981
11262
  */
@@ -11043,6 +11324,12 @@ class InputMediaDeviceManagerState {
11043
11324
  get status() {
11044
11325
  return this.getCurrentValue(this.status$);
11045
11326
  }
11327
+ /**
11328
+ * The requested device status. Useful for optimistic UIs
11329
+ */
11330
+ get optimisticStatus() {
11331
+ return this.getCurrentValue(this.optimisticStatus$);
11332
+ }
11046
11333
  /**
11047
11334
  * The currently selected device
11048
11335
  */
@@ -11062,6 +11349,13 @@ class InputMediaDeviceManagerState {
11062
11349
  setStatus(status) {
11063
11350
  this.setCurrentValue(this.statusSubject, status);
11064
11351
  }
11352
+ /**
11353
+ * @internal
11354
+ * @param pendingStatus
11355
+ */
11356
+ setPendingStatus(pendingStatus) {
11357
+ this.setCurrentValue(this.optimisticStatusSubject, pendingStatus);
11358
+ }
11065
11359
  /**
11066
11360
  * Updates the `mediaStream` state variable.
11067
11361
  *
@@ -11181,9 +11475,9 @@ class CameraManager extends InputMediaDeviceManager {
11181
11475
  async selectTargetResolution(resolution) {
11182
11476
  this.targetResolution.height = resolution.height;
11183
11477
  this.targetResolution.width = resolution.width;
11184
- if (this.enablePromise) {
11478
+ if (this.statusChangePromise && this.state.optimisticStatus === 'enabled') {
11185
11479
  try {
11186
- await this.enablePromise;
11480
+ await this.statusChangePromise;
11187
11481
  }
11188
11482
  catch (error) {
11189
11483
  // couldn't enable device, target resolution will be applied the next time user attempts to start the device
@@ -13324,12 +13618,7 @@ class Call {
13324
13618
  }
13325
13619
  async initCamera(options) {
13326
13620
  // Wait for any in progress camera operation
13327
- if (this.camera.enablePromise) {
13328
- await this.camera.enablePromise;
13329
- }
13330
- if (this.camera.disablePromise) {
13331
- await this.camera.disablePromise;
13332
- }
13621
+ await this.camera.statusChangePromise;
13333
13622
  if (this.state.localParticipant?.videoStream ||
13334
13623
  !this.permissionsContext.hasPermission('send-video')) {
13335
13624
  return;
@@ -13366,12 +13655,7 @@ class Call {
13366
13655
  }
13367
13656
  async initMic(options) {
13368
13657
  // Wait for any in progress mic operation
13369
- if (this.microphone.enablePromise) {
13370
- await this.microphone.enablePromise;
13371
- }
13372
- if (this.microphone.disablePromise) {
13373
- await this.microphone.disablePromise;
13374
- }
13658
+ await this.microphone.statusChangePromise;
13375
13659
  if (this.state.localParticipant?.audioStream ||
13376
13660
  !this.permissionsContext.hasPermission('send-audio')) {
13377
13661
  return;
@@ -14912,7 +15196,7 @@ class StreamClient {
14912
15196
  });
14913
15197
  };
14914
15198
  this.getUserAgent = () => {
14915
- const version = "1.0.0" ;
15199
+ const version = "1.0.2" ;
14916
15200
  return (this.userAgent ||
14917
15201
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
14918
15202
  };