@stream-io/video-client 0.0.2-alpha.6 → 0.0.2-alpha.8

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.
@@ -115,6 +115,10 @@ export declare class Call {
115
115
  private waitForJoinResponse;
116
116
  /**
117
117
  * Loads the information about the call.
118
+ *
119
+ * @param params.ring if set to true, a `call.ring` event will be sent to the call members.
120
+ * @param params.notify if set to true, a `call.notification` event will be sent to the call members.
121
+ * @param params.members_limit the members limit.
118
122
  */
119
123
  get: (params?: {
120
124
  ring?: boolean;
@@ -127,20 +131,38 @@ export declare class Call {
127
131
  * @param data the data to create the call with.
128
132
  */
129
133
  getOrCreate: (data?: GetOrCreateCallRequest) => Promise<GetOrCreateCallResponse>;
134
+ /**
135
+ * A shortcut for {@link Call.get} with `ring` parameter set to `true`.
136
+ * Will send a `call.ring` event to the call members.
137
+ */
130
138
  ring: () => Promise<GetCallResponse>;
139
+ /**
140
+ * A shortcut for {@link Call.get} with `notify` parameter set to `true`.
141
+ * Will send a `call.notification` event to the call members.
142
+ */
131
143
  notify: () => Promise<GetCallResponse>;
132
144
  /**
133
- * Will start to watch for call related WebSocket events and initiate a call session with the server.
145
+ * Marks the incoming call as accepted.
134
146
  *
135
- * @returns a promise which resolves once the call join-flow has finished.
147
+ * This method should be used only for "ringing" call flows.
148
+ * {@link Call.join} invokes this method automatically for you when joining a call.
149
+ * Unless you are implementing a custom "ringing" flow, you should not use this method.
136
150
  */
137
- join: (data?: JoinCallData) => Promise<void>;
151
+ accept: () => Promise<AcceptCallResponse>;
138
152
  /**
139
- * Will update the call members.
153
+ * Marks the incoming call as rejected.
140
154
  *
141
- * @param data the request data.
155
+ * This method should be used only for "ringing" call flows.
156
+ * {@link Call.leave} invokes this method automatically for you when you leave or reject this call.
157
+ * Unless you are implementing a custom "ringing" flow, you should not use this method.
142
158
  */
143
- updateCallMembers: (data: UpdateCallMembersRequest) => Promise<UpdateCallMembersResponse>;
159
+ reject: () => Promise<RejectCallResponse>;
160
+ /**
161
+ * Will start to watch for call related WebSocket events and initiate a call session with the server.
162
+ *
163
+ * @returns a promise which resolves once the call join-flow has finished.
164
+ */
165
+ join: (data?: JoinCallData) => Promise<void>;
144
166
  /**
145
167
  * Starts publishing the given video stream to the call.
146
168
  * The stream will be stopped if the user changes an input device, or if the user leaves the call.
@@ -385,6 +407,12 @@ export declare class Call {
385
407
  * @returns
386
408
  */
387
409
  queryMembers: (request: Omit<QueryMembersRequest, 'type' | 'id'>) => Promise<QueryMembersResponse>;
410
+ /**
411
+ * Will update the call members.
412
+ *
413
+ * @param data the request data.
414
+ */
415
+ updateCallMembers: (data: UpdateCallMembersRequest) => Promise<UpdateCallMembersResponse>;
388
416
  private scheduleAutoDrop;
389
417
  /**
390
418
  * Retrieves the list of recordings for the current call or call session.
@@ -411,6 +439,4 @@ export declare class Call {
411
439
  sendEvent: (event: SendEventRequest & {
412
440
  type: StreamCallEvent['type'];
413
441
  }) => Promise<SendEventResponse>;
414
- accept: () => Promise<AcceptCallResponse>;
415
- reject: () => Promise<RejectCallResponse>;
416
442
  }
@@ -13,6 +13,7 @@ export declare class StreamVideoClient {
13
13
  readonly readOnlyStateStore: StreamVideoReadOnlyStateStore;
14
14
  private readonly writeableStateStore;
15
15
  streamClient: StreamClient;
16
+ private eventHandlersToUnregister;
16
17
  /**
17
18
  * You should create only one instance of `StreamVideoClient`.
18
19
  * @param apiKey your Stream API key
@@ -1,5 +1,17 @@
1
1
  import { Call } from '../Call';
2
2
  import { Dispatcher } from '../rtc';
3
3
  import { CallState } from '../store';
4
+ /**
5
+ * Registers the default event handlers for a call during its lifecycle.
6
+ *
7
+ * @param call the call to register event handlers for.
8
+ * @param state the call state.
9
+ * @param dispatcher the dispatcher.
10
+ */
4
11
  export declare const registerEventHandlers: (call: Call, state: CallState, dispatcher: Dispatcher) => () => void;
12
+ /**
13
+ * Registers event handlers for a call that is of ringing type.
14
+ *
15
+ * @param call the call to register event handlers for.
16
+ */
5
17
  export declare const registerRingingCallEventHandlers: (call: Call) => () => void;
@@ -707,6 +707,12 @@ export interface CallNotificationEvent {
707
707
  * @memberof CallNotificationEvent
708
708
  */
709
709
  created_at: string;
710
+ /**
711
+ * Call members
712
+ * @type {Array<MemberResponse>}
713
+ * @memberof CallNotificationEvent
714
+ */
715
+ members: Array<MemberResponse>;
710
716
  /**
711
717
  * Call session ID
712
718
  * @type {string}
@@ -1104,6 +1110,12 @@ export interface CallRingEvent {
1104
1110
  * @memberof CallRingEvent
1105
1111
  */
1106
1112
  created_at: string;
1113
+ /**
1114
+ * Call members
1115
+ * @type {Array<MemberResponse>}
1116
+ * @memberof CallRingEvent
1117
+ */
1118
+ members: Array<MemberResponse>;
1107
1119
  /**
1108
1120
  * Call session ID
1109
1121
  * @type {string}
@@ -3237,12 +3249,6 @@ export interface RingSettings {
3237
3249
  * @memberof RingSettings
3238
3250
  */
3239
3251
  auto_cancel_timeout_ms: number;
3240
- /**
3241
- *
3242
- * @type {number}
3243
- * @memberof RingSettings
3244
- */
3245
- auto_reject_timeout_ms: number;
3246
3252
  /**
3247
3253
  *
3248
3254
  * @type {number}
@@ -3262,12 +3268,6 @@ export interface RingSettingsRequest {
3262
3268
  * @memberof RingSettingsRequest
3263
3269
  */
3264
3270
  auto_cancel_timeout_ms?: number;
3265
- /**
3266
- *
3267
- * @type {number}
3268
- * @memberof RingSettingsRequest
3269
- */
3270
- auto_reject_timeout_ms?: number;
3271
3271
  /**
3272
3272
  *
3273
3273
  * @type {number}
@@ -71,6 +71,13 @@ export declare class StreamVideoWriteableStateStore {
71
71
  * @param call the call to remove
72
72
  */
73
73
  unregisterCall: (call: Call) => Call[];
74
+ /**
75
+ * Finds a {@link Call} object in the list of {@link Call} objects created/tracked by this client.
76
+ *
77
+ * @param type the type of call to find.
78
+ * @param id the id of the call to find.
79
+ */
80
+ findCall: (type: string, id: string) => Call | undefined;
74
81
  /**
75
82
  * A list of objects describing incoming calls.
76
83
  * @deprecated derive from calls$ instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "0.0.2-alpha.6",
3
+ "version": "0.0.2-alpha.8",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
package/src/Call.ts CHANGED
@@ -375,12 +375,10 @@ export class Call {
375
375
  if (this.isCreatedByMe && !hasOtherParticipants) {
376
376
  // Signals other users that I have cancelled my call to them
377
377
  // before they accepted it.
378
- // Causes the `call.ended` event to be emitted to all the call members.
379
- await this.endCall();
378
+ await this.reject();
380
379
  } else if (reject && callingState === CallingState.RINGING) {
381
380
  // Signals other users that I have rejected the incoming call.
382
- // Causes the `call.rejected` event to be emitted to all the call members.
383
- await this.sendEvent({ type: 'call.rejected' });
381
+ await this.reject();
384
382
  }
385
383
  }
386
384
 
@@ -450,6 +448,10 @@ export class Call {
450
448
 
451
449
  /**
452
450
  * Loads the information about the call.
451
+ *
452
+ * @param params.ring if set to true, a `call.ring` event will be sent to the call members.
453
+ * @param params.notify if set to true, a `call.notification` event will be sent to the call members.
454
+ * @param params.members_limit the members limit.
453
455
  */
454
456
  get = async (params?: {
455
457
  ring?: boolean;
@@ -460,6 +462,11 @@ export class Call {
460
462
  this.streamClientBasePath,
461
463
  params,
462
464
  );
465
+
466
+ if (params?.ring && !this.ringing) {
467
+ this.ringingSubject.next(true);
468
+ }
469
+
463
470
  this.state.setMetadata(response.call);
464
471
  this.state.setMembers(response.members);
465
472
 
@@ -482,6 +489,10 @@ export class Call {
482
489
  GetOrCreateCallRequest
483
490
  >(this.streamClientBasePath, data);
484
491
 
492
+ if (data?.ring && !this.ringing) {
493
+ this.ringingSubject.next(true);
494
+ }
495
+
485
496
  this.state.setMetadata(response.call);
486
497
  this.state.setMembers(response.members);
487
498
 
@@ -493,14 +504,48 @@ export class Call {
493
504
  return response;
494
505
  };
495
506
 
507
+ /**
508
+ * A shortcut for {@link Call.get} with `ring` parameter set to `true`.
509
+ * Will send a `call.ring` event to the call members.
510
+ */
496
511
  ring = async (): Promise<GetCallResponse> => {
497
512
  return await this.get({ ring: true });
498
513
  };
499
514
 
515
+ /**
516
+ * A shortcut for {@link Call.get} with `notify` parameter set to `true`.
517
+ * Will send a `call.notification` event to the call members.
518
+ */
500
519
  notify = async (): Promise<GetCallResponse> => {
501
520
  return await this.get({ notify: true });
502
521
  };
503
522
 
523
+ /**
524
+ * Marks the incoming call as accepted.
525
+ *
526
+ * This method should be used only for "ringing" call flows.
527
+ * {@link Call.join} invokes this method automatically for you when joining a call.
528
+ * Unless you are implementing a custom "ringing" flow, you should not use this method.
529
+ */
530
+ accept = async () => {
531
+ return this.streamClient.post<AcceptCallResponse>(
532
+ `${this.streamClientBasePath}/accept`,
533
+ );
534
+ };
535
+
536
+ /**
537
+ * Marks the incoming call as rejected.
538
+ *
539
+ * This method should be used only for "ringing" call flows.
540
+ * {@link Call.leave} invokes this method automatically for you when you leave or reject this call.
541
+ * Unless you are implementing a custom "ringing" flow, you should not use this method.
542
+ */
543
+ reject = async () => {
544
+ return this.streamClient.post<RejectCallResponse>(
545
+ `${this.streamClientBasePath}/reject`,
546
+ );
547
+ };
548
+
504
549
  /**
505
550
  * Will start to watch for call related WebSocket events and initiate a call session with the server.
506
551
  *
@@ -518,6 +563,15 @@ export class Call {
518
563
  const previousCallingState = this.state.callingState;
519
564
  this.state.setCallingState(CallingState.JOINING);
520
565
 
566
+ if (data?.ring && !this.ringing) {
567
+ this.ringingSubject.next(true);
568
+ }
569
+
570
+ if (this.ringing && !this.isCreatedByMe) {
571
+ // signals other users that I have accepted the incoming call.
572
+ await this.accept();
573
+ }
574
+
521
575
  let sfuServer: SFUResponse;
522
576
  let sfuToken: string;
523
577
  let connectionConfig: RTCConfiguration | undefined;
@@ -759,20 +813,6 @@ export class Call {
759
813
  }
760
814
  };
761
815
 
762
- /**
763
- * Will update the call members.
764
- *
765
- * @param data the request data.
766
- */
767
- updateCallMembers = async (
768
- data: UpdateCallMembersRequest,
769
- ): Promise<UpdateCallMembersResponse> => {
770
- return this.streamClient.post<
771
- UpdateCallMembersResponse,
772
- UpdateCallMembersRequest
773
- >(`${this.streamClientBasePath}/members`, data);
774
- };
775
-
776
816
  /**
777
817
  * Starts publishing the given video stream to the call.
778
818
  * The stream will be stopped if the user changes an input device, or if the user leaves the call.
@@ -1410,7 +1450,22 @@ export class Call {
1410
1450
  );
1411
1451
  };
1412
1452
 
1453
+ /**
1454
+ * Will update the call members.
1455
+ *
1456
+ * @param data the request data.
1457
+ */
1458
+ updateCallMembers = async (
1459
+ data: UpdateCallMembersRequest,
1460
+ ): Promise<UpdateCallMembersResponse> => {
1461
+ return this.streamClient.post<
1462
+ UpdateCallMembersResponse,
1463
+ UpdateCallMembersRequest
1464
+ >(`${this.streamClientBasePath}/members`, data);
1465
+ };
1466
+
1413
1467
  private scheduleAutoDrop = () => {
1468
+ if (this.dropTimeout) clearTimeout(this.dropTimeout);
1414
1469
  const subscription = this.state.metadata$
1415
1470
  .pipe(
1416
1471
  pairwise(),
@@ -1426,8 +1481,8 @@ export class Call {
1426
1481
  currentMeta.settings.ring.auto_cancel_timeout_ms,
1427
1482
  ]
1428
1483
  : [
1429
- prevMeta?.settings.ring.auto_reject_timeout_ms,
1430
- currentMeta.settings.ring.auto_reject_timeout_ms,
1484
+ prevMeta?.settings.ring.incoming_call_timeout_ms,
1485
+ currentMeta.settings.ring.incoming_call_timeout_ms,
1431
1486
  ];
1432
1487
  if (typeof timeoutMs === 'undefined' || timeoutMs === prevTimeoutMs)
1433
1488
  return;
@@ -1497,16 +1552,4 @@ export class Call {
1497
1552
  event,
1498
1553
  );
1499
1554
  };
1500
-
1501
- accept = async () => {
1502
- return this.streamClient.post<AcceptCallResponse>(
1503
- `${this.streamClientBasePath}/accept`,
1504
- );
1505
- };
1506
-
1507
- reject = async () => {
1508
- return this.streamClient.post<RejectCallResponse>(
1509
- `${this.streamClientBasePath}/reject`,
1510
- );
1511
- };
1512
1555
  }
@@ -24,7 +24,6 @@ import type {
24
24
  EventHandler,
25
25
  EventTypes,
26
26
  StreamClientOptions,
27
- StreamVideoEvent,
28
27
  TokenOrProvider,
29
28
  User,
30
29
  } from './coordinator/connection/types';
@@ -40,6 +39,8 @@ export class StreamVideoClient {
40
39
  private readonly writeableStateStore: StreamVideoWriteableStateStore;
41
40
  streamClient: StreamClient;
42
41
 
42
+ private eventHandlersToUnregister: Array<() => void> = [];
43
+
43
44
  /**
44
45
  * You should create only one instance of `StreamVideoClient`.
45
46
  * @param apiKey your Stream API key
@@ -73,52 +74,83 @@ export class StreamVideoClient {
73
74
  user,
74
75
  tokenOrProvider,
75
76
  );
77
+ this.writeableStateStore.setConnectedUser(user);
76
78
 
77
- // FIXME OL: unregister the event listeners.
78
- this.on('connection.changed', (e) => {
79
- const event = e as ConnectionChangedEvent;
80
- if (event.online) {
81
- const callsToReWatch = this.writeableStateStore.calls
82
- .filter((call) => call.watching)
83
- .map((call) => call.cid);
79
+ this.eventHandlersToUnregister.push(
80
+ this.on('connection.changed', (e) => {
81
+ const event = e as ConnectionChangedEvent;
82
+ if (event.online) {
83
+ const callsToReWatch = this.writeableStateStore.calls
84
+ .filter((call) => call.watching)
85
+ .map((call) => call.cid);
84
86
 
85
- if (callsToReWatch.length > 0) {
86
- this.queryCalls({
87
- watch: true,
88
- filter_conditions: {
89
- cid: { $in: callsToReWatch },
90
- },
91
- sort: [{ field: 'cid', direction: 1 }],
92
- }).catch((err) => {
93
- console.warn('Failed to re-watch calls', err);
94
- });
87
+ if (callsToReWatch.length > 0) {
88
+ this.queryCalls({
89
+ watch: true,
90
+ filter_conditions: {
91
+ cid: { $in: callsToReWatch },
92
+ },
93
+ sort: [{ field: 'cid', direction: 1 }],
94
+ }).catch((err) => {
95
+ console.warn('Failed to re-watch calls', err);
96
+ });
97
+ }
95
98
  }
96
- }
97
- });
99
+ }),
100
+ );
98
101
 
99
- // FIXME: OL: unregister the event listeners.
100
- this.on('call.created', (event: StreamVideoEvent) => {
101
- if (event.type !== 'call.created') return;
102
- const { call, members } = event;
103
- if (user.id === call.created_by.id) {
104
- console.warn('Received `call.created` sent by the current user');
105
- return;
106
- }
102
+ this.eventHandlersToUnregister.push(
103
+ this.on('call.created', (event) => {
104
+ if (event.type !== 'call.created') return;
105
+ const { call, members } = event;
106
+ if (user.id === call.created_by.id) {
107
+ console.warn('Received `call.created` sent by the current user');
108
+ return;
109
+ }
107
110
 
108
- this.writeableStateStore.registerCall(
109
- new Call({
110
- streamClient: this.streamClient,
111
- type: call.type,
112
- id: call.id,
113
- metadata: call,
114
- members,
115
- ringing: false, //TODO: remove ringing from here
116
- clientStore: this.writeableStateStore,
117
- }),
118
- );
119
- });
111
+ this.writeableStateStore.registerCall(
112
+ new Call({
113
+ streamClient: this.streamClient,
114
+ type: call.type,
115
+ id: call.id,
116
+ metadata: call,
117
+ members,
118
+ clientStore: this.writeableStateStore,
119
+ }),
120
+ );
121
+ }),
122
+ );
120
123
 
121
- this.writeableStateStore.setConnectedUser(user);
124
+ this.eventHandlersToUnregister.push(
125
+ this.on('call.ring', async (event) => {
126
+ if (event.type !== 'call.ring') return;
127
+ const { call, members } = event;
128
+ if (user.id === call.created_by.id) {
129
+ console.warn('Received `call.ring` sent by the current user');
130
+ return;
131
+ }
132
+
133
+ // The call might already be tracked by the client,
134
+ // if `call.created` was received before `call.ring`.
135
+ // In that case, we just reuse the already tracked call.
136
+ let theCall = this.writeableStateStore.findCall(call.type, call.id);
137
+ if (!theCall) {
138
+ // otherwise, we create a new call
139
+ theCall = new Call({
140
+ streamClient: this.streamClient,
141
+ type: call.type,
142
+ id: call.id,
143
+ members,
144
+ clientStore: this.writeableStateStore,
145
+ ringing: true,
146
+ });
147
+ }
148
+
149
+ // we fetch the latest metadata for the call from the server
150
+ await theCall.get({ ring: true });
151
+ this.writeableStateStore.registerCall(theCall);
152
+ }),
153
+ );
122
154
 
123
155
  return connectUserResponse;
124
156
  };
@@ -147,6 +179,8 @@ export class StreamVideoClient {
147
179
  */
148
180
  disconnectUser = async (timeout?: number) => {
149
181
  await this.streamClient.disconnectUser(timeout);
182
+ this.eventHandlersToUnregister.forEach((unregister) => unregister());
183
+ this.eventHandlersToUnregister = [];
150
184
  this.writeableStateStore.setConnectedUser(undefined);
151
185
  };
152
186