@stream-io/video-client 1.36.1 → 1.37.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.
- package/CHANGELOG.md +12 -0
- package/dist/index.browser.es.js +29 -22
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +29 -22
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +29 -22
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +6 -4
- package/dist/src/gen/coordinator/index.d.ts +726 -16
- package/dist/src/store/CallState.d.ts +9 -0
- package/package.json +1 -1
- package/src/Call.ts +12 -5
- package/src/StreamVideoClient.ts +2 -9
- package/src/__tests__/StreamVideoClient.api.test.ts +5 -19
- package/src/__tests__/StreamVideoClient.ringing.test.ts +55 -6
- package/src/gen/coordinator/index.ts +708 -16
- package/src/helpers/DynascaleManager.ts +9 -3
- package/src/store/CallState.ts +12 -0
|
@@ -295,6 +295,15 @@ export declare class CallState {
|
|
|
295
295
|
* The stable list of participants in the current call, unsorted.
|
|
296
296
|
*/
|
|
297
297
|
get rawParticipants(): StreamVideoParticipant[];
|
|
298
|
+
/**
|
|
299
|
+
* Returns the current participants array directly from the BehaviorSubject.
|
|
300
|
+
* This bypasses the observable pipeline and is guaranteed to be synchronous.
|
|
301
|
+
* Use this when you need the absolute latest value without any potential
|
|
302
|
+
* timing issues from shareReplay/refCount.
|
|
303
|
+
*
|
|
304
|
+
* @internal
|
|
305
|
+
*/
|
|
306
|
+
getParticipantsSnapshot: () => StreamVideoParticipant[];
|
|
298
307
|
/**
|
|
299
308
|
* Sets the list of participants in the current call.
|
|
300
309
|
*
|
package/package.json
CHANGED
package/src/Call.ts
CHANGED
|
@@ -64,6 +64,8 @@ import type {
|
|
|
64
64
|
RejectCallResponse,
|
|
65
65
|
RequestPermissionRequest,
|
|
66
66
|
RequestPermissionResponse,
|
|
67
|
+
RingCallRequest,
|
|
68
|
+
RingCallResponse,
|
|
67
69
|
SendCallEventRequest,
|
|
68
70
|
SendCallEventResponse,
|
|
69
71
|
SendReactionRequest,
|
|
@@ -731,12 +733,14 @@ export class Call {
|
|
|
731
733
|
* @param params.ring if set to true, a `call.ring` event will be sent to the call members.
|
|
732
734
|
* @param params.notify if set to true, a `call.notification` event will be sent to the call members.
|
|
733
735
|
* @param params.members_limit the total number of members to return as part of the response.
|
|
736
|
+
* @param params.video if set to true, in a ringing scenario, mobile SDKs will show "incoming video call", audio only otherwise.
|
|
734
737
|
*/
|
|
735
738
|
get = async (params?: {
|
|
736
739
|
ring?: boolean;
|
|
737
740
|
notify?: boolean;
|
|
738
741
|
members_limit?: number;
|
|
739
|
-
|
|
742
|
+
video?: boolean;
|
|
743
|
+
}): Promise<GetCallResponse> => {
|
|
740
744
|
await this.setup();
|
|
741
745
|
const response = await this.streamClient.get<GetCallResponse>(
|
|
742
746
|
this.streamClientBasePath,
|
|
@@ -813,11 +817,14 @@ export class Call {
|
|
|
813
817
|
};
|
|
814
818
|
|
|
815
819
|
/**
|
|
816
|
-
*
|
|
817
|
-
*
|
|
820
|
+
* Sends a ring notification to the provided users who are not already in the call.
|
|
821
|
+
* All users should be members of the call.
|
|
818
822
|
*/
|
|
819
|
-
ring = async (): Promise<
|
|
820
|
-
return
|
|
823
|
+
ring = async (data: RingCallRequest = {}): Promise<RingCallResponse> => {
|
|
824
|
+
return this.streamClient.post<RingCallResponse, RingCallRequest>(
|
|
825
|
+
`${this.streamClientBasePath}/ring`,
|
|
826
|
+
data,
|
|
827
|
+
);
|
|
821
828
|
};
|
|
822
829
|
|
|
823
830
|
/**
|
package/src/StreamVideoClient.ts
CHANGED
|
@@ -199,11 +199,6 @@ export class StreamVideoClient {
|
|
|
199
199
|
* @param e the event.
|
|
200
200
|
*/
|
|
201
201
|
private initCallFromEvent = async (e: CallCreatedEvent | CallRingEvent) => {
|
|
202
|
-
if (this.state.connectedUser?.id === e.call.created_by.id) {
|
|
203
|
-
this.logger.debug(`Ignoring ${e.type} event sent by the current user`);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
202
|
try {
|
|
208
203
|
const concurrencyTag = getCallInitConcurrencyTag(e.call_cid);
|
|
209
204
|
await withoutConcurrency(concurrencyTag, async () => {
|
|
@@ -597,16 +592,14 @@ export class StreamVideoClient {
|
|
|
597
592
|
|
|
598
593
|
private shouldRejectCall = (currentCallId: string) => {
|
|
599
594
|
if (!this.rejectCallWhenBusy) return false;
|
|
600
|
-
|
|
601
|
-
const hasOngoingRingingCall = this.state.calls.some(
|
|
595
|
+
return this.state.calls.some(
|
|
602
596
|
(c) =>
|
|
603
597
|
c.cid !== currentCallId &&
|
|
604
598
|
c.ringing &&
|
|
599
|
+
!c.isCreatedByMe &&
|
|
605
600
|
c.state.callingState !== CallingState.IDLE &&
|
|
606
601
|
c.state.callingState !== CallingState.LEFT &&
|
|
607
602
|
c.state.callingState !== CallingState.RECONNECTING_FAILED,
|
|
608
603
|
);
|
|
609
|
-
|
|
610
|
-
return hasOngoingRingingCall;
|
|
611
604
|
};
|
|
612
605
|
}
|
|
@@ -10,34 +10,20 @@ const secret = process.env.STREAM_SECRET!;
|
|
|
10
10
|
|
|
11
11
|
const serverClient = new StreamClient(apiKey, secret);
|
|
12
12
|
|
|
13
|
-
const tokenProvider = (userId: string) => {
|
|
14
|
-
return async () => {
|
|
15
|
-
return new Promise<string>((resolve) => {
|
|
16
|
-
setTimeout(() => {
|
|
17
|
-
const token = serverClient.createToken(
|
|
18
|
-
userId,
|
|
19
|
-
undefined,
|
|
20
|
-
Math.round(Date.now() / 1000 - 10),
|
|
21
|
-
);
|
|
22
|
-
resolve(token);
|
|
23
|
-
}, 100);
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
|
|
28
13
|
describe('StreamVideoClient - coordinator API', () => {
|
|
29
14
|
let client: StreamVideoClient;
|
|
30
|
-
const user = {
|
|
31
|
-
id: 'sara',
|
|
32
|
-
};
|
|
33
15
|
|
|
34
16
|
beforeAll(() => {
|
|
17
|
+
const user = { id: 'sara' };
|
|
35
18
|
client = new StreamVideoClient(apiKey, {
|
|
36
19
|
// tests run in node, so we have to fake being in browser env
|
|
37
20
|
browser: true,
|
|
38
21
|
timeout: 15000,
|
|
39
22
|
});
|
|
40
|
-
client.connectUser(
|
|
23
|
+
client.connectUser(
|
|
24
|
+
user,
|
|
25
|
+
serverClient.generateUserToken({ user_id: user.id }),
|
|
26
|
+
);
|
|
41
27
|
});
|
|
42
28
|
|
|
43
29
|
it('query calls', { retry: 3, timeout: 20000 }, async () => {
|
|
@@ -100,26 +100,75 @@ describe('StreamVideoClient Ringing', () => {
|
|
|
100
100
|
],
|
|
101
101
|
},
|
|
102
102
|
});
|
|
103
|
+
expect(call.ringing).toBe(true);
|
|
103
104
|
|
|
104
|
-
const [
|
|
105
|
-
|
|
105
|
+
const ringEventsPromise = Promise.all([sachaRing, marceloRing]);
|
|
106
|
+
await expect(ringEventsPromise).resolves.toHaveLength(2);
|
|
107
|
+
await expect(oliverRing).rejects.toThrow(); // caller doesn't get ring event
|
|
108
|
+
const [sachaRingEvent, marceloRingEvent] = await ringEventsPromise;
|
|
106
109
|
|
|
107
|
-
expect(oliverRingEvent.call.cid).toBe(call.cid);
|
|
108
110
|
expect(sachaRingEvent.call.cid).toBe(call.cid);
|
|
109
111
|
expect(marceloRingEvent.call.cid).toBe(call.cid);
|
|
110
112
|
|
|
111
|
-
const oliverCall = await expectCall(oliverClient, call.cid);
|
|
112
113
|
const sachaCall = await expectCall(sachaClient, call.cid);
|
|
113
114
|
const marceloCall = await expectCall(marceloClient, call.cid);
|
|
114
|
-
expect(oliverCall).toBeDefined();
|
|
115
115
|
expect(sachaCall).toBeDefined();
|
|
116
116
|
expect(marceloCall).toBeDefined();
|
|
117
|
-
expect(oliverCall.ringing).toBe(true);
|
|
118
117
|
expect(sachaCall.ringing).toBe(true);
|
|
119
118
|
expect(marceloCall.ringing).toBe(true);
|
|
120
119
|
});
|
|
121
120
|
});
|
|
122
121
|
|
|
122
|
+
describe('ringing individual members', () => {
|
|
123
|
+
it('should ring individual members', async () => {
|
|
124
|
+
const oliverCall = oliverClient.call('default', crypto.randomUUID());
|
|
125
|
+
await oliverCall.create({
|
|
126
|
+
ring: false, // don't ring all members by default
|
|
127
|
+
data: {
|
|
128
|
+
members: [
|
|
129
|
+
{ user_id: 'oliver' },
|
|
130
|
+
{ user_id: 'sacha' },
|
|
131
|
+
{ user_id: 'marcelo' },
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// no one should get a ring event yet
|
|
137
|
+
const oliverRing = expectEvent(oliverClient, 'call.ring', 500);
|
|
138
|
+
const sachaRing = expectEvent(sachaClient, 'call.ring', 500);
|
|
139
|
+
const marceloRing = expectEvent(marceloClient, 'call.ring', 500);
|
|
140
|
+
await expect(
|
|
141
|
+
Promise.all([oliverRing, sachaRing, marceloRing]),
|
|
142
|
+
).rejects.toThrow();
|
|
143
|
+
|
|
144
|
+
// oliver is calling sacha. only sacha should get a ring event
|
|
145
|
+
const sachaIndividualRing = expectEvent(sachaClient, 'call.ring');
|
|
146
|
+
const marceloIndividualRing = expectEvent(marceloClient, 'call.ring');
|
|
147
|
+
await oliverCall.ring({ members_ids: ['sacha'] });
|
|
148
|
+
await expect(sachaIndividualRing).resolves.toHaveProperty(
|
|
149
|
+
'call.cid',
|
|
150
|
+
oliverCall.cid,
|
|
151
|
+
);
|
|
152
|
+
await expect(marceloIndividualRing).rejects.toThrow();
|
|
153
|
+
|
|
154
|
+
const sachaCall = await expectCall(sachaClient, oliverCall.cid);
|
|
155
|
+
expect(sachaCall).toBeDefined();
|
|
156
|
+
|
|
157
|
+
// sacha is calling marcelo. only marcelo should get a ring event
|
|
158
|
+
const oliverIndividualRing = expectEvent(oliverClient, 'call.ring');
|
|
159
|
+
const marceloIndividualRing2 = expectEvent(marceloClient, 'call.ring');
|
|
160
|
+
await sachaCall.ring({ members_ids: ['marcelo'] });
|
|
161
|
+
await expect(marceloIndividualRing2).resolves.toHaveProperty(
|
|
162
|
+
'call.cid',
|
|
163
|
+
sachaCall.cid,
|
|
164
|
+
);
|
|
165
|
+
await expect(oliverIndividualRing).rejects.toThrow();
|
|
166
|
+
|
|
167
|
+
const marceloCall = await expectCall(marceloClient, sachaCall.cid);
|
|
168
|
+
expect(marceloCall).toBeDefined();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
123
172
|
describe('ringing concurrently', async () => {
|
|
124
173
|
it('dispatches `call.ring` before `call.created`', async () => {
|
|
125
174
|
oliverClient.streamClient.dispatchEvent(
|