@stream-io/video-client 0.2.3 → 0.3.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 (71) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/index.browser.es.js +982 -675
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +984 -673
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +982 -675
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/Call.d.ts +21 -9
  9. package/dist/src/StreamVideoClient.d.ts +3 -1
  10. package/dist/src/devices/CameraManager.d.ts +31 -0
  11. package/dist/src/devices/CameraManagerState.d.ts +28 -0
  12. package/dist/src/devices/InputMediaDeviceManager.d.ts +47 -0
  13. package/dist/src/devices/InputMediaDeviceManagerState.d.ts +69 -0
  14. package/dist/src/devices/MicrophoneManager.d.ts +19 -0
  15. package/dist/src/devices/MicrophoneManagerState.d.ts +4 -0
  16. package/dist/src/devices/__tests__/mocks.d.ts +13 -0
  17. package/dist/src/devices/index.d.ts +4 -0
  18. package/dist/src/events/call-permissions.d.ts +0 -5
  19. package/dist/src/events/call.d.ts +0 -6
  20. package/dist/src/events/index.d.ts +0 -6
  21. package/dist/src/rtc/Dispatcher.d.ts +2 -2
  22. package/dist/src/rtc/Publisher.d.ts +0 -1
  23. package/dist/src/store/CallState.d.ts +164 -89
  24. package/dist/src/types.d.ts +5 -7
  25. package/dist/version.d.ts +1 -1
  26. package/package.json +1 -1
  27. package/src/Call.ts +130 -44
  28. package/src/StreamVideoClient.ts +14 -17
  29. package/src/__tests__/StreamVideoClient.test.ts +3 -0
  30. package/src/devices/CameraManager.ts +73 -0
  31. package/src/devices/CameraManagerState.ts +61 -0
  32. package/src/devices/InputMediaDeviceManager.ts +121 -0
  33. package/src/devices/InputMediaDeviceManagerState.ts +111 -0
  34. package/src/devices/MicrophoneManager.ts +45 -0
  35. package/src/devices/MicrophoneManagerState.ts +9 -0
  36. package/src/devices/__tests__/CameraManager.test.ts +150 -0
  37. package/src/devices/__tests__/InputMediaDeviceManager.test.ts +159 -0
  38. package/src/devices/__tests__/MicrophoneManager.test.ts +103 -0
  39. package/src/devices/__tests__/mocks.ts +98 -0
  40. package/src/devices/index.ts +4 -0
  41. package/src/events/__tests__/call-permissions.test.ts +1 -61
  42. package/src/events/__tests__/call.test.ts +5 -50
  43. package/src/events/call-permissions.ts +0 -14
  44. package/src/events/call.ts +5 -16
  45. package/src/events/callEventHandlers.ts +2 -57
  46. package/src/events/index.ts +0 -6
  47. package/src/rtc/Dispatcher.ts +2 -2
  48. package/src/rtc/Publisher.ts +4 -6
  49. package/src/store/CallState.ts +475 -119
  50. package/src/store/__tests__/CallState.test.ts +447 -1
  51. package/src/types.ts +4 -8
  52. package/dist/src/events/__tests__/sessions.test.d.ts +0 -1
  53. package/dist/src/events/backstage.d.ts +0 -6
  54. package/dist/src/events/members.d.ts +0 -18
  55. package/dist/src/events/moderation.d.ts +0 -14
  56. package/dist/src/events/reactions.d.ts +0 -8
  57. package/dist/src/events/recording.d.ts +0 -18
  58. package/dist/src/events/sessions.d.ts +0 -26
  59. package/src/events/__tests__/backstage.test.ts +0 -15
  60. package/src/events/__tests__/members.test.ts +0 -135
  61. package/src/events/__tests__/recording.test.ts +0 -65
  62. package/src/events/__tests__/sessions.test.ts +0 -135
  63. package/src/events/backstage.ts +0 -15
  64. package/src/events/members.ts +0 -62
  65. package/src/events/moderation.ts +0 -35
  66. package/src/events/reactions.ts +0 -30
  67. package/src/events/recording.ts +0 -64
  68. package/src/events/sessions.ts +0 -102
  69. /package/dist/src/{events/__tests__/backstage.test.d.ts → devices/__tests__/CameraManager.test.d.ts} +0 -0
  70. /package/dist/src/{events/__tests__/members.test.d.ts → devices/__tests__/InputMediaDeviceManager.test.d.ts} +0 -0
  71. /package/dist/src/{events/__tests__/recording.test.d.ts → devices/__tests__/MicrophoneManager.test.d.ts} +0 -0
@@ -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)