@stream-io/video-client 1.61.0 → 2.0.0-beta.1

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +10 -10
  2. package/dist/index.browser.es.js +358 -611
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +357 -611
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +358 -611
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +3 -3
  9. package/dist/src/StreamVideoClient.d.ts +15 -21
  10. package/dist/src/store/CallState.d.ts +108 -266
  11. package/dist/src/store/ClientState.d.ts +62 -0
  12. package/dist/src/store/index.d.ts +1 -1
  13. package/dist/src/store/subjects.d.ts +15 -0
  14. package/dist/src/store/types.d.ts +22 -0
  15. package/dist/src/types.d.ts +3 -3
  16. package/package.json +2 -2
  17. package/src/Call.ts +14 -18
  18. package/src/StreamVideoClient.ts +40 -47
  19. package/src/__tests__/Call.autodrop.test.ts +4 -4
  20. package/src/__tests__/Call.getRingState.test.ts +2 -2
  21. package/src/__tests__/Call.lifecycle.test.ts +8 -8
  22. package/src/__tests__/Call.publishing.test.ts +2 -2
  23. package/src/__tests__/Call.ringSettled.test.ts +4 -4
  24. package/src/__tests__/Call.ringingLifecycle.test.ts +2 -2
  25. package/src/__tests__/StreamVideoClient.disconnect.test.ts +62 -0
  26. package/src/devices/__tests__/CameraManager.test.ts +2 -2
  27. package/src/devices/__tests__/DeviceManager.test.ts +2 -2
  28. package/src/devices/__tests__/DeviceManagerFilters.test.ts +2 -2
  29. package/src/devices/__tests__/MicrophoneManager.test.ts +2 -2
  30. package/src/devices/__tests__/MicrophoneManagerRN.test.ts +2 -2
  31. package/src/devices/__tests__/ScreenShareManager.test.ts +3 -3
  32. package/src/devices/__tests__/SpeakerManager.test.ts +4 -4
  33. package/src/events/__tests__/call.test.ts +3 -3
  34. package/src/helpers/__tests__/AudioBindingsWatchdog.test.ts +2 -2
  35. package/src/helpers/__tests__/DynascaleManager.test.ts +2 -2
  36. package/src/helpers/__tests__/ViewportTracker.test.ts +2 -2
  37. package/src/ringing/__tests__/RingStatePoller.test.ts +7 -7
  38. package/src/ringing/__tests__/RingTimeout.test.ts +4 -4
  39. package/src/ringing/__tests__/reconcileRingState.test.ts +3 -3
  40. package/src/rtc/__tests__/Call.reconnect.test.ts +5 -5
  41. package/src/store/CallState.ts +281 -551
  42. package/src/store/ClientState.ts +101 -0
  43. package/src/store/__tests__/CallState.test.ts +1 -1
  44. package/src/store/__tests__/ClientState.test.ts +35 -0
  45. package/src/store/__tests__/rxUtils.test.ts +67 -1
  46. package/src/store/index.ts +1 -1
  47. package/src/store/rxUtils.ts +8 -4
  48. package/src/store/subjects.ts +30 -0
  49. package/src/store/types.ts +45 -0
  50. package/src/types.ts +3 -3
  51. package/dist/src/store/stateStore.d.ts +0 -84
  52. package/src/store/stateStore.ts +0 -156
@@ -1,15 +1,18 @@
1
1
  import {
2
- BehaviorSubject,
3
2
  combineLatest,
4
3
  distinctUntilChanged,
5
4
  map,
6
- Observable,
7
5
  ReplaySubject,
8
- shareReplay,
9
6
  startWith,
10
7
  } from 'rxjs';
11
- import type { Patch } from './rxUtils';
12
- import * as RxUtils from './rxUtils';
8
+ import {
9
+ getCurrentValue,
10
+ isShallowArrayEqual,
11
+ type Patch,
12
+ setCurrentValue,
13
+ updateValue,
14
+ } from './rxUtils';
15
+ import { duc, shared, subject } from './subjects';
13
16
  import { CallingState } from './CallingState';
14
17
  import {
15
18
  type CallRecordingType,
@@ -60,10 +63,9 @@ import { Comparator, defaultSortPreset } from '../sorting';
60
63
  import { ensureExhausted } from '../helpers/ensureExhausted';
61
64
  import { hasScreenShare } from '../helpers/participantUtils';
62
65
  import { videoLoggerSystem } from '../logger';
66
+ import type { AllEventHandlers, CallStateEventHandlers } from './types';
63
67
 
64
- /**
65
- * Returns the default egress object - when no egress data is available.
66
- */
68
+ /** Returns the default egress object - when no egress data is available. */
67
69
  const defaultEgress: EgressResponse = {
68
70
  broadcasting: false,
69
71
  hls: { playlist_url: '', status: '' },
@@ -83,359 +85,188 @@ type OrphanedTrack = {
83
85
  * @react You don't have to use this class directly, as we are exposing the state through Hooks.
84
86
  */
85
87
  export class CallState {
86
- private backstageSubject = new BehaviorSubject<boolean>(true);
87
- private blockedUserIdsSubject = new BehaviorSubject<string[]>([]);
88
- private createdAtSubject = new BehaviorSubject<Date>(new Date());
89
- private endedAtSubject = new BehaviorSubject<Date | undefined>(undefined);
90
- private startsAtSubject = new BehaviorSubject<Date | undefined>(undefined);
91
- private updatedAtSubject = new BehaviorSubject<Date>(new Date());
92
- private createdBySubject = new BehaviorSubject<UserResponse | undefined>(
93
- undefined,
94
- );
95
- private customSubject = new BehaviorSubject<Record<string, any>>({});
96
- private egressSubject = new BehaviorSubject<EgressResponse | undefined>(
97
- undefined,
98
- );
99
- private ingressSubject = new BehaviorSubject<CallIngressResponse | undefined>(
100
- undefined,
101
- );
102
- private recordingSubject = new BehaviorSubject<boolean>(false);
103
- private individualRecordingSubject = new BehaviorSubject<boolean>(false);
104
- private rawRecordingSubject = new BehaviorSubject<boolean>(false);
105
- private sessionSubject = new BehaviorSubject<CallSessionResponse | undefined>(
106
- undefined,
107
- );
108
- private settingsSubject = new BehaviorSubject<
109
- CallSettingsResponse | undefined
110
- >(undefined);
111
- private transcribingSubject = new BehaviorSubject<boolean>(false);
112
- private captioningSubject = new BehaviorSubject<boolean>(false);
113
- private e2eeEnabledSubject = new BehaviorSubject<boolean>(false);
114
- private endedBySubject = new BehaviorSubject<UserResponse | undefined>(
115
- undefined,
116
- );
117
- private thumbnailsSubject = new BehaviorSubject<
118
- ThumbnailResponse | undefined
119
- >(undefined);
120
- private membersSubject = new BehaviorSubject<MemberResponse[]>([]);
121
- private ownCapabilitiesSubject = new BehaviorSubject<OwnCapability[]>([]);
122
- private callingStateSubject = new BehaviorSubject<CallingState>(
123
- CallingState.UNKNOWN,
124
- );
125
- private startedAtSubject = new BehaviorSubject<Date | undefined>(undefined);
126
- private participantCountSubject = new BehaviorSubject<number>(0);
127
- private anonymousParticipantCountSubject = new BehaviorSubject<number>(0);
128
- private participantsSubject = new BehaviorSubject<StreamVideoParticipant[]>(
129
- [],
130
- );
131
- private callStatsReportSubject = new BehaviorSubject<
132
- CallStatsReport | undefined
133
- >(undefined);
134
- private closedCaptionsSubject = new BehaviorSubject<CallClosedCaption[]>([]);
135
-
136
- // These are tracks that were delivered to the Subscriber's onTrack event
137
- // that we couldn't associate with a participant yet.
138
- // This happens when the participantJoined event hasn't been received yet.
139
- // We keep these tracks around until we can associate them with a participant.
140
- private orphanedTracks: OrphanedTrack[] = [];
88
+ readonly logger = videoLoggerSystem.getLogger('CallState');
89
+ /** A list of comparators that are used to sort the participants. */
90
+ private sortParticipantsBy = defaultSortPreset;
141
91
 
92
+ private backstageSubject = subject(true);
93
+ private blockedUserIdsSubject = subject<string[]>([]);
94
+ private createdAtSubject = subject(new Date());
95
+ private endedAtSubject = subject<Date>();
96
+ private startsAtSubject = subject<Date>();
97
+ private updatedAtSubject = subject(new Date());
98
+ private createdBySubject = subject<UserResponse>();
99
+ private customSubject = subject<Record<string, any>>({});
100
+ private egressSubject = subject<EgressResponse>();
101
+ private ingressSubject = subject<CallIngressResponse>();
102
+ private recordingSubject = subject(false);
103
+ private individualRecordingSubject = subject(false);
104
+ private rawRecordingSubject = subject(false);
105
+ private sessionSubject = subject<CallSessionResponse>();
106
+ private settingsSubject = subject<CallSettingsResponse>();
107
+ private transcribingSubject = subject(false);
108
+ private captioningSubject = subject(false);
109
+ private e2eeEnabledSubject = subject(false);
110
+ private endedBySubject = subject<UserResponse>();
111
+ private thumbnailsSubject = subject<ThumbnailResponse>();
112
+ private membersSubject = subject<MemberResponse[]>([]);
113
+ private ownCapabilitiesSubject = subject<OwnCapability[]>([]);
114
+ private callingStateSubject = subject<CallingState>(CallingState.UNKNOWN);
115
+ private startedAtSubject = subject<Date>();
116
+ private participantCountSubject = subject(0);
117
+ private anonymousParticipantCountSubject = subject(0);
118
+ private participantsSubject = subject<StreamVideoParticipant[]>([]);
119
+ private callStatsReportSubject = subject<CallStatsReport>();
120
+ private closedCaptionsSubject = subject<CallClosedCaption[]>([]);
142
121
  private callGrantsSubject = new ReplaySubject<CallGrants>(1);
143
122
 
144
- // Derived state
123
+ /** The backstage state. */
124
+ backstage$ = duc(this.backstageSubject);
145
125
 
146
- /**
147
- * The time the call session actually started.
148
- * Useful for displaying the call duration.
149
- */
150
- startedAt$: Observable<Date | undefined>;
126
+ /** Will provide the list of blocked user IDs. */
127
+ blockedUserIds$ = duc(this.blockedUserIdsSubject, isShallowArrayEqual);
151
128
 
152
- /**
153
- * The server-side counted number of participants connected to the current call.
154
- * This number includes the anonymous participants as well.
155
- */
156
- participantCount$: Observable<number>;
157
-
158
- /**
159
- * The server-side counted number of anonymous participants connected to the current call.
160
- * This number excludes the regular participants.
161
- */
162
- anonymousParticipantCount$: Observable<number>;
129
+ /** Will provide the time when this call has been created. */
130
+ createdAt$ = this.createdAtSubject.asObservable();
163
131
 
164
- /**
165
- * All participants of the current call (this includes the current user and other participants as well),
166
- * unsorted. This observable only updates when participants join or leave the call.
167
- */
168
- rawParticipants$: Observable<StreamVideoParticipant[]>;
169
-
170
- /**
171
- * All participants of the current call (this includes the current user and other participants as well),
172
- * sorted according to the current `sortByParticipantsBy` setting
173
- */
174
- participants$: Observable<StreamVideoParticipant[]>;
175
-
176
- /**
177
- * Remote participants of the current call (this includes every participant except the logged-in user).
178
- */
179
- remoteParticipants$: Observable<StreamVideoParticipant[]>;
180
-
181
- /**
182
- * The local participant of the current call (the logged-in user).
183
- */
184
- localParticipant$: Observable<StreamVideoParticipant | undefined>;
185
-
186
- /**
187
- * Pinned participants of the current call.
188
- */
189
- pinnedParticipants$: Observable<StreamVideoParticipant[]>;
190
-
191
- /**
192
- * The currently elected dominant speaker in the current call.
193
- */
194
- dominantSpeaker$: Observable<StreamVideoParticipant | undefined>;
195
-
196
- /**
197
- * Emits true whenever there is an active screen sharing session within
198
- * the current call. Useful for displaying a "screen sharing" indicator and
199
- * switching the layout to a screen sharing layout.
200
- *
201
- * The actual screen sharing track isn't exposed here, but can be retrieved
202
- * from the list of call participants. We also don't want to be limiting
203
- * to the number of share screen tracks are displayed in a call.
204
- */
205
- hasOngoingScreenShare$: Observable<boolean>;
206
-
207
- /**
208
- * The latest stats report of the current call.
209
- * When stats gathering is enabled, this observable will emit a new value
210
- * at a regular (configurable) interval.
211
- *
212
- * Consumers of this observable can implement their own batching logic
213
- * in case they want to show historical stats data.
214
- */
215
- callStatsReport$: Observable<CallStatsReport | undefined>;
132
+ /** Will provide the time when this call has been ended. */
133
+ endedAt$ = this.endedAtSubject.asObservable();
216
134
 
217
- /**
218
- * The list of members in the current call.
219
- */
220
- members$: Observable<MemberResponse[]>;
135
+ /** Will provide the time when this call has been scheduled to start. */
136
+ startsAt$ = this.startsAtSubject.asObservable();
221
137
 
222
- /**
223
- * The list of capabilities of the current user.
224
- */
225
- ownCapabilities$: Observable<OwnCapability[]>;
138
+ /** Will provide the time when this call has been updated. */
139
+ updatedAt$ = this.updatedAtSubject.asObservable();
226
140
 
227
- /**
228
- * The calling state.
229
- */
230
- callingState$: Observable<CallingState>;
141
+ /** Will provide the user who created this call. */
142
+ createdBy$ = this.createdBySubject.asObservable();
231
143
 
232
- /**
233
- * The backstage state.
234
- */
235
- backstage$: Observable<boolean>;
144
+ /** Will provide the custom data of this call. */
145
+ custom$ = this.customSubject.asObservable();
236
146
 
237
- /**
238
- * Will provide the list of blocked user IDs.
239
- */
240
- blockedUserIds$: Observable<string[]>;
147
+ /** Will provide the egress data of this call. */
148
+ egress$ = this.egressSubject.asObservable();
241
149
 
242
- /**
243
- * Will provide the time when this call has been created.
244
- */
245
- createdAt$: Observable<Date>;
150
+ /** Will provide the ingress data of this call. */
151
+ ingress$ = this.ingressSubject.asObservable();
246
152
 
247
- /**
248
- * Will provide the time when this call has been ended.
249
- */
250
- endedAt$: Observable<Date | undefined>;
153
+ /** Will provide the recording state of this call. */
154
+ recording$ = duc(this.recordingSubject);
251
155
 
252
- /**
253
- * Will provide the time when this call has been scheduled to start.
254
- */
255
- startsAt$: Observable<Date | undefined>;
156
+ /** Will provide the recording state of this call. */
157
+ individualRecording$ = duc(this.individualRecordingSubject);
256
158
 
257
- /**
258
- * Will provide the time when this call has been updated.
259
- */
260
- updatedAt$: Observable<Date>;
159
+ /** Will provide the recording state of this call. */
160
+ rawRecording$ = duc(this.rawRecordingSubject);
261
161
 
262
- /**
263
- * Will provide the user who created this call.
264
- */
265
- createdBy$: Observable<UserResponse | undefined>;
162
+ /** Will provide the session data of this call. */
163
+ session$ = this.sessionSubject.asObservable();
266
164
 
267
- /**
268
- * Will provide the custom data of this call.
269
- */
270
- custom$: Observable<Record<string, any>>;
165
+ /** Will provide the settings of this call. */
166
+ settings$ = this.settingsSubject.asObservable();
271
167
 
272
- /**
273
- * Will provide the egress data of this call.
274
- */
275
- egress$: Observable<EgressResponse | undefined>;
168
+ /** Will provide the transcribing state of this call. */
169
+ transcribing$ = duc(this.transcribingSubject);
276
170
 
277
- /**
278
- * Will provide the ingress data of this call.
279
- */
280
- ingress$: Observable<CallIngressResponse | undefined>;
171
+ /** Will provide the closed captioning state of this call. */
172
+ captioning$ = duc(this.captioningSubject);
281
173
 
282
174
  /**
283
- * Will provide the recording state of this call.
175
+ * Whether end-to-end encryption is active for this call, as reported by the
176
+ * SFU in the join response.
284
177
  */
285
- recording$: Observable<boolean>;
178
+ e2eeEnabled$ = duc(this.e2eeEnabledSubject);
286
179
 
287
- /**
288
- * Will provide the recording state of this call.
289
- */
290
- individualRecording$: Observable<boolean>;
180
+ /** Will provide the user who ended this call. */
181
+ endedBy$ = this.endedBySubject.asObservable();
291
182
 
292
- /**
293
- * Will provide the recording state of this call.
294
- */
295
- rawRecording$: Observable<boolean>;
183
+ /** Will provide the thumbnails of this call. */
184
+ thumbnails$ = this.thumbnailsSubject.asObservable();
296
185
 
297
- /**
298
- * Will provide the session data of this call.
299
- */
300
- session$: Observable<CallSessionResponse | undefined>;
186
+ /** The list of members in the current call. */
187
+ members$ = this.membersSubject.asObservable();
301
188
 
302
- /**
303
- * Will provide the settings of this call.
304
- */
305
- settings$: Observable<CallSettingsResponse | undefined>;
189
+ /** The calling state. */
190
+ callingState$ = duc(this.callingStateSubject);
306
191
 
307
192
  /**
308
- * Will provide the transcribing state of this call.
193
+ * The time the call session actually started.
194
+ * Useful for displaying the call duration.
309
195
  */
310
- transcribing$: Observable<boolean>;
196
+ startedAt$ = this.startedAtSubject.asObservable();
311
197
 
312
198
  /**
313
- * Will provide the closed captioning state of this call.
199
+ * The server-side counted number of participants connected to the current call.
200
+ * This number includes the anonymous participants as well.
314
201
  */
315
- captioning$: Observable<boolean>;
202
+ participantCount$ = duc(this.participantCountSubject);
316
203
 
317
204
  /**
318
- * Whether end-to-end encryption is active for this call, as reported by the
319
- * SFU in the join response.
205
+ * The server-side counted number of anonymous participants connected to the current call.
206
+ * This number excludes the regular participants.
320
207
  */
321
- e2eeEnabled$: Observable<boolean>;
208
+ anonymousParticipantCount$ = duc(this.anonymousParticipantCountSubject);
322
209
 
323
210
  /**
324
- * Will provide the user who ended this call.
211
+ * All participants of the current call (this includes the current user and other participants as well),
212
+ * unsorted. This observable only updates when participants join or leave the call.
325
213
  */
326
- endedBy$: Observable<UserResponse | undefined>;
214
+ rawParticipants$ = shared(this.participantsSubject.asObservable());
327
215
 
328
- /**
329
- * Will provide the thumbnails of this call.
330
- */
331
- thumbnails$: Observable<ThumbnailResponse | undefined>;
216
+ /** All participants of the current call sorted according to the current `sortByParticipantsBy` setting. */
217
+ participants$ = shared(
218
+ this.participantsSubject.asObservable().pipe(
219
+ // maintain stable-sort by mutating the participants stored in the original subject
220
+ map((ps) => ps.sort(this.sortParticipantsBy)),
221
+ ),
222
+ );
332
223
 
333
- /**
334
- * The queue of closed captions.
335
- */
336
- closedCaptions$: Observable<CallClosedCaption[]>;
224
+ /** The local participant of the current call. */
225
+ localParticipant$ = shared(
226
+ this.participants$.pipe(map((ps) => ps.find((p) => p.isLocalParticipant))),
227
+ );
337
228
 
338
- readonly logger = videoLoggerSystem.getLogger('CallState');
229
+ /** Remote participants of the current call (all except the local participant). */
230
+ remoteParticipants$ = shared(
231
+ this.participants$.pipe(
232
+ map((ps) => ps.filter((p) => !p.isLocalParticipant)),
233
+ ),
234
+ );
339
235
 
340
- /**
341
- * A list of comparators that are used to sort the participants.
342
- */
343
- private sortParticipantsBy = defaultSortPreset;
236
+ /** Pinned participants of the current call. */
237
+ pinnedParticipants$ = shared(
238
+ this.participants$.pipe(map((ps) => ps.filter((p) => !!p.pin))),
239
+ );
344
240
 
345
- /**
346
- * The closed captions configuration.
347
- */
348
- private closedCaptionsSettings: ClosedCaptionsSettings | undefined;
349
- private closedCaptionsTasks = new Map<string, NodeJS.Timeout>();
241
+ /** The currently elected dominant speaker in the current call. */
242
+ dominantSpeaker$ = shared(
243
+ this.participants$.pipe(map((ps) => ps.find((p) => p.isDominantSpeaker))),
244
+ );
350
245
 
351
- private readonly eventHandlers: {
352
- [EventType in VideoEvent['type']]:
353
- ((event: Extract<VideoEvent, { type: EventType }>) => void) | undefined;
354
- };
246
+ /** Emits true whenever there is an active screen sharing session within the current call. */
247
+ hasOngoingScreenShare$ = shared(
248
+ this.participants$.pipe(
249
+ map((ps) => ps.some((p) => hasScreenShare(p))),
250
+ distinctUntilChanged(),
251
+ ),
252
+ );
355
253
 
356
254
  /**
357
- * Creates a new instance of the CallState class.
255
+ * The latest stats report of the current call.
256
+ * When stats gathering is enabled, this observable will emit a new value
257
+ * at a regular (configurable) interval.
358
258
  *
259
+ * Consumers of this observable can implement their own batching logic
260
+ * in case they want to show historical stats data.
359
261
  */
360
- constructor() {
361
- this.rawParticipants$ = this.participantsSubject
362
- .asObservable()
363
- .pipe(shareReplay({ bufferSize: 1, refCount: true }));
262
+ callStatsReport$ = this.callStatsReportSubject.asObservable();
364
263
 
365
- this.participants$ = this.participantsSubject.asObservable().pipe(
366
- // maintain stable-sort by mutating the participants stored
367
- // in the original subject
368
- map((ps) => ps.sort(this.sortParticipantsBy)),
369
- shareReplay({ bufferSize: 1, refCount: true }),
370
- );
371
-
372
- this.localParticipant$ = this.participants$.pipe(
373
- map((participants) => participants.find((p) => p.isLocalParticipant)),
374
- shareReplay({ bufferSize: 1, refCount: true }),
375
- );
376
-
377
- this.remoteParticipants$ = this.participants$.pipe(
378
- map((participants) => participants.filter((p) => !p.isLocalParticipant)),
379
- shareReplay({ bufferSize: 1, refCount: true }),
380
- );
381
-
382
- this.pinnedParticipants$ = this.participants$.pipe(
383
- map((participants) => participants.filter((p) => !!p.pin)),
384
- shareReplay({ bufferSize: 1, refCount: true }),
385
- );
264
+ /** The queue of closed captions. */
265
+ closedCaptions$ = this.closedCaptionsSubject.asObservable();
386
266
 
387
- this.dominantSpeaker$ = this.participants$.pipe(
388
- map((participants) => participants.find((p) => p.isDominantSpeaker)),
389
- shareReplay({ bufferSize: 1, refCount: true }),
390
- );
391
-
392
- this.hasOngoingScreenShare$ = this.participants$.pipe(
393
- map((participants) => participants.some((p) => hasScreenShare(p))),
394
- distinctUntilChanged(),
395
- shareReplay({ bufferSize: 1, refCount: true }),
396
- );
397
-
398
- // dates
399
- this.createdAt$ = this.createdAtSubject.asObservable();
400
- this.endedAt$ = this.endedAtSubject.asObservable();
401
- this.startsAt$ = this.startsAtSubject.asObservable();
402
- this.startedAt$ = this.startedAtSubject.asObservable();
403
- this.updatedAt$ = this.updatedAtSubject.asObservable();
404
-
405
- this.callStatsReport$ = this.callStatsReportSubject.asObservable();
406
- this.members$ = this.membersSubject.asObservable();
407
-
408
- // complex objects should work as streams of data
409
- this.createdBy$ = this.createdBySubject.asObservable();
410
- this.custom$ = this.customSubject.asObservable();
411
- this.egress$ = this.egressSubject.asObservable();
412
- this.ingress$ = this.ingressSubject.asObservable();
413
- this.session$ = this.sessionSubject.asObservable();
414
- this.settings$ = this.settingsSubject.asObservable();
415
- this.endedBy$ = this.endedBySubject.asObservable();
416
- this.thumbnails$ = this.thumbnailsSubject.asObservable();
417
- this.closedCaptions$ = this.closedCaptionsSubject.asObservable();
418
-
419
- /**
420
- * Creates an Observable from the given subject by piping to the
421
- * `distinctUntilChanged()` operator.
422
- */
423
- const duc = <T>(
424
- subject: BehaviorSubject<T>,
425
- comparator?: (a: T, b: T) => boolean,
426
- ): Observable<T> => subject.pipe(distinctUntilChanged(comparator));
427
-
428
- // primitive values should only emit once the value they hold changes
429
- this.anonymousParticipantCount$ = duc(
430
- this.anonymousParticipantCountSubject,
431
- );
432
- this.blockedUserIds$ = duc(
433
- this.blockedUserIdsSubject,
434
- RxUtils.isShallowArrayEqual,
435
- );
436
- this.backstage$ = duc(this.backstageSubject);
437
- this.callingState$ = duc(this.callingStateSubject);
438
- this.ownCapabilities$ = combineLatest([
267
+ /** The list of capabilities of the current user. */
268
+ ownCapabilities$ = shared(
269
+ combineLatest([
439
270
  this.ownCapabilitiesSubject,
440
271
  this.callGrantsSubject.pipe(startWith(undefined)),
441
272
  ]).pipe(
@@ -467,56 +298,42 @@ export class CallState {
467
298
 
468
299
  return nextCapabilities;
469
300
  }),
470
- distinctUntilChanged(RxUtils.isShallowArrayEqual),
471
- shareReplay({ bufferSize: 1, refCount: true }),
472
- );
473
- this.participantCount$ = duc(this.participantCountSubject);
474
- this.recording$ = duc(this.recordingSubject);
475
- this.individualRecording$ = duc(this.individualRecordingSubject);
476
- this.rawRecording$ = duc(this.rawRecordingSubject);
477
- this.transcribing$ = duc(this.transcribingSubject);
478
- this.captioning$ = duc(this.captioningSubject);
479
- this.e2eeEnabled$ = duc(this.e2eeEnabledSubject);
301
+ distinctUntilChanged(isShallowArrayEqual),
302
+ ),
303
+ );
304
+
305
+ // These are tracks that were delivered to the Subscriber's onTrack event
306
+ // that we couldn't associate with a participant yet.
307
+ // This happens when the participantJoined event hasn't been received yet.
308
+ // We keep these tracks around until we can associate them with a participant.
309
+ private orphanedTracks: OrphanedTrack[] = [];
480
310
 
311
+ /** The closed captions configuration. */
312
+ private closedCaptionsSettings: ClosedCaptionsSettings | undefined;
313
+ private closedCaptionsTasks = new Map<string, NodeJS.Timeout>();
314
+
315
+ private readonly eventHandlers: Partial<AllEventHandlers>;
316
+
317
+ /** Creates a new instance of the CallState class. */
318
+ constructor() {
481
319
  this.eventHandlers = {
482
- // these events are not updating the call state:
483
- 'call.frame_recording_ready': undefined,
484
- 'call.kicked_user': undefined,
485
- 'call.moderation_blur': undefined,
486
- 'call.moderation_warning': undefined,
487
- 'call.permission_request': undefined,
488
- 'call.recording_ready': undefined,
489
- 'call.rtmp_broadcast_failed': undefined,
490
- 'call.rtmp_broadcast_started': undefined,
491
- 'call.rtmp_broadcast_stopped': undefined,
492
- 'call.stats_report_ready': undefined,
493
- 'call.transcription_ready': undefined,
494
- 'call.user_feedback_submitted': undefined,
495
- 'call.user_muted': undefined,
496
- 'connection.error': undefined,
497
- 'connection.ok': undefined,
498
- 'health.check': undefined,
499
- 'user.updated': undefined,
500
- custom: undefined,
501
-
502
- // events that update call state:
503
320
  'call.accepted': (e) => this.updateFromCallResponse(e.call),
504
321
  'call.blocked_user': this.blockUser,
505
322
  'call.closed_caption': this.updateFromClosedCaptions,
506
323
  'call.closed_captions_failed': () => {
507
- this.setCurrentValue(this.captioningSubject, false);
324
+ setCurrentValue(this.captioningSubject, false);
508
325
  },
509
326
  'call.closed_captions_started': () => {
510
- this.setCurrentValue(this.captioningSubject, true);
327
+ setCurrentValue(this.captioningSubject, true);
511
328
  },
512
329
  'call.closed_captions_stopped': () => {
513
- this.setCurrentValue(this.captioningSubject, false);
330
+ setCurrentValue(this.captioningSubject, false);
514
331
  },
515
332
  'call.created': (e) => this.updateFromCallResponse(e.call),
516
333
  'call.deleted': (e) => this.updateFromCallResponse(e.call),
517
334
  'call.ended': (e) => {
518
335
  this.updateFromCallResponse(e.call);
519
- this.setCurrentValue(this.endedBySubject, e.user);
336
+ setCurrentValue(this.endedBySubject, e.user);
520
337
  },
521
338
  'call.frame_recording_failed': (e) => {
522
339
  this.updateFromCallResponse(e.call);
@@ -563,22 +380,20 @@ export class CallState {
563
380
  'call.session_participant_left': this.updateFromSessionParticipantLeft,
564
381
  'call.session_started': (e) => this.updateFromCallResponse(e.call),
565
382
  'call.transcription_started': () => {
566
- this.setCurrentValue(this.transcribingSubject, true);
383
+ setCurrentValue(this.transcribingSubject, true);
567
384
  },
568
385
  'call.transcription_stopped': () => {
569
- this.setCurrentValue(this.transcribingSubject, false);
386
+ setCurrentValue(this.transcribingSubject, false);
570
387
  },
571
388
  'call.transcription_failed': () => {
572
- this.setCurrentValue(this.transcribingSubject, false);
389
+ setCurrentValue(this.transcribingSubject, false);
573
390
  },
574
391
  'call.unblocked_user': this.unblockUser,
575
392
  'call.updated': (e) => this.updateFromCallResponse(e.call),
576
- };
393
+ } satisfies CallStateEventHandlers;
577
394
  }
578
395
 
579
- /**
580
- * Runs the cleanup tasks.
581
- */
396
+ /** Runs the cleanup tasks. */
582
397
  dispose = () => {
583
398
  for (const [ccKey, taskId] of this.closedCaptionsTasks.entries()) {
584
399
  clearTimeout(taskId);
@@ -596,43 +411,20 @@ export class CallState {
596
411
  setSortParticipantsBy = (comparator: Comparator<StreamVideoParticipant>) => {
597
412
  this.sortParticipantsBy = comparator;
598
413
  // trigger re-sorting of participants
599
- this.setCurrentValue(this.participantsSubject, (ps) => ps);
414
+ setCurrentValue(this.participantsSubject, (ps) => ps);
600
415
  };
601
416
 
602
- /**
603
- * Returns the comparator currently used to sort the participants.
604
- */
417
+ /** Returns the comparator currently used to sort the participants. */
605
418
  getSortParticipantsBy = (): Comparator<StreamVideoParticipant> => {
606
419
  return this.sortParticipantsBy;
607
420
  };
608
421
 
609
- /**
610
- * Gets the current value of an observable, or undefined if the observable has
611
- * not emitted a value yet.
612
- *
613
- * @param observable$ the observable to get the value from.
614
- */
615
- getCurrentValue = RxUtils.getCurrentValue;
616
-
617
- /**
618
- * Updates the value of the provided Subject.
619
- * An `update` can either be a new value or a function which takes
620
- * the current value and returns a new value.
621
- *
622
- * @internal
623
- *
624
- * @param subject the subject to update.
625
- * @param update the update to apply to the subject.
626
- * @return the updated value.
627
- */
628
- setCurrentValue = RxUtils.setCurrentValue;
629
-
630
422
  /**
631
423
  * The server-side counted number of participants connected to the current call.
632
424
  * This number includes the anonymous participants as well.
633
425
  */
634
426
  get participantCount() {
635
- return this.getCurrentValue(this.participantCount$);
427
+ return getCurrentValue(this.participantCountSubject);
636
428
  }
637
429
 
638
430
  /**
@@ -642,7 +434,7 @@ export class CallState {
642
434
  * @param count the number of participants.
643
435
  */
644
436
  setParticipantCount = (count: Patch<number>) => {
645
- return this.setCurrentValue(this.participantCountSubject, count);
437
+ return setCurrentValue(this.participantCountSubject, count);
646
438
  };
647
439
 
648
440
  /**
@@ -650,7 +442,7 @@ export class CallState {
650
442
  * Useful for displaying the call duration.
651
443
  */
652
444
  get startedAt() {
653
- return this.getCurrentValue(this.startedAt$);
445
+ return getCurrentValue(this.startedAtSubject);
654
446
  }
655
447
 
656
448
  /**
@@ -660,14 +452,12 @@ export class CallState {
660
452
  * @param startedAt the time the call session actually started.
661
453
  */
662
454
  setStartedAt = (startedAt: Patch<Date | undefined>) => {
663
- return this.setCurrentValue(this.startedAtSubject, startedAt);
455
+ return setCurrentValue(this.startedAtSubject, startedAt);
664
456
  };
665
457
 
666
- /**
667
- * Returns whether closed captions are enabled in the current call.
668
- */
458
+ /** Returns whether closed captions are enabled in the current call. */
669
459
  get captioning() {
670
- return this.getCurrentValue(this.captioning$);
460
+ return getCurrentValue(this.captioningSubject);
671
461
  }
672
462
 
673
463
  /**
@@ -677,7 +467,7 @@ export class CallState {
677
467
  * @param captioning the closed captioning state.
678
468
  */
679
469
  setCaptioning = (captioning: boolean) => {
680
- return RxUtils.updateValue(this.captioningSubject, captioning);
470
+ return updateValue(this.captioningSubject, captioning);
681
471
  };
682
472
 
683
473
  /**
@@ -685,7 +475,7 @@ export class CallState {
685
475
  * This number includes the anonymous participants as well.
686
476
  */
687
477
  get anonymousParticipantCount() {
688
- return this.getCurrentValue(this.anonymousParticipantCount$);
478
+ return getCurrentValue(this.anonymousParticipantCountSubject);
689
479
  }
690
480
 
691
481
  /**
@@ -695,21 +485,17 @@ export class CallState {
695
485
  * @param count the number of anonymous participants.
696
486
  */
697
487
  setAnonymousParticipantCount = (count: Patch<number>) => {
698
- return this.setCurrentValue(this.anonymousParticipantCountSubject, count);
488
+ return setCurrentValue(this.anonymousParticipantCountSubject, count);
699
489
  };
700
490
 
701
- /**
702
- * The list of participants in the current call.
703
- */
491
+ /** The list of participants in the current call. */
704
492
  get participants() {
705
- return this.getCurrentValue(this.participants$);
493
+ return getCurrentValue(this.participants$);
706
494
  }
707
495
 
708
- /**
709
- * The stable list of participants in the current call, unsorted.
710
- */
496
+ /** The stable list of participants in the current call, unsorted. */
711
497
  get rawParticipants() {
712
- return this.getCurrentValue(this.rawParticipants$);
498
+ return getCurrentValue(this.rawParticipants$);
713
499
  }
714
500
 
715
501
  /**
@@ -732,49 +518,37 @@ export class CallState {
732
518
  * @param participants the list of participants.
733
519
  */
734
520
  setParticipants = (participants: Patch<StreamVideoParticipant[]>) => {
735
- return this.setCurrentValue(this.participantsSubject, participants);
521
+ return setCurrentValue(this.participantsSubject, participants);
736
522
  };
737
523
 
738
- /**
739
- * The local participant in the current call.
740
- */
524
+ /** The local participant in the current call. */
741
525
  get localParticipant() {
742
- return this.getCurrentValue(this.localParticipant$);
526
+ return getCurrentValue(this.localParticipant$);
743
527
  }
744
528
 
745
- /**
746
- * The list of remote participants in the current call.
747
- */
529
+ /** The list of remote participants in the current call. */
748
530
  get remoteParticipants() {
749
- return this.getCurrentValue(this.remoteParticipants$);
531
+ return getCurrentValue(this.remoteParticipants$);
750
532
  }
751
533
 
752
- /**
753
- * The dominant speaker in the current call.
754
- */
534
+ /** The dominant speaker in the current call. */
755
535
  get dominantSpeaker() {
756
- return this.getCurrentValue(this.dominantSpeaker$);
536
+ return getCurrentValue(this.dominantSpeaker$);
757
537
  }
758
538
 
759
- /**
760
- * The list of pinned participants in the current call.
761
- */
539
+ /** The list of pinned participants in the current call. */
762
540
  get pinnedParticipants() {
763
- return this.getCurrentValue(this.pinnedParticipants$);
541
+ return getCurrentValue(this.pinnedParticipants$);
764
542
  }
765
543
 
766
- /**
767
- * Tell if there is an ongoing screen share in this call.
768
- */
544
+ /** Tell if there is an ongoing screen share in this call. */
769
545
  get hasOngoingScreenShare() {
770
- return this.getCurrentValue(this.hasOngoingScreenShare$);
546
+ return getCurrentValue(this.hasOngoingScreenShare$);
771
547
  }
772
548
 
773
- /**
774
- * The calling state.
775
- */
549
+ /** The calling state. */
776
550
  get callingState() {
777
- return this.getCurrentValue(this.callingState$);
551
+ return getCurrentValue(this.callingStateSubject);
778
552
  }
779
553
 
780
554
  /**
@@ -784,14 +558,12 @@ export class CallState {
784
558
  * @param state the new calling state.
785
559
  */
786
560
  setCallingState = (state: Patch<CallingState>) => {
787
- return this.setCurrentValue(this.callingStateSubject, state);
561
+ return setCurrentValue(this.callingStateSubject, state);
788
562
  };
789
563
 
790
- /**
791
- * The call stats report.
792
- */
564
+ /** The call stats report. */
793
565
  get callStatsReport() {
794
- return this.getCurrentValue(this.callStatsReport$);
566
+ return getCurrentValue(this.callStatsReportSubject);
795
567
  }
796
568
 
797
569
  /**
@@ -809,14 +581,12 @@ export class CallState {
809
581
  * @param report the report to set.
810
582
  */
811
583
  setCallStatsReport = (report: Patch<CallStatsReport | undefined>) => {
812
- return this.setCurrentValue(this.callStatsReportSubject, report);
584
+ return setCurrentValue(this.callStatsReportSubject, report);
813
585
  };
814
586
 
815
- /**
816
- * The members of the current call.
817
- */
587
+ /** The members of the current call. */
818
588
  get members() {
819
- return this.getCurrentValue(this.members$);
589
+ return getCurrentValue(this.membersSubject);
820
590
  }
821
591
 
822
592
  /**
@@ -826,14 +596,12 @@ export class CallState {
826
596
  * @param members the members to set.
827
597
  */
828
598
  setMembers = (members: Patch<MemberResponse[]>) => {
829
- this.setCurrentValue(this.membersSubject, members);
599
+ setCurrentValue(this.membersSubject, members);
830
600
  };
831
601
 
832
- /**
833
- * The capabilities of the current user for the current call.
834
- */
602
+ /** The capabilities of the current user for the current call. */
835
603
  get ownCapabilities() {
836
- return this.getCurrentValue(this.ownCapabilities$);
604
+ return getCurrentValue(this.ownCapabilities$);
837
605
  }
838
606
 
839
607
  /**
@@ -843,7 +611,7 @@ export class CallState {
843
611
  * @param capabilities the capabilities to set.
844
612
  */
845
613
  setOwnCapabilities = (capabilities: Patch<OwnCapability[]>) => {
846
- return this.setCurrentValue(this.ownCapabilitiesSubject, capabilities);
614
+ return setCurrentValue(this.ownCapabilitiesSubject, capabilities);
847
615
  };
848
616
 
849
617
  /**
@@ -853,14 +621,12 @@ export class CallState {
853
621
  * @param grants the grants to set.
854
622
  */
855
623
  setCallGrants = (grants: Patch<CallGrants>) => {
856
- return this.setCurrentValue(this.callGrantsSubject, grants);
624
+ return setCurrentValue(this.callGrantsSubject, grants);
857
625
  };
858
626
 
859
- /**
860
- * The backstage state.
861
- */
627
+ /** The backstage state. */
862
628
  get backstage() {
863
- return this.getCurrentValue(this.backstage$);
629
+ return getCurrentValue(this.backstageSubject);
864
630
  }
865
631
 
866
632
  /**
@@ -868,28 +634,22 @@ export class CallState {
868
634
  * @param backstage the backstage state.
869
635
  */
870
636
  setBackstage = (backstage: Patch<boolean>) => {
871
- return this.setCurrentValue(this.backstageSubject, backstage);
637
+ return setCurrentValue(this.backstageSubject, backstage);
872
638
  };
873
639
 
874
- /**
875
- * Will provide the list of blocked user IDs.
876
- */
640
+ /** Will provide the list of blocked user IDs. */
877
641
  get blockedUserIds() {
878
- return this.getCurrentValue(this.blockedUserIds$);
642
+ return getCurrentValue(this.blockedUserIdsSubject);
879
643
  }
880
644
 
881
- /**
882
- * Will provide the time when this call has been created.
883
- */
645
+ /** Will provide the time when this call has been created. */
884
646
  get createdAt() {
885
- return this.getCurrentValue(this.createdAt$);
647
+ return getCurrentValue(this.createdAtSubject);
886
648
  }
887
649
 
888
- /**
889
- * Will provide the time when this call has been ended.
890
- */
650
+ /** Will provide the time when this call has been ended. */
891
651
  get endedAt() {
892
- return this.getCurrentValue(this.endedAt$);
652
+ return getCurrentValue(this.endedAtSubject);
893
653
  }
894
654
 
895
655
  /**
@@ -897,119 +657,91 @@ export class CallState {
897
657
  * @param endedAt the time when this call has been ended.
898
658
  */
899
659
  setEndedAt = (endedAt: Patch<Date | undefined>) => {
900
- return this.setCurrentValue(this.endedAtSubject, endedAt);
660
+ return setCurrentValue(this.endedAtSubject, endedAt);
901
661
  };
902
662
 
903
- /**
904
- * Will provide the time when this call has been scheduled to start.
905
- */
663
+ /** Will provide the time when this call has been scheduled to start. */
906
664
  get startsAt() {
907
- return this.getCurrentValue(this.startsAt$);
665
+ return getCurrentValue(this.startsAtSubject);
908
666
  }
909
667
 
910
- /**
911
- * Will provide the time when this call has been updated.
912
- */
668
+ /** Will provide the time when this call has been updated. */
913
669
  get updatedAt() {
914
- return this.getCurrentValue(this.updatedAt$);
670
+ return getCurrentValue(this.updatedAtSubject);
915
671
  }
916
672
 
917
- /**
918
- * Will provide the user who created this call.
919
- */
673
+ /** Will provide the user who created this call. */
920
674
  get createdBy() {
921
- return this.getCurrentValue(this.createdBy$);
675
+ return getCurrentValue(this.createdBySubject);
922
676
  }
923
677
 
924
- /**
925
- * Will provide the custom data of this call.
926
- */
678
+ /** Will provide the custom data of this call. */
927
679
  get custom() {
928
- return this.getCurrentValue(this.custom$);
680
+ return getCurrentValue(this.customSubject);
929
681
  }
930
682
 
931
683
  /**
932
684
  * Will provide the egress data of this call.
933
685
  */
934
686
  get egress() {
935
- return this.getCurrentValue(this.egress$);
687
+ return getCurrentValue(this.egressSubject);
936
688
  }
937
689
 
938
- /**
939
- * Will provide the ingress data of this call.
940
- */
690
+ /** Will provide the ingress data of this call. */
941
691
  get ingress() {
942
- return this.getCurrentValue(this.ingress$);
692
+ return getCurrentValue(this.ingressSubject);
943
693
  }
944
694
 
945
- /**
946
- * Will provide the composite recording state of this call.
947
- */
695
+ /** Will provide the composite recording state of this call. */
948
696
  get recording() {
949
- return this.getCurrentValue(this.recording$);
697
+ return getCurrentValue(this.recordingSubject);
950
698
  }
951
699
 
952
- /**
953
- * Will provide the individual recording state of this call.
954
- */
700
+ /** Will provide the individual recording state of this call. */
955
701
  get individualRecording() {
956
- return this.getCurrentValue(this.individualRecording$);
702
+ return getCurrentValue(this.individualRecordingSubject);
957
703
  }
958
704
 
959
- /**
960
- * Will provide the raw recording state of this call.
961
- */
705
+ /** Will provide the raw recording state of this call. */
962
706
  get rawRecording() {
963
- return this.getCurrentValue(this.rawRecording$);
707
+ return getCurrentValue(this.rawRecordingSubject);
964
708
  }
965
709
 
966
- /**
967
- * Will provide the session data of this call.
968
- */
710
+ /** Will provide the session data of this call. */
969
711
  get session() {
970
- return this.getCurrentValue(this.session$);
712
+ return getCurrentValue(this.sessionSubject);
971
713
  }
972
714
 
973
- /**
974
- * Will provide the settings of this call.
975
- */
715
+ /** Will provide the settings of this call. */
976
716
  get settings() {
977
- return this.getCurrentValue(this.settings$);
717
+ return getCurrentValue(this.settingsSubject);
978
718
  }
979
719
 
980
- /**
981
- * Will provide the transcribing state of this call.
982
- */
720
+ /** Will provide the transcribing state of this call. */
983
721
  get transcribing() {
984
- return this.getCurrentValue(this.transcribing$);
722
+ return getCurrentValue(this.transcribingSubject);
985
723
  }
986
724
 
987
- /**
988
- * Whether end-to-end encryption is active for this call.
989
- */
725
+ /** Whether end-to-end encryption is active for this call. */
990
726
  get e2eeEnabled() {
991
- return this.getCurrentValue(this.e2eeEnabled$);
727
+ return getCurrentValue(this.e2eeEnabledSubject);
992
728
  }
993
729
 
994
- /**
995
- * Will provide the user who ended this call.
996
- */
730
+ /** Will provide the user who ended this call. */
997
731
  get endedBy() {
998
- return this.getCurrentValue(this.endedBy$);
732
+ return getCurrentValue(this.endedBySubject);
999
733
  }
1000
734
 
1001
735
  /**
1002
736
  * Will provide the thumbnails of this call, if enabled in the call settings.
1003
737
  */
1004
738
  get thumbnails() {
1005
- return this.getCurrentValue(this.thumbnails$);
739
+ return getCurrentValue(this.thumbnailsSubject);
1006
740
  }
1007
741
 
1008
- /**
1009
- * Returns the current queue of closed captions.
1010
- */
742
+ /** Returns the current queue of closed captions. */
1011
743
  get closedCaptions() {
1012
- return this.getCurrentValue(this.closedCaptions$);
744
+ return getCurrentValue(this.closedCaptionsSubject);
1013
745
  }
1014
746
 
1015
747
  /**
@@ -1024,9 +756,7 @@ export class CallState {
1024
756
  return this.participants.find((p) => p.sessionId === sessionId);
1025
757
  };
1026
758
 
1027
- /**
1028
- * Returns a new lookup table of participants indexed by their session ID.
1029
- */
759
+ /** Returns a new lookup table of participants indexed by their session ID. */
1030
760
  getParticipantLookupBySessionId = () => {
1031
761
  return this.participants.reduce<{
1032
762
  [sessionId: string]: StreamVideoParticipant | undefined;
@@ -1318,39 +1048,39 @@ export class CallState {
1318
1048
  */
1319
1049
  updateFromCallResponse = (call: CallResponse) => {
1320
1050
  this.setBackstage(call.backstage);
1321
- this.setCurrentValue(this.blockedUserIdsSubject, call.blocked_user_ids);
1322
- this.setCurrentValue(this.createdAtSubject, new Date(call.created_at));
1323
- this.setCurrentValue(this.updatedAtSubject, new Date(call.updated_at));
1324
- this.setCurrentValue(
1051
+ setCurrentValue(this.blockedUserIdsSubject, call.blocked_user_ids);
1052
+ setCurrentValue(this.createdAtSubject, new Date(call.created_at));
1053
+ setCurrentValue(this.updatedAtSubject, new Date(call.updated_at));
1054
+ setCurrentValue(
1325
1055
  this.startsAtSubject,
1326
1056
  call.starts_at ? new Date(call.starts_at) : undefined,
1327
1057
  );
1328
1058
  this.setEndedAt(call.ended_at ? new Date(call.ended_at) : undefined);
1329
- this.setCurrentValue(this.createdBySubject, call.created_by);
1330
- this.setCurrentValue(this.customSubject, call.custom);
1331
- this.setCurrentValue(this.egressSubject, call.egress);
1332
- this.setCurrentValue(this.ingressSubject, call.ingress);
1059
+ setCurrentValue(this.createdBySubject, call.created_by);
1060
+ setCurrentValue(this.customSubject, call.custom);
1061
+ setCurrentValue(this.egressSubject, call.egress);
1062
+ setCurrentValue(this.ingressSubject, call.ingress);
1333
1063
  const { individual_recording, composite_recording, raw_recording } =
1334
1064
  call.egress;
1335
- this.setCurrentValue(
1065
+ setCurrentValue(
1336
1066
  this.recordingSubject,
1337
1067
  call.recording || composite_recording?.status === 'running',
1338
1068
  );
1339
- this.setCurrentValue(
1069
+ setCurrentValue(
1340
1070
  this.individualRecordingSubject,
1341
1071
  individual_recording?.status === 'running',
1342
1072
  );
1343
- this.setCurrentValue(
1073
+ setCurrentValue(
1344
1074
  this.rawRecordingSubject,
1345
1075
  raw_recording?.status === 'running',
1346
1076
  );
1347
1077
 
1348
- const s = this.setCurrentValue(this.sessionSubject, call.session);
1078
+ const s = setCurrentValue(this.sessionSubject, call.session);
1349
1079
  this.updateParticipantCountFromSession(s);
1350
- this.setCurrentValue(this.settingsSubject, call.settings);
1351
- this.setCurrentValue(this.transcribingSubject, call.transcribing);
1352
- this.setCurrentValue(this.captioningSubject, call.captioning);
1353
- this.setCurrentValue(this.thumbnailsSubject, call.thumbnails);
1080
+ setCurrentValue(this.settingsSubject, call.settings);
1081
+ setCurrentValue(this.transcribingSubject, call.transcribing);
1082
+ setCurrentValue(this.captioningSubject, call.captioning);
1083
+ setCurrentValue(this.thumbnailsSubject, call.thumbnails);
1354
1084
  };
1355
1085
 
1356
1086
  /**
@@ -1362,7 +1092,7 @@ export class CallState {
1362
1092
  * @param ringState the ring state as returned by the coordinator.
1363
1093
  */
1364
1094
  updateFromRingState = (ringState: GetCallRingStateResponse) => {
1365
- this.setCurrentValue(this.sessionSubject, (session) => {
1095
+ setCurrentValue(this.sessionSubject, (session) => {
1366
1096
  if (!session || session.id !== ringState.session_id) return session;
1367
1097
  return {
1368
1098
  ...session,
@@ -1421,26 +1151,26 @@ export class CallState {
1421
1151
  this.setAnonymousParticipantCount(participantCount?.anonymous || 0);
1422
1152
  this.setStartedAt(startedAt ? Timestamp.toDate(startedAt) : new Date());
1423
1153
  this.setServerSidePins(pins);
1424
- this.setCurrentValue(this.e2eeEnabledSubject, e2EeEnabled);
1154
+ setCurrentValue(this.e2eeEnabledSubject, e2EeEnabled);
1425
1155
  };
1426
1156
 
1427
1157
  private updateFromMemberRemoved = (event: CallMemberRemovedEvent) => {
1428
1158
  this.updateFromCallResponse(event.call);
1429
- this.setCurrentValue(this.membersSubject, (members) =>
1159
+ setCurrentValue(this.membersSubject, (members) =>
1430
1160
  members.filter((m) => event.members.indexOf(m.user_id) === -1),
1431
1161
  );
1432
1162
  };
1433
1163
 
1434
1164
  private updateFromMemberAdded = (event: CallMemberAddedEvent) => {
1435
1165
  this.updateFromCallResponse(event.call);
1436
- this.setCurrentValue(this.membersSubject, (members) => [
1166
+ setCurrentValue(this.membersSubject, (members) => [
1437
1167
  ...members,
1438
1168
  ...event.members,
1439
1169
  ]);
1440
1170
  };
1441
1171
 
1442
1172
  private updateFromHLSBroadcastStopped = () => {
1443
- this.setCurrentValue(this.egressSubject, (egress = defaultEgress) => ({
1173
+ setCurrentValue(this.egressSubject, (egress = defaultEgress) => ({
1444
1174
  ...egress,
1445
1175
  broadcasting: false,
1446
1176
  hls: {
@@ -1451,7 +1181,7 @@ export class CallState {
1451
1181
  };
1452
1182
 
1453
1183
  private updateFromHLSBroadcastingFailed = () => {
1454
- this.setCurrentValue(this.egressSubject, (egress = defaultEgress) => ({
1184
+ setCurrentValue(this.egressSubject, (egress = defaultEgress) => ({
1455
1185
  ...egress,
1456
1186
  broadcasting: false,
1457
1187
  hls: {
@@ -1467,11 +1197,11 @@ export class CallState {
1467
1197
  ) => {
1468
1198
  // handle the legacy format, where `type` is absent in the emitted events
1469
1199
  if (type === undefined || type === 'composite') {
1470
- this.setCurrentValue(this.recordingSubject, running);
1200
+ setCurrentValue(this.recordingSubject, running);
1471
1201
  } else if (type === 'individual') {
1472
- this.setCurrentValue(this.individualRecordingSubject, running);
1202
+ setCurrentValue(this.individualRecordingSubject, running);
1473
1203
  } else if (type === 'raw') {
1474
- this.setCurrentValue(this.rawRecordingSubject, running);
1204
+ setCurrentValue(this.rawRecordingSubject, running);
1475
1205
  } else {
1476
1206
  ensureExhausted(type, 'Unknown recording type');
1477
1207
  }
@@ -1494,7 +1224,7 @@ export class CallState {
1494
1224
  private updateFromSessionParticipantCountUpdate = (
1495
1225
  event: CallSessionParticipantCountsUpdatedEvent,
1496
1226
  ) => {
1497
- const s = this.setCurrentValue(this.sessionSubject, (session) => {
1227
+ const s = setCurrentValue(this.sessionSubject, (session) => {
1498
1228
  if (!session) return session;
1499
1229
  return {
1500
1230
  ...session,
@@ -1508,7 +1238,7 @@ export class CallState {
1508
1238
  private updateFromSessionParticipantLeft = (
1509
1239
  event: CallSessionParticipantLeftEvent,
1510
1240
  ) => {
1511
- const s = this.setCurrentValue(this.sessionSubject, (session) => {
1241
+ const s = setCurrentValue(this.sessionSubject, (session) => {
1512
1242
  if (!session) return session;
1513
1243
  const { participants, participants_count_by_role } = session;
1514
1244
  const { user, user_session_id } = event.participant;
@@ -1532,7 +1262,7 @@ export class CallState {
1532
1262
  private updateFromSessionParticipantJoined = (
1533
1263
  event: CallSessionParticipantJoinedEvent,
1534
1264
  ) => {
1535
- const s = this.setCurrentValue(this.sessionSubject, (session) => {
1265
+ const s = setCurrentValue(this.sessionSubject, (session) => {
1536
1266
  if (!session) return session;
1537
1267
  const { participants, participants_count_by_role } = session;
1538
1268
  const { user, user_session_id } = event.participant;
@@ -1572,7 +1302,7 @@ export class CallState {
1572
1302
  event: CallMemberUpdatedEvent | CallMemberUpdatedPermissionEvent,
1573
1303
  ) => {
1574
1304
  this.updateFromCallResponse(event.call);
1575
- this.setCurrentValue(this.membersSubject, (members) =>
1305
+ setCurrentValue(this.membersSubject, (members) =>
1576
1306
  members.map((member) => {
1577
1307
  const memberUpdate = event.members.find(
1578
1308
  (m) => m.user_id === member.user_id,
@@ -1602,14 +1332,14 @@ export class CallState {
1602
1332
  };
1603
1333
 
1604
1334
  private unblockUser = (event: UnblockedUserEvent) => {
1605
- this.setCurrentValue(this.blockedUserIdsSubject, (current) => {
1335
+ setCurrentValue(this.blockedUserIdsSubject, (current) => {
1606
1336
  if (!current) return current;
1607
1337
  return current.filter((id) => id !== event.user.id);
1608
1338
  });
1609
1339
  };
1610
1340
 
1611
1341
  private blockUser = (event: BlockedUserEvent) => {
1612
- this.setCurrentValue(this.blockedUserIdsSubject, (current) => [
1342
+ setCurrentValue(this.blockedUserIdsSubject, (current) => [
1613
1343
  ...(current || []),
1614
1344
  event.user.id,
1615
1345
  ]);
@@ -1622,7 +1352,7 @@ export class CallState {
1622
1352
  };
1623
1353
 
1624
1354
  private updateFromClosedCaptions = (event: ClosedCaptionEvent) => {
1625
- this.setCurrentValue(this.closedCaptionsSubject, (queue) => {
1355
+ setCurrentValue(this.closedCaptionsSubject, (queue) => {
1626
1356
  const { closed_caption } = event;
1627
1357
 
1628
1358
  const keyOf = (c: CallClosedCaption) => `${c.speaker_id}/${c.start_time}`;
@@ -1638,7 +1368,7 @@ export class CallState {
1638
1368
  // schedule the removal of the closed caption after the retention time
1639
1369
  if (visibilityDurationMs > 0) {
1640
1370
  const taskId = setTimeout(() => {
1641
- this.setCurrentValue(this.closedCaptionsSubject, (captions) =>
1371
+ setCurrentValue(this.closedCaptionsSubject, (captions) =>
1642
1372
  captions.filter((caption) => caption !== closed_caption),
1643
1373
  );
1644
1374
  this.closedCaptionsTasks.delete(currentKey);