@stream-io/video-client 0.2.3 → 0.3.0

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 (51) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/index.browser.es.js +320 -419
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +320 -419
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +320 -419
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +3 -8
  9. package/dist/src/StreamVideoClient.d.ts +3 -1
  10. package/dist/src/events/call-permissions.d.ts +0 -5
  11. package/dist/src/events/call.d.ts +0 -6
  12. package/dist/src/events/index.d.ts +0 -6
  13. package/dist/src/rtc/Dispatcher.d.ts +2 -2
  14. package/dist/src/rtc/Publisher.d.ts +0 -1
  15. package/dist/src/store/CallState.d.ts +164 -89
  16. package/dist/src/types.d.ts +1 -7
  17. package/dist/version.d.ts +1 -1
  18. package/package.json +1 -1
  19. package/src/Call.ts +30 -41
  20. package/src/StreamVideoClient.ts +14 -17
  21. package/src/events/__tests__/call-permissions.test.ts +1 -61
  22. package/src/events/__tests__/call.test.ts +5 -50
  23. package/src/events/call-permissions.ts +0 -14
  24. package/src/events/call.ts +5 -16
  25. package/src/events/callEventHandlers.ts +2 -57
  26. package/src/events/index.ts +0 -6
  27. package/src/rtc/Dispatcher.ts +2 -2
  28. package/src/rtc/Publisher.ts +4 -6
  29. package/src/store/CallState.ts +475 -119
  30. package/src/store/__tests__/CallState.test.ts +447 -1
  31. package/src/types.ts +0 -8
  32. package/dist/src/events/__tests__/backstage.test.d.ts +0 -1
  33. package/dist/src/events/__tests__/members.test.d.ts +0 -1
  34. package/dist/src/events/__tests__/recording.test.d.ts +0 -1
  35. package/dist/src/events/__tests__/sessions.test.d.ts +0 -1
  36. package/dist/src/events/backstage.d.ts +0 -6
  37. package/dist/src/events/members.d.ts +0 -18
  38. package/dist/src/events/moderation.d.ts +0 -14
  39. package/dist/src/events/reactions.d.ts +0 -8
  40. package/dist/src/events/recording.d.ts +0 -18
  41. package/dist/src/events/sessions.d.ts +0 -26
  42. package/src/events/__tests__/backstage.test.ts +0 -15
  43. package/src/events/__tests__/members.test.ts +0 -135
  44. package/src/events/__tests__/recording.test.ts +0 -65
  45. package/src/events/__tests__/sessions.test.ts +0 -135
  46. package/src/events/backstage.ts +0 -15
  47. package/src/events/members.ts +0 -62
  48. package/src/events/moderation.ts +0 -35
  49. package/src/events/reactions.ts +0 -30
  50. package/src/events/recording.ts +0 -64
  51. package/src/events/sessions.ts +0 -102
@@ -217,16 +217,15 @@ export class StreamVideoClient {
217
217
  }
218
218
 
219
219
  this.logger('info', `New call created and registered: ${call.cid}`);
220
- this.writeableStateStore.registerCall(
221
- new Call({
222
- streamClient: this.streamClient,
223
- type: call.type,
224
- id: call.id,
225
- metadata: call,
226
- members,
227
- clientStore: this.writeableStateStore,
228
- }),
229
- );
220
+ const newCall = new Call({
221
+ streamClient: this.streamClient,
222
+ type: call.type,
223
+ id: call.id,
224
+ members,
225
+ clientStore: this.writeableStateStore,
226
+ });
227
+ newCall.state.updateFromCallResponse(call);
228
+ this.writeableStateStore.registerCall(newCall);
230
229
  }),
231
230
  );
232
231
 
@@ -246,7 +245,6 @@ export class StreamVideoClient {
246
245
  // if `call.created` was received before `call.ring`.
247
246
  // In that case, we cleanup the already tracked call.
248
247
  const prevCall = this.writeableStateStore.findCall(call.type, call.id);
249
- const prevMetadata = prevCall?.state.metadata;
250
248
  await prevCall?.leave();
251
249
  // we create a new call
252
250
  const theCall = new Call({
@@ -256,8 +254,8 @@ export class StreamVideoClient {
256
254
  members,
257
255
  clientStore: this.writeableStateStore,
258
256
  ringing: true,
259
- metadata: prevMetadata,
260
257
  });
258
+ theCall.state.updateFromCallResponse(call);
261
259
  // we fetch the latest metadata for the call from the server
262
260
  await theCall.get();
263
261
  this.writeableStateStore.registerCall(theCall);
@@ -357,12 +355,12 @@ export class StreamVideoClient {
357
355
  streamClient: this.streamClient,
358
356
  id: c.call.id,
359
357
  type: c.call.type,
360
- metadata: c.call,
361
358
  members: c.members,
362
359
  ownCapabilities: c.own_capabilities,
363
360
  watching: data.watch,
364
361
  clientStore: this.writeableStateStore,
365
362
  });
363
+ call.state.updateFromCallResponse(c.call);
366
364
  if (data.watch) {
367
365
  this.writeableStateStore.registerCall(call);
368
366
  }
@@ -374,10 +372,9 @@ export class StreamVideoClient {
374
372
  };
375
373
  };
376
374
 
377
- queryUsers = async () => {
378
- console.log('Querying users is not implemented yet.');
379
- };
380
-
375
+ /**
376
+ * Returns a list of available data centers available for hosting calls.
377
+ */
381
378
  edges = async () => {
382
379
  return this.streamClient.get<GetEdgesResponse>(`/edges`);
383
380
  };
@@ -1,69 +1,9 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { CallState } from '../../store';
3
- import {
4
- watchCallGrantsUpdated,
5
- watchCallPermissionsUpdated,
6
- } from '../call-permissions';
3
+ import { watchCallGrantsUpdated } from '../call-permissions';
7
4
  import { OwnCapability } from '../../gen/coordinator';
8
- import { ConnectionQuality } from '../../gen/video/sfu/models/models';
9
5
 
10
6
  describe('Call Permission Events', () => {
11
- it('handles call.permissions_updated', () => {
12
- const state = new CallState();
13
- state.setParticipants([
14
- {
15
- userId: 'test',
16
- name: 'test',
17
- sessionId: 'test',
18
- isDominantSpeaker: false,
19
- isSpeaking: false,
20
- audioLevel: 0,
21
- image: '',
22
- publishedTracks: [],
23
- connectionQuality: ConnectionQuality.EXCELLENT,
24
- roles: [],
25
- trackLookupPrefix: '',
26
- isLocalParticipant: true,
27
- },
28
- ]);
29
- const handler = watchCallPermissionsUpdated(state);
30
- handler({
31
- type: 'call.permissions_updated',
32
- created_at: '',
33
- call_cid: 'development:12345',
34
- own_capabilities: [OwnCapability.SEND_AUDIO, OwnCapability.SEND_VIDEO],
35
- user: {
36
- id: 'test',
37
- created_at: '',
38
- role: '',
39
- updated_at: '',
40
- custom: {},
41
- teams: [],
42
- },
43
- });
44
-
45
- expect(state.ownCapabilities).toEqual([
46
- OwnCapability.SEND_AUDIO,
47
- OwnCapability.SEND_VIDEO,
48
- ]);
49
-
50
- handler({
51
- type: 'call.permissions_updated',
52
- created_at: '',
53
- call_cid: 'development:12345',
54
- own_capabilities: [OwnCapability.SEND_VIDEO],
55
- user: {
56
- id: 'test',
57
- created_at: '',
58
- role: '',
59
- updated_at: '',
60
- custom: {},
61
- teams: [],
62
- },
63
- });
64
- expect(state.ownCapabilities).toEqual([OwnCapability.SEND_VIDEO]);
65
- });
66
-
67
7
  it('handles sfu.callGrantsUpdated', () => {
68
8
  const state = new CallState();
69
9
  const handler = watchCallGrantsUpdated(state);
@@ -1,60 +1,15 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
- import {
3
- CallingState,
4
- CallState,
5
- StreamVideoWriteableStateStore,
6
- } from '../../store';
7
- import {
8
- watchCallAccepted,
9
- watchCallEnded,
10
- watchCallRejected,
11
- watchCallUpdated,
12
- } from '../call';
2
+ import { CallingState, StreamVideoWriteableStateStore } from '../../store';
3
+ import { watchCallAccepted, watchCallEnded, watchCallRejected } from '../call';
13
4
  import {
14
5
  CallAcceptedEvent,
15
6
  CallEndedEvent,
16
7
  CallResponse,
17
- CallUpdatedEvent,
18
8
  } from '../../gen/coordinator';
19
9
  import { Call } from '../../Call';
20
10
  import { StreamClient } from '../../coordinator/connection/client';
21
11
 
22
12
  describe('Call ringing events', () => {
23
- describe('call.updated', () => {
24
- it(`will update the call's metadata`, () => {
25
- const state = new CallState();
26
- const handler = watchCallUpdated(state);
27
- const event: CallUpdatedEvent = {
28
- type: 'call.updated',
29
- call_cid: 'development:12345',
30
- // @ts-expect-error
31
- call: {
32
- cid: 'development:12345',
33
- },
34
- };
35
-
36
- // @ts-ignore
37
- handler(event);
38
- expect(state.metadata).toEqual(event.call);
39
- });
40
-
41
- it(`will ignore unknown events`, () => {
42
- const state = new CallState();
43
- const handler = watchCallUpdated(state);
44
- const event = {
45
- type: 'call.updated.unknown',
46
- call_cid: 'development:12345',
47
- call: {
48
- cid: 'development:12345',
49
- },
50
- };
51
-
52
- // @ts-ignore
53
- handler(event);
54
- expect(state.metadata).toBeUndefined();
55
- });
56
- });
57
-
58
13
  describe(`call.accepted`, () => {
59
14
  it(`will ignore events from the current user`, async () => {
60
15
  const call = fakeCall();
@@ -96,7 +51,7 @@ describe('Call ringing events', () => {
96
51
  describe(`call.rejected`, () => {
97
52
  it(`caller will leave the call if all callees have rejected`, async () => {
98
53
  const call = fakeCall({ currentUserId: 'm1' });
99
- call.state.setMetadata({
54
+ call.state.updateFromCallResponse({
100
55
  ...fakeMetadata(),
101
56
  // @ts-ignore
102
57
  created_by: { id: 'm1' },
@@ -141,7 +96,7 @@ describe('Call ringing events', () => {
141
96
 
142
97
  it(`caller will not leave the call if only one callee rejects`, async () => {
143
98
  const call = fakeCall();
144
- call.state.setMetadata({
99
+ call.state.updateFromCallResponse({
145
100
  ...fakeMetadata(),
146
101
  // @ts-ignore
147
102
  created_by: { id: 'm0' },
@@ -182,7 +137,7 @@ describe('Call ringing events', () => {
182
137
 
183
138
  it('callee will leave the call if caller rejects', async () => {
184
139
  const call = fakeCall({ currentUserId: 'm1' });
185
- call.state.setMetadata({
140
+ call.state.updateFromCallResponse({
186
141
  ...fakeMetadata(),
187
142
  // @ts-ignore
188
143
  created_by: { id: 'm0' },
@@ -1,21 +1,7 @@
1
- import { StreamVideoEvent } from '../coordinator/connection/types';
2
1
  import { CallState } from '../store';
3
2
  import { SfuEvent } from '../gen/video/sfu/event/events';
4
3
  import { OwnCapability } from '../gen/coordinator';
5
4
 
6
- /**
7
- * Event handler that watches for `call.permissions_updated` events
8
- */
9
- export const watchCallPermissionsUpdated = (state: CallState) => {
10
- return function onCallPermissionsUpdated(event: StreamVideoEvent) {
11
- if (event.type !== 'call.permissions_updated') return;
12
- const { localParticipant } = state;
13
- if (event.user.id === localParticipant?.userId) {
14
- state.setOwnCapabilities(event.own_capabilities);
15
- }
16
- };
17
- };
18
-
19
5
  /**
20
6
  * Event handler that watches for `callGrantsUpdated` events.
21
7
  *
@@ -1,4 +1,4 @@
1
- import { CallingState, CallState } from '../store';
1
+ import { CallingState } from '../store';
2
2
  import { StreamVideoEvent } from '../coordinator/connection/types';
3
3
  import { Call } from '../Call';
4
4
 
@@ -76,24 +76,13 @@ export const watchCallRejected = (call: Call) => {
76
76
  export const watchCallEnded = (call: Call) => {
77
77
  return async function onCallCancelled(event: StreamVideoEvent) {
78
78
  if (event.type !== 'call.ended') return;
79
+ const { callingState } = call.state;
79
80
  if (
80
- call.state.callingState === CallingState.RINGING ||
81
- call.state.callingState === CallingState.JOINED ||
82
- call.state.callingState === CallingState.JOINING
81
+ callingState === CallingState.RINGING ||
82
+ callingState === CallingState.JOINED ||
83
+ callingState === CallingState.JOINING
83
84
  ) {
84
- call.state.setMetadata(event.call);
85
85
  await call.leave();
86
86
  }
87
87
  };
88
88
  };
89
-
90
- /**
91
- * An event handler which listens to `call.updated` events
92
- * and updates the given call state accordingly.
93
- */
94
- export const watchCallUpdated = (state: CallState) => {
95
- return function onCallUpdated(event: StreamVideoEvent) {
96
- if (event.type !== 'call.updated') return;
97
- state.setMetadata(event.call);
98
- };
99
- };
@@ -3,31 +3,14 @@ import { Dispatcher } from '../rtc';
3
3
  import { CallState } from '../store';
4
4
  import {
5
5
  watchAudioLevelChanged,
6
- watchBlockedUser,
7
6
  watchCallAccepted,
8
- watchCallBroadcastingStarted,
9
- watchCallBroadcastingStopped,
10
7
  watchCallEnded,
11
8
  watchCallGrantsUpdated,
12
- watchCallLiveStarted,
13
- watchCallMemberAdded,
14
- watchCallMemberRemoved,
15
- watchCallMemberUpdated,
16
- watchCallMemberUpdatedPermission,
17
- watchCallPermissionsUpdated,
18
- watchCallRecordingStarted,
19
- watchCallRecordingStopped,
20
9
  watchCallRejected,
21
- watchCallSessionEnded,
22
- watchCallSessionParticipantJoined,
23
- watchCallSessionParticipantLeft,
24
- watchCallSessionStarted,
25
- watchCallUpdated,
26
10
  watchChangePublishQuality,
27
11
  watchConnectionQualityChanged,
28
12
  watchDominantSpeakerChanged,
29
13
  watchLiveEnded,
30
- watchNewReactions,
31
14
  watchParticipantCountChanged,
32
15
  watchParticipantJoined,
33
16
  watchParticipantLeft,
@@ -35,7 +18,6 @@ import {
35
18
  watchSfuErrorReports,
36
19
  watchTrackPublished,
37
20
  watchTrackUnpublished,
38
- watchUnblockedUser,
39
21
  } from '../events';
40
22
  import {
41
23
  CallEventTypes,
@@ -47,16 +29,6 @@ type RingCallEvents = Extract<
47
29
  'call.accepted' | 'call.rejected'
48
30
  >;
49
31
 
50
- type AllCallEvents = Exclude<
51
- CallEventTypes,
52
- | 'call.created' // handled by StreamVideoClient
53
- | 'call.ring' // handled by StreamVideoClient
54
- | 'call.notification' // not used currently
55
- | 'call.permission_request' // should be handled by the SDK component
56
- | 'custom' // integrators should handle custom events
57
- | RingCallEvents // handled by registerRingingCallEventHandlers
58
- >;
59
-
60
32
  /**
61
33
  * Registers the default event handlers for a call during its lifecycle.
62
34
  *
@@ -69,31 +41,9 @@ export const registerEventHandlers = (
69
41
  state: CallState,
70
42
  dispatcher: Dispatcher,
71
43
  ) => {
72
- const coordinatorEvents: {
73
- [key in AllCallEvents]: (e: StreamCallEvent) => any;
74
- } = {
75
- 'call.blocked_user': watchBlockedUser(state),
76
- 'call.broadcasting_started': watchCallBroadcastingStarted(state),
77
- 'call.broadcasting_stopped': watchCallBroadcastingStopped(state),
78
- 'call.ended': watchCallEnded(call),
79
- 'call.live_started': watchCallLiveStarted(state),
80
- 'call.member_added': watchCallMemberAdded(state),
81
- 'call.member_removed': watchCallMemberRemoved(state),
82
- 'call.member_updated': watchCallMemberUpdated(state),
83
- 'call.member_updated_permission': watchCallMemberUpdatedPermission(state),
84
- 'call.permissions_updated': watchCallPermissionsUpdated(state),
85
- 'call.reaction_new': watchNewReactions(state),
86
- 'call.recording_started': watchCallRecordingStarted(state),
87
- 'call.recording_stopped': watchCallRecordingStopped(state),
88
- 'call.session_started': watchCallSessionStarted(state),
89
- 'call.session_ended': watchCallSessionEnded(state),
90
- 'call.session_participant_joined': watchCallSessionParticipantJoined(state),
91
- 'call.session_participant_left': watchCallSessionParticipantLeft(state),
92
- 'call.unblocked_user': watchUnblockedUser(state),
93
- 'call.updated': watchCallUpdated(state),
94
- 'call.user_muted': () => console.log('call.user_muted received'),
95
- };
96
44
  const eventHandlers = [
45
+ call.on('call.ended', watchCallEnded(call)),
46
+
97
47
  watchLiveEnded(dispatcher, call),
98
48
  watchSfuErrorReports(dispatcher),
99
49
  watchChangePublishQuality(dispatcher, call),
@@ -113,11 +63,6 @@ export const registerEventHandlers = (
113
63
  call.on('pinsUpdated', watchPinsUpdated(state)),
114
64
  ];
115
65
 
116
- Object.keys(coordinatorEvents).forEach((event) => {
117
- const eventName = event as AllCallEvents;
118
- eventHandlers.push(call.on(eventName, coordinatorEvents[eventName]));
119
- });
120
-
121
66
  if (call.ringing) {
122
67
  // these events are only relevant when the call is ringing
123
68
  eventHandlers.push(registerRingingCallEventHandlers(call));
@@ -1,11 +1,5 @@
1
- export * from './backstage';
2
1
  export * from './call';
3
2
  export * from './call-permissions';
4
3
  export * from './internal';
5
- export * from './members';
6
4
  export * from './participant';
7
- export * from './reactions';
8
- export * from './recording';
9
5
  export * from './speaker';
10
- export * from './sessions';
11
- export * from './moderation';
@@ -1,4 +1,4 @@
1
- import { CallEventTypes, Logger } from '../coordinator/connection/types';
1
+ import { EventTypes, Logger } from '../coordinator/connection/types';
2
2
  import type { SfuEvent } from '../gen/video/sfu/event/events';
3
3
  import { getLogger } from '../logger';
4
4
 
@@ -26,7 +26,7 @@ const sfuEventKinds: { [key in SfuEventKinds]: undefined } = {
26
26
  };
27
27
 
28
28
  export const isSfuEvent = (
29
- eventName: SfuEventKinds | CallEventTypes,
29
+ eventName: SfuEventKinds | EventTypes,
30
30
  ): eventName is SfuEventKinds => {
31
31
  return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
32
32
  };
@@ -50,7 +50,6 @@ export type PublisherOpts = {
50
50
  export class Publisher {
51
51
  private pc: RTCPeerConnection;
52
52
  private readonly state: CallState;
53
- private readonly dispatcher: Dispatcher;
54
53
 
55
54
  private readonly transceiverRegistry: {
56
55
  [key in TrackType]: RTCRtpTransceiver | undefined;
@@ -129,7 +128,6 @@ export class Publisher {
129
128
  this.pc = this.createPeerConnection(connectionConfig);
130
129
  this.sfuClient = sfuClient;
131
130
  this.state = state;
132
- this.dispatcher = dispatcher;
133
131
  this.isDtxEnabled = isDtxEnabled;
134
132
  this.isRedEnabled = isRedEnabled;
135
133
  this.preferredVideoCodec = preferredVideoCodec;
@@ -234,8 +232,8 @@ export class Publisher {
234
232
  };
235
233
 
236
234
  if (!transceiver) {
237
- const metadata = this.state.metadata;
238
- const targetResolution = metadata?.settings.video.target_resolution;
235
+ const { settings } = this.state;
236
+ const targetResolution = settings?.video.target_resolution;
239
237
  const videoEncodings =
240
238
  trackType === TrackType.VIDEO
241
239
  ? findOptimalVideoLayers(track, targetResolution)
@@ -608,8 +606,8 @@ export class Publisher {
608
606
  return String(media.mid);
609
607
  };
610
608
 
611
- const metadata = this.state.metadata;
612
- const targetResolution = metadata?.settings.video.target_resolution;
609
+ const { settings } = this.state;
610
+ const targetResolution = settings?.video.target_resolution;
613
611
  return this.pc
614
612
  .getTransceivers()
615
613
  .filter((t) => t.direction === 'sendonly' && t.sender.track)