@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.
- package/CHANGELOG.md +13 -0
- package/dist/index.browser.es.js +145 -37
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +145 -37
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +145 -37
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +34 -8
- package/dist/src/StreamVideoClient.d.ts +1 -0
- package/dist/src/events/callEventHandlers.d.ts +12 -0
- package/dist/src/gen/coordinator/index.d.ts +12 -12
- package/dist/src/store/stateStore.d.ts +7 -0
- package/package.json +1 -1
- package/src/Call.ts +75 -32
- package/src/StreamVideoClient.ts +75 -41
- package/src/events/__tests__/call.test.ts +127 -15
- package/src/events/call.ts +31 -8
- package/src/events/callEventHandlers.ts +17 -7
- package/src/gen/coordinator/index.ts +12 -12
- package/src/store/stateStore.ts +10 -0
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
CallAcceptedEvent,
|
|
15
15
|
CallEndedEvent,
|
|
16
|
+
CallResponse,
|
|
16
17
|
CallUpdatedEvent,
|
|
17
18
|
} from '../../gen/coordinator';
|
|
18
19
|
import { Call } from '../../Call';
|
|
@@ -93,28 +94,58 @@ describe('Call ringing events', () => {
|
|
|
93
94
|
});
|
|
94
95
|
|
|
95
96
|
describe(`call.rejected`, () => {
|
|
96
|
-
it(`will leave the call if all callees have rejected`, async () => {
|
|
97
|
-
const call = fakeCall();
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
it(`caller will leave the call if all callees have rejected`, async () => {
|
|
98
|
+
const call = fakeCall({ currentUserId: 'm1' });
|
|
99
|
+
call.state.setMetadata({
|
|
100
|
+
...fakeMetadata(),
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
created_by: { id: 'm1' },
|
|
103
|
+
});
|
|
104
|
+
call.state.setMembers([
|
|
105
|
+
// @ts-expect-error
|
|
106
|
+
{ user_id: 'm1' },
|
|
107
|
+
// @ts-expect-error
|
|
108
|
+
{ user_id: 'm2' },
|
|
109
|
+
// @ts-expect-error
|
|
110
|
+
{ user_id: 'm3' },
|
|
111
|
+
]);
|
|
112
|
+
call.state.setCallingState(CallingState.RINGING);
|
|
100
113
|
vi.spyOn(call, 'leave').mockImplementation(async () => {
|
|
101
114
|
console.log(`TEST: leave() called`);
|
|
102
115
|
});
|
|
103
116
|
|
|
104
117
|
const handler = watchCallRejected(call);
|
|
105
118
|
// all members reject the call
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const event: CallAcceptedEvent = { type: 'call.rejected' };
|
|
119
|
+
await handler({
|
|
120
|
+
type: 'call.rejected',
|
|
109
121
|
// @ts-ignore
|
|
110
|
-
|
|
122
|
+
user: {
|
|
123
|
+
id: 'm2',
|
|
124
|
+
},
|
|
125
|
+
call: {
|
|
126
|
+
// @ts-ignore
|
|
127
|
+
created_by: {
|
|
128
|
+
id: 'm1',
|
|
129
|
+
},
|
|
130
|
+
// @ts-ignore
|
|
131
|
+
session: {
|
|
132
|
+
rejected_by: {
|
|
133
|
+
m2: new Date().toISOString(),
|
|
134
|
+
m3: new Date().toISOString(),
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
111
138
|
});
|
|
112
|
-
|
|
113
139
|
expect(call.leave).toHaveBeenCalled();
|
|
114
140
|
});
|
|
115
141
|
|
|
116
|
-
it(`will not leave the call if only one callee rejects`, async () => {
|
|
142
|
+
it(`caller will not leave the call if only one callee rejects`, async () => {
|
|
117
143
|
const call = fakeCall();
|
|
144
|
+
call.state.setMetadata({
|
|
145
|
+
...fakeMetadata(),
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
created_by: { id: 'm0' },
|
|
148
|
+
});
|
|
118
149
|
// @ts-expect-error
|
|
119
150
|
call.state.setMembers([{ user_id: 'm1' }, { user_id: 'm2' }]);
|
|
120
151
|
vi.spyOn(call, 'leave').mockImplementation(async () => {
|
|
@@ -124,12 +155,71 @@ describe('Call ringing events', () => {
|
|
|
124
155
|
|
|
125
156
|
// only one member rejects the call
|
|
126
157
|
// @ts-ignore
|
|
127
|
-
const event: CallAcceptedEvent = {
|
|
158
|
+
const event: CallAcceptedEvent = {
|
|
159
|
+
type: 'call.rejected',
|
|
160
|
+
// @ts-ignore
|
|
161
|
+
user: {
|
|
162
|
+
id: 'm2',
|
|
163
|
+
},
|
|
164
|
+
call: {
|
|
165
|
+
// @ts-ignore
|
|
166
|
+
created_by: {
|
|
167
|
+
id: 'm0',
|
|
168
|
+
},
|
|
169
|
+
// @ts-ignore
|
|
170
|
+
session: {
|
|
171
|
+
rejected_by: {
|
|
172
|
+
m2: new Date().toISOString(),
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
};
|
|
128
177
|
// @ts-ignore
|
|
129
178
|
await handler(event);
|
|
130
179
|
|
|
131
180
|
expect(call.leave).not.toHaveBeenCalled();
|
|
132
181
|
});
|
|
182
|
+
|
|
183
|
+
it('callee will leave the call if caller rejects', async () => {
|
|
184
|
+
const call = fakeCall({ currentUserId: 'm1' });
|
|
185
|
+
call.state.setMetadata({
|
|
186
|
+
...fakeMetadata(),
|
|
187
|
+
// @ts-ignore
|
|
188
|
+
created_by: { id: 'm0' },
|
|
189
|
+
});
|
|
190
|
+
// @ts-expect-error
|
|
191
|
+
call.state.setMembers([{ user_id: 'm1' }, { user_id: 'm2' }]);
|
|
192
|
+
vi.spyOn(call, 'leave').mockImplementation(async () => {
|
|
193
|
+
console.log(`TEST: leave() called`);
|
|
194
|
+
});
|
|
195
|
+
const handler = watchCallRejected(call);
|
|
196
|
+
|
|
197
|
+
// only one member rejects the call
|
|
198
|
+
// @ts-ignore
|
|
199
|
+
const event: CallAcceptedEvent = {
|
|
200
|
+
type: 'call.rejected',
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
user: {
|
|
203
|
+
id: 'm0',
|
|
204
|
+
},
|
|
205
|
+
call: {
|
|
206
|
+
// @ts-ignore
|
|
207
|
+
created_by: {
|
|
208
|
+
id: 'm0',
|
|
209
|
+
},
|
|
210
|
+
// @ts-ignore
|
|
211
|
+
session: {
|
|
212
|
+
rejected_by: {
|
|
213
|
+
m0: new Date().toISOString(),
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
// @ts-ignore
|
|
219
|
+
await handler(event);
|
|
220
|
+
|
|
221
|
+
expect(call.leave).toHaveBeenCalled();
|
|
222
|
+
});
|
|
133
223
|
});
|
|
134
224
|
|
|
135
225
|
describe(`call.ended`, () => {
|
|
@@ -171,8 +261,7 @@ describe('Call ringing events', () => {
|
|
|
171
261
|
});
|
|
172
262
|
|
|
173
263
|
it(`will not leave the call if idle`, async () => {
|
|
174
|
-
const
|
|
175
|
-
const call = fakeCall(ringing);
|
|
264
|
+
const call = fakeCall({ ring: false });
|
|
176
265
|
vi.spyOn(call, 'leave').mockImplementation(async () => {
|
|
177
266
|
console.log(`TEST: leave() called`);
|
|
178
267
|
});
|
|
@@ -189,10 +278,10 @@ describe('Call ringing events', () => {
|
|
|
189
278
|
});
|
|
190
279
|
});
|
|
191
280
|
|
|
192
|
-
const fakeCall = (ring = true) => {
|
|
281
|
+
const fakeCall = ({ ring = true, currentUserId = 'test-user-id' } = {}) => {
|
|
193
282
|
const store = new StreamVideoWriteableStateStore();
|
|
194
283
|
store.setConnectedUser({
|
|
195
|
-
id:
|
|
284
|
+
id: currentUserId,
|
|
196
285
|
});
|
|
197
286
|
const client = new StreamClient('api-key');
|
|
198
287
|
return new Call({
|
|
@@ -203,3 +292,26 @@ const fakeCall = (ring = true) => {
|
|
|
203
292
|
ringing: ring,
|
|
204
293
|
});
|
|
205
294
|
};
|
|
295
|
+
|
|
296
|
+
const fakeMetadata = (): CallResponse => {
|
|
297
|
+
return {
|
|
298
|
+
id: '12345',
|
|
299
|
+
type: 'development',
|
|
300
|
+
cid: 'development:12345',
|
|
301
|
+
|
|
302
|
+
// @ts-ignore
|
|
303
|
+
created_by: {
|
|
304
|
+
id: 'test-user-id',
|
|
305
|
+
},
|
|
306
|
+
own_capabilities: [],
|
|
307
|
+
blocked_user_ids: [],
|
|
308
|
+
|
|
309
|
+
// @ts-ignore
|
|
310
|
+
settings: {
|
|
311
|
+
ring: {
|
|
312
|
+
auto_cancel_timeout_ms: 30000,
|
|
313
|
+
incoming_call_timeout_ms: 30000,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
};
|
package/src/events/call.ts
CHANGED
|
@@ -27,16 +27,39 @@ export const watchCallAccepted = (call: Call) => {
|
|
|
27
27
|
* Once the event is received, the call is left.
|
|
28
28
|
*/
|
|
29
29
|
export const watchCallRejected = (call: Call) => {
|
|
30
|
-
let totalRejections = 0;
|
|
31
30
|
return async function onCallRejected(event: StreamVideoEvent) {
|
|
32
31
|
if (event.type !== 'call.rejected') return;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
) {
|
|
39
|
-
|
|
32
|
+
// We want to discard the event if it's from the current user
|
|
33
|
+
if (event.user.id === call.currentUserId) return;
|
|
34
|
+
const { call: eventCall } = event;
|
|
35
|
+
const { session: callSession } = eventCall;
|
|
36
|
+
|
|
37
|
+
if (!callSession) {
|
|
38
|
+
console.log('No call session provided. Ignoring call.rejected event.');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const rejectedBy = callSession.rejected_by;
|
|
43
|
+
const { members, callingState } = call.state;
|
|
44
|
+
if (callingState !== CallingState.RINGING) {
|
|
45
|
+
console.log(
|
|
46
|
+
'Call is not in ringing mode (it is either accepted or rejected already). Ignoring call.rejected event.',
|
|
47
|
+
);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (call.isCreatedByMe) {
|
|
51
|
+
const everyoneElseRejected = members
|
|
52
|
+
.filter((m) => m.user_id !== call.currentUserId)
|
|
53
|
+
.every((m) => rejectedBy[m.user_id]);
|
|
54
|
+
if (everyoneElseRejected) {
|
|
55
|
+
console.log('everyone rejected, leaving the call');
|
|
56
|
+
await call.leave();
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
if (rejectedBy[eventCall.created_by.id]) {
|
|
60
|
+
console.log('call creator rejected, leaving call');
|
|
61
|
+
await call.leave();
|
|
62
|
+
}
|
|
40
63
|
}
|
|
41
64
|
};
|
|
42
65
|
};
|
|
@@ -45,13 +45,22 @@ type RingCallEvents = Extract<
|
|
|
45
45
|
'call.accepted' | 'call.rejected'
|
|
46
46
|
>;
|
|
47
47
|
|
|
48
|
-
// call.created is handled by the StreamVideoClient
|
|
49
|
-
// custom events should be handled by integrators
|
|
50
48
|
type AllCallEvents = Exclude<
|
|
51
49
|
CallEventTypes,
|
|
52
|
-
'call.created'
|
|
50
|
+
| 'call.created' // handled by StreamVideoClient
|
|
51
|
+
| 'call.ring' // handled by StreamVideoClient
|
|
52
|
+
| 'call.notification' // not used currently
|
|
53
|
+
| 'custom' // integrators should handle custom events
|
|
54
|
+
| RingCallEvents // handled by registerRingingCallEventHandlers
|
|
53
55
|
>;
|
|
54
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Registers the default event handlers for a call during its lifecycle.
|
|
59
|
+
*
|
|
60
|
+
* @param call the call to register event handlers for.
|
|
61
|
+
* @param state the call state.
|
|
62
|
+
* @param dispatcher the dispatcher.
|
|
63
|
+
*/
|
|
55
64
|
export const registerEventHandlers = (
|
|
56
65
|
call: Call,
|
|
57
66
|
state: CallState,
|
|
@@ -80,10 +89,6 @@ export const registerEventHandlers = (
|
|
|
80
89
|
'call.session_participant_left': watchCallSessionParticipantLeft(state),
|
|
81
90
|
'call.unblocked_user': watchUnblockedUser(state),
|
|
82
91
|
'call.updated': watchCallUpdated(state),
|
|
83
|
-
'call.notification': (event: StreamCallEvent) =>
|
|
84
|
-
console.log(`Received ${event.type} event`, event),
|
|
85
|
-
'call.ring': (event: StreamCallEvent) =>
|
|
86
|
-
console.log(`Received ${event.type} event`, event),
|
|
87
92
|
};
|
|
88
93
|
const eventHandlers = [
|
|
89
94
|
watchChangePublishQuality(dispatcher, call),
|
|
@@ -117,6 +122,11 @@ export const registerEventHandlers = (
|
|
|
117
122
|
};
|
|
118
123
|
};
|
|
119
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Registers event handlers for a call that is of ringing type.
|
|
127
|
+
*
|
|
128
|
+
* @param call the call to register event handlers for.
|
|
129
|
+
*/
|
|
120
130
|
export const registerRingingCallEventHandlers = (call: Call) => {
|
|
121
131
|
const coordinatorRingEvents: {
|
|
122
132
|
[key in RingCallEvents]: (e: StreamCallEvent) => any;
|
|
@@ -708,6 +708,12 @@ export interface CallNotificationEvent {
|
|
|
708
708
|
* @memberof CallNotificationEvent
|
|
709
709
|
*/
|
|
710
710
|
created_at: string;
|
|
711
|
+
/**
|
|
712
|
+
* Call members
|
|
713
|
+
* @type {Array<MemberResponse>}
|
|
714
|
+
* @memberof CallNotificationEvent
|
|
715
|
+
*/
|
|
716
|
+
members: Array<MemberResponse>;
|
|
711
717
|
/**
|
|
712
718
|
* Call session ID
|
|
713
719
|
* @type {string}
|
|
@@ -1101,6 +1107,12 @@ export interface CallRingEvent {
|
|
|
1101
1107
|
* @memberof CallRingEvent
|
|
1102
1108
|
*/
|
|
1103
1109
|
created_at: string;
|
|
1110
|
+
/**
|
|
1111
|
+
* Call members
|
|
1112
|
+
* @type {Array<MemberResponse>}
|
|
1113
|
+
* @memberof CallRingEvent
|
|
1114
|
+
*/
|
|
1115
|
+
members: Array<MemberResponse>;
|
|
1104
1116
|
/**
|
|
1105
1117
|
* Call session ID
|
|
1106
1118
|
* @type {string}
|
|
@@ -3213,12 +3225,6 @@ export interface RingSettings {
|
|
|
3213
3225
|
* @memberof RingSettings
|
|
3214
3226
|
*/
|
|
3215
3227
|
auto_cancel_timeout_ms: number;
|
|
3216
|
-
/**
|
|
3217
|
-
*
|
|
3218
|
-
* @type {number}
|
|
3219
|
-
* @memberof RingSettings
|
|
3220
|
-
*/
|
|
3221
|
-
auto_reject_timeout_ms: number;
|
|
3222
3228
|
/**
|
|
3223
3229
|
*
|
|
3224
3230
|
* @type {number}
|
|
@@ -3238,12 +3244,6 @@ export interface RingSettingsRequest {
|
|
|
3238
3244
|
* @memberof RingSettingsRequest
|
|
3239
3245
|
*/
|
|
3240
3246
|
auto_cancel_timeout_ms?: number;
|
|
3241
|
-
/**
|
|
3242
|
-
*
|
|
3243
|
-
* @type {number}
|
|
3244
|
-
* @memberof RingSettingsRequest
|
|
3245
|
-
*/
|
|
3246
|
-
auto_reject_timeout_ms?: number;
|
|
3247
3247
|
/**
|
|
3248
3248
|
*
|
|
3249
3249
|
* @type {number}
|
package/src/store/stateStore.ts
CHANGED
|
@@ -137,6 +137,16 @@ export class StreamVideoWriteableStateStore {
|
|
|
137
137
|
return this.setCalls((calls) => calls.filter((c) => c !== call));
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Finds a {@link Call} object in the list of {@link Call} objects created/tracked by this client.
|
|
142
|
+
*
|
|
143
|
+
* @param type the type of call to find.
|
|
144
|
+
* @param id the id of the call to find.
|
|
145
|
+
*/
|
|
146
|
+
findCall = (type: string, id: string) => {
|
|
147
|
+
return this.calls.find((c) => c.type === type && c.id === id);
|
|
148
|
+
};
|
|
149
|
+
|
|
140
150
|
/**
|
|
141
151
|
* A list of objects describing incoming calls.
|
|
142
152
|
* @deprecated derive from calls$ instead.
|