Skip to content

Commit bb47935

Browse files
authored
chore: Update media-signaling to 0.2.0 (#7153)
1 parent 2a098f7 commit bb47935

12 files changed

+155
-54
lines changed

app/lib/services/restApi.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { ServerMediaSignal } from '@rocket.chat/media-signaling';
2+
3+
import { mediaCallsStateSignals } from './restApi';
4+
5+
const mockSdkGet = jest.fn();
6+
jest.mock('./sdk', () => ({
7+
__esModule: true,
8+
default: {
9+
get: (...args: unknown[]) => mockSdkGet(...args)
10+
}
11+
}));
12+
13+
describe('mediaCallsStateSignals', () => {
14+
beforeEach(() => {
15+
jest.clearAllMocks();
16+
});
17+
18+
it('calls sdk.get with media-calls.stateSignals and the contractId', async () => {
19+
mockSdkGet.mockResolvedValueOnce({ signals: [], success: true });
20+
21+
const result = await mediaCallsStateSignals('device-contract-id-123');
22+
23+
expect(mockSdkGet).toHaveBeenCalledWith('media-calls.stateSignals', { contractId: 'device-contract-id-123' });
24+
expect(result).toEqual({ signals: [], success: true });
25+
});
26+
27+
it('returns signals and success from the API response', async () => {
28+
const mockSignals = [
29+
{ type: 'new', callId: 'call-1' } as unknown as ServerMediaSignal,
30+
{ type: 'notification', notification: 'ringing' } as unknown as ServerMediaSignal
31+
];
32+
mockSdkGet.mockResolvedValueOnce({ signals: mockSignals, success: true });
33+
34+
const result = await mediaCallsStateSignals('device-id');
35+
36+
expect(result.signals).toHaveLength(2);
37+
expect(result.success).toBe(true);
38+
});
39+
40+
it('returns empty signals and success false when sdk.get throws', async () => {
41+
mockSdkGet.mockRejectedValueOnce(new Error('Network error'));
42+
43+
const result = await mediaCallsStateSignals('device-id');
44+
45+
expect(result.signals).toEqual([]);
46+
expect(result.success).toBe(false);
47+
});
48+
49+
it('returns empty signals and success false when sdk.get returns an error response', async () => {
50+
mockSdkGet.mockResolvedValueOnce({ signals: [], success: false });
51+
52+
const result = await mediaCallsStateSignals('device-id');
53+
54+
expect(result.signals).toEqual([]);
55+
expect(result.success).toBe(false);
56+
});
57+
});

app/lib/services/restApi.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getUniqueId } from 'react-native-device-info';
2+
import type { ServerMediaSignal } from '@rocket.chat/media-signaling';
23

34
import {
45
type IAvatarSuggestion,
@@ -1215,3 +1216,14 @@ export const getUsersRoles = async (): Promise<boolean | IRoleUser[]> => {
12151216

12161217
export const getSupportedVersionsCloud = (uniqueId?: string, domain?: string) =>
12171218
fetch(`https://releases.rocket.chat/v2/server/supportedVersions?uniqueId=${uniqueId}&domain=${domain}&source=mobile`);
1219+
1220+
export const mediaCallsStateSignals = async (contractId: string): Promise<{ signals: ServerMediaSignal[]; success: boolean }> => {
1221+
try {
1222+
const result = await (
1223+
sdk.get as unknown as (path: string, params?: object) => Promise<{ signals: ServerMediaSignal[]; success: boolean }>
1224+
)('media-calls.stateSignals', { contractId });
1225+
return result;
1226+
} catch {
1227+
return { signals: [], success: false };
1228+
}
1229+
};

app/lib/services/voip/MediaSessionInstance.test.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ type MockMediaSignalingSession = {
105105
processSignal: jest.Mock;
106106
setIceGatheringTimeout: jest.Mock;
107107
startCall: jest.Mock;
108-
getMainCall: jest.Mock;
108+
getCallData: jest.Mock;
109109
};
110110

111111
const createdSessions: MockMediaSignalingSession[] = [];
@@ -124,7 +124,7 @@ jest.mock('@rocket.chat/media-signaling', () => ({
124124
this.processSignal = jest.fn().mockResolvedValue(undefined);
125125
this.setIceGatheringTimeout = jest.fn();
126126
this.startCall = jest.fn().mockResolvedValue(undefined);
127-
this.getMainCall = jest.fn();
127+
this.getCallData = jest.fn();
128128
Object.defineProperty(this, 'sessionId', { value: `session-${config.userId}`, writable: false });
129129
createdSessions.push(this);
130130
})
@@ -160,13 +160,23 @@ function buildClientMediaCall(options: {
160160
role: 'caller' | 'callee';
161161
hidden?: boolean;
162162
reject?: jest.Mock;
163+
contact?: { username?: string; sipExtension?: string };
163164
}): IClientMediaCall {
164165
const reject = options.reject ?? jest.fn();
165166
const emitter = { on: jest.fn(), off: jest.fn(), emit: jest.fn() };
166167
return {
167168
callId: options.callId,
168-
role: options.role,
169169
hidden: options.hidden ?? false,
170+
localParticipant: { local: true, role: options.role, muted: false, held: false, contact: {} },
171+
remoteParticipants: [
172+
{
173+
local: false,
174+
role: options.role === 'caller' ? 'callee' : 'caller',
175+
muted: false,
176+
held: false,
177+
contact: options.contact ?? {}
178+
}
179+
],
170180
reject,
171181
emitter: emitter as unknown as IClientMediaCall['emitter']
172182
} as unknown as IClientMediaCall;
@@ -505,9 +515,9 @@ describe('MediaSessionInstance', () => {
505515
newCallHandler({
506516
call: {
507517
hidden: false,
508-
role: 'caller',
518+
localParticipant: { role: 'caller' },
519+
remoteParticipants: [{ contact: { username: 'alice', sipExtension: '' } }],
509520
callId: 'c1',
510-
contact: { username: 'alice', sipExtension: '' },
511521
emitter: { on: jest.fn(), off: jest.fn() }
512522
} as unknown as IClientMediaCall
513523
});
@@ -536,9 +546,9 @@ describe('MediaSessionInstance', () => {
536546
newCallHandler({
537547
call: {
538548
hidden: false,
539-
role: 'caller',
549+
localParticipant: { role: 'caller' },
550+
remoteParticipants: [{ contact: { username: 'alice', sipExtension: '' } }],
540551
callId: 'c1',
541-
contact: { username: 'alice', sipExtension: '' },
542552
emitter: { on: jest.fn(), off: jest.fn() }
543553
} as unknown as IClientMediaCall
544554
});
@@ -557,9 +567,9 @@ describe('MediaSessionInstance', () => {
557567
newCallHandler({
558568
call: {
559569
hidden: false,
560-
role: 'caller',
570+
localParticipant: { role: 'caller' },
571+
remoteParticipants: [{ contact: { username: 'alice', sipExtension: '100' } }],
561572
callId: 'c1',
562-
contact: { username: 'alice', sipExtension: '100' },
563573
emitter: { on: jest.fn(), off: jest.fn() }
564574
} as unknown as IClientMediaCall
565575
});
@@ -575,9 +585,9 @@ describe('MediaSessionInstance', () => {
575585
const mainCall = {
576586
callId: 'call-ans',
577587
accept: jest.fn().mockResolvedValue(undefined),
578-
contact: { username: 'bob', sipExtension: '' }
588+
remoteParticipants: [{ contact: { username: 'bob', sipExtension: '' } }]
579589
};
580-
session.getMainCall.mockReturnValue(mainCall);
590+
session.getCallData.mockReturnValue(mainCall);
581591

582592
await mediaSessionInstance.answerCall('call-ans');
583593

@@ -591,9 +601,9 @@ describe('MediaSessionInstance', () => {
591601
const mainCall = {
592602
callId: 'call-sip',
593603
accept: jest.fn().mockResolvedValue(undefined),
594-
contact: { username: 'bob', sipExtension: 'ext' }
604+
remoteParticipants: [{ contact: { username: 'bob', sipExtension: 'ext' } }]
595605
};
596-
session.getMainCall.mockReturnValue(mainCall);
606+
session.getCallData.mockReturnValue(mainCall);
597607

598608
await mediaSessionInstance.answerCall('call-sip');
599609

app/lib/services/voip/MediaSessionInstance.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
MediaCallWebRTCProcessor,
3+
type CallContact,
34
type ClientMediaSignal,
45
type IClientMediaCall,
56
type CallActorType,
@@ -91,11 +92,11 @@ class MediaSessionInstance {
9192
console.log('🤙 [VoIP] New call data:', call);
9293
});
9394

94-
if (call.role === 'caller') {
95+
if (call.localParticipant.role === 'caller') {
9596
useCallStore.getState().setCall(call);
9697
Navigation.navigate('CallView');
9798
if (useCallStore.getState().roomId == null) {
98-
this.resolveRoomIdFromContact(call.contact).catch(error => {
99+
this.resolveRoomIdFromContact(call.remoteParticipants[0]?.contact).catch(error => {
99100
console.error('[VoIP] Error resolving room id from contact (newCall):', error);
100101
});
101102
}
@@ -116,7 +117,7 @@ class MediaSessionInstance {
116117
}
117118

118119
console.log('[VoIP] Answering call:', callId);
119-
const mainCall = this.instance?.getMainCall();
120+
const mainCall = this.instance?.getCallData(callId);
120121
console.log('[VoIP] Main call:', mainCall);
121122

122123
if (mainCall && mainCall.callId === callId) {
@@ -126,7 +127,7 @@ class MediaSessionInstance {
126127
RNCallKeep.setCurrentCallActive(callId);
127128
useCallStore.getState().setCall(mainCall);
128129
Navigation.navigate('CallView');
129-
this.resolveRoomIdFromContact(mainCall.contact).catch(error => {
130+
this.resolveRoomIdFromContact(mainCall.remoteParticipants[0]?.contact).catch(error => {
130131
console.error('[VoIP] Error resolving room id from contact (answerCall):', error);
131132
});
132133
} else {
@@ -154,7 +155,7 @@ class MediaSessionInstance {
154155
};
155156

156157
public endCall = (callId: string) => {
157-
const mainCall = this.instance?.getMainCall();
158+
const mainCall = this.instance?.getCallData(callId);
158159

159160
if (mainCall && mainCall.callId === callId) {
160161
if (mainCall.state === 'ringing') {
@@ -170,8 +171,8 @@ class MediaSessionInstance {
170171
useCallStore.getState().reset();
171172
};
172173

173-
private async resolveRoomIdFromContact(contact: IClientMediaCall['contact']): Promise<void> {
174-
if (contact.sipExtension) {
174+
private async resolveRoomIdFromContact(contact: CallContact | undefined): Promise<void> {
175+
if (!contact || contact.sipExtension) {
175176
return;
176177
}
177178
const { username } = contact;

app/lib/services/voip/MediaSessionStore.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ class MediaSessionStore extends Emitter<{ change: void }> {
6767
randomStringFactory,
6868
logger: new MediaCallLogger(),
6969
features: ['audio'],
70-
mobileDeviceId
70+
mobileDeviceId,
71+
autoSync: true
7172
});
7273

7374
this.change();

app/lib/services/voip/mockCall.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,21 @@ export function createMockCall(overrides: MockCallOverrides = {}): IClientMediaC
3333
const contact = { ...DEFAULT_CONTACT, ...overrides.contact };
3434
const callState: CallState = overrides.callState ?? 'active';
3535

36-
const mock = {
37-
callId: 'mock-call-id',
38-
state: callState,
36+
const localParticipant = {
37+
local: true,
38+
role: 'caller',
3939
muted: overrides.isMuted ?? false,
4040
held: overrides.isOnHold ?? false,
41-
remoteMute: false,
42-
remoteHeld: false,
43-
contact,
41+
contact: {},
4442
setMuted: () => {},
45-
setHeld: () => {},
43+
setHeld: () => {}
44+
};
45+
const remoteParticipants = [{ local: false, role: 'callee', muted: false, held: false, contact }];
46+
const mock = {
47+
callId: 'mock-call-id',
48+
state: callState,
49+
localParticipant,
50+
remoteParticipants,
4651
hangup: () => {},
4752
reject: () => {},
4853
sendDTMF: () => {},

app/lib/services/voip/useCallStore.test.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,31 @@ function createMockCall(callId: string) {
3535
const emit = (ev: string, ...args: unknown[]) => {
3636
listeners[ev]?.forEach(fn => fn(...args));
3737
};
38+
const localParticipant = {
39+
local: true,
40+
role: 'callee',
41+
muted: false,
42+
held: false,
43+
contact: {},
44+
setMuted: jest.fn(),
45+
setHeld: jest.fn()
46+
};
47+
const remoteParticipants = [
48+
{
49+
local: false,
50+
role: 'caller',
51+
muted: false,
52+
held: false,
53+
contact: { id: 'u', displayName: 'U', username: 'u', sipExtension: '' }
54+
}
55+
];
3856
const call = {
3957
callId,
4058
state: 'active',
41-
muted: false,
42-
held: false,
43-
remoteMute: false,
44-
remoteHeld: false,
4559
hidden: false,
46-
role: 'callee',
47-
contact: { id: 'u', displayName: 'U', username: 'u', sipExtension: '' },
60+
localParticipant,
61+
remoteParticipants,
4862
emitter,
49-
setMuted: jest.fn(),
50-
setHeld: jest.fn(),
5163
sendDTMF: jest.fn(),
5264
hangup: jest.fn(),
5365
accept: jest.fn(),

app/lib/services/voip/useCallStore.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,21 @@ export const useCallStore = create<CallStore>((set, get) => ({
140140
cleanupCallListeners();
141141
get().resetNativeCallId();
142142
// Update state with call info
143+
const remote = call.remoteParticipants[0];
144+
const remoteContact = remote?.contact;
143145
set({
144146
call,
145147
callId: call.callId,
146148
callState: call.state,
147-
isMuted: call.muted,
148-
isOnHold: call.held,
149-
remoteMute: call.remoteMute,
150-
remoteHeld: call.remoteHeld,
149+
isMuted: call.localParticipant.muted,
150+
isOnHold: call.localParticipant.held,
151+
remoteMute: remote?.muted ?? false,
152+
remoteHeld: remote?.held ?? false,
151153
contact: {
152-
id: call.contact.id,
153-
displayName: call.contact.displayName,
154-
username: call.contact.username,
155-
sipExtension: call.contact.sipExtension
154+
id: remoteContact?.id,
155+
displayName: remoteContact?.displayName,
156+
username: remoteContact?.username,
157+
sipExtension: remoteContact?.sipExtension
156158
},
157159
callStartTime: call.state === 'active' ? Date.now() : null
158160
});
@@ -187,11 +189,12 @@ export const useCallStore = create<CallStore>((set, get) => ({
187189
const currentCall = get().call;
188190
if (!currentCall) return;
189191

192+
const currentRemote = currentCall.remoteParticipants[0];
190193
set({
191-
isMuted: currentCall.muted,
192-
isOnHold: currentCall.held,
193-
remoteMute: currentCall.remoteMute,
194-
remoteHeld: currentCall.remoteHeld,
194+
isMuted: currentCall.localParticipant.muted,
195+
isOnHold: currentCall.localParticipant.held,
196+
remoteMute: currentRemote?.muted ?? false,
197+
remoteHeld: currentRemote?.held ?? false,
195198
controlsVisible: true
196199
});
197200
};
@@ -225,15 +228,15 @@ export const useCallStore = create<CallStore>((set, get) => ({
225228
const { call, isMuted } = get();
226229
if (!call) return;
227230

228-
call.setMuted(!isMuted);
231+
call.localParticipant.setMuted(!isMuted);
229232
set({ isMuted: !isMuted });
230233
},
231234

232235
toggleHold: () => {
233236
const { call, isOnHold } = get();
234237
if (!call) return;
235238

236-
call.setHeld(!isOnHold);
239+
call.localParticipant.setHeld(!isOnHold);
237240
set({ isOnHold: !isOnHold });
238241
},
239242

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"@react-navigation/elements": "^2.6.1",
4848
"@react-navigation/native": "^7.1.16",
4949
"@react-navigation/native-stack": "^7.3.23",
50-
"@rocket.chat/media-signaling": "file:./packages/rocket.chat-media-signaling-0.1.3.tgz",
50+
"@rocket.chat/media-signaling": "file:./packages/rocket.chat-media-signaling-0.2.0.tgz",
5151
"@rocket.chat/message-parser": "0.31.32",
5252
"@rocket.chat/mobile-crypto": "RocketChat/rocket.chat-mobile-crypto",
5353
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
-69.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)