@wazo/sdk 1.0.0-alpha.0 → 1.0.0-alpha.2

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 (83) hide show
  1. package/dist/wazo-sdk.js +6 -6
  2. package/esm/__tests__/__voice__/call.test.js +44 -2
  3. package/esm/__tests__/api-client.test.js +20 -1
  4. package/esm/api/auth.js +13 -2
  5. package/esm/domain/Phone/WebRTCPhone.js +6 -7
  6. package/esm/index.js +2 -0
  7. package/esm/simple/Auth.js +6 -12
  8. package/esm/simple/Phone.js +1 -0
  9. package/esm/simple/__tests__/Auth.test.js +8 -64
  10. package/esm/state-machine/call-state-machine.js +1 -1
  11. package/esm/types/api/auth.d.ts.map +1 -1
  12. package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  13. package/esm/types/index.d.ts +2 -0
  14. package/esm/types/index.d.ts.map +1 -1
  15. package/esm/types/simple/Auth.d.ts.map +1 -1
  16. package/esm/types/simple/Phone.d.ts.map +1 -1
  17. package/esm/types/state-machine/utils.d.ts +1 -1
  18. package/esm/types/state-machine/utils.d.ts.map +1 -1
  19. package/esm/types/utils/api-requester.d.ts.map +1 -1
  20. package/esm/types/utils/sdp.d.ts +2 -0
  21. package/esm/types/utils/sdp.d.ts.map +1 -1
  22. package/esm/types/utils/webrtc.d.ts +1 -0
  23. package/esm/types/utils/webrtc.d.ts.map +1 -1
  24. package/esm/types/voice/call.d.ts +19 -11
  25. package/esm/types/voice/call.d.ts.map +1 -1
  26. package/esm/types/voice/softphone.d.ts +18 -13
  27. package/esm/types/voice/softphone.d.ts.map +1 -1
  28. package/esm/utils/__tests__/api-requester.test.js +17 -0
  29. package/esm/utils/api-requester.js +9 -1
  30. package/esm/utils/sdp.js +13 -0
  31. package/esm/utils/webrtc.js +21 -1
  32. package/esm/voice/call.js +90 -30
  33. package/esm/voice/softphone.js +94 -23
  34. package/lib/__tests__/__voice__/call.test.js +43 -1
  35. package/lib/__tests__/api-client.test.js +20 -1
  36. package/lib/api/auth.js +13 -2
  37. package/lib/domain/Phone/WebRTCPhone.js +5 -6
  38. package/lib/index.js +6 -2
  39. package/lib/simple/Auth.js +6 -12
  40. package/lib/simple/Phone.js +1 -0
  41. package/lib/simple/__tests__/Auth.test.js +11 -64
  42. package/lib/state-machine/call-state-machine.js +1 -1
  43. package/lib/types/api/auth.d.ts.map +1 -1
  44. package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  45. package/lib/types/index.d.ts +2 -0
  46. package/lib/types/index.d.ts.map +1 -1
  47. package/lib/types/simple/Auth.d.ts.map +1 -1
  48. package/lib/types/simple/Phone.d.ts.map +1 -1
  49. package/lib/types/state-machine/utils.d.ts +1 -1
  50. package/lib/types/state-machine/utils.d.ts.map +1 -1
  51. package/lib/types/utils/api-requester.d.ts.map +1 -1
  52. package/lib/types/utils/sdp.d.ts +2 -0
  53. package/lib/types/utils/sdp.d.ts.map +1 -1
  54. package/lib/types/utils/webrtc.d.ts +1 -0
  55. package/lib/types/utils/webrtc.d.ts.map +1 -1
  56. package/lib/types/voice/call.d.ts +19 -11
  57. package/lib/types/voice/call.d.ts.map +1 -1
  58. package/lib/types/voice/softphone.d.ts +18 -13
  59. package/lib/types/voice/softphone.d.ts.map +1 -1
  60. package/lib/utils/__tests__/api-requester.test.js +17 -0
  61. package/lib/utils/api-requester.js +9 -1
  62. package/lib/utils/sdp.js +16 -1
  63. package/lib/utils/webrtc.js +23 -2
  64. package/lib/voice/call.js +87 -28
  65. package/lib/voice/softphone.js +95 -23
  66. package/package.json +1 -1
  67. package/src/__tests__/__voice__/call.test.ts +47 -2
  68. package/src/__tests__/api-client.test.ts +22 -1
  69. package/src/api/auth.ts +25 -3
  70. package/src/domain/Phone/WebRTCPhone.ts +9 -9
  71. package/src/index.ts +2 -0
  72. package/src/simple/Auth.ts +6 -13
  73. package/src/simple/Phone.ts +1 -0
  74. package/src/simple/__tests__/Auth.test.ts +9 -87
  75. package/src/state-machine/call-state-machine.ts +1 -1
  76. package/src/state-machine/utils.ts +2 -2
  77. package/src/utils/__tests__/api-requester.test.ts +20 -0
  78. package/src/utils/api-requester.ts +9 -1
  79. package/src/utils/sdp.ts +16 -0
  80. package/src/utils/webrtc.ts +24 -2
  81. package/src/voice/call.ts +118 -40
  82. package/src/voice/softphone.ts +116 -30
  83. package/src/web-rtc-client.ts +1 -1
@@ -131,7 +131,6 @@ describe('With correct API results', () => {
131
131
  body: JSON.stringify({ saml_session_id: samlSessionId }),
132
132
  signal: expect.any(Object),
133
133
  headers: {
134
- 'X-Auth-Token': token,
135
134
  'Content-Type': 'application/json',
136
135
  Accept: 'application/json',
137
136
  },
@@ -140,6 +139,28 @@ describe('With correct API results', () => {
140
139
  });
141
140
  });
142
141
 
142
+ describe('samlLogIn mobile test', () => {
143
+ it('should retrieve user token', async () => {
144
+ Wazo.Auth.mobile = true;
145
+ const samlSessionId = 'a1b2C3d4';
146
+ const result = await client.auth.samlLogIn(samlSessionId);
147
+
148
+ expect(result).toBeInstanceOf(Session);
149
+ expect(result?.token).toBe(1);
150
+ expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token`, {
151
+ method: 'post',
152
+ body: JSON.stringify({ saml_session_id: samlSessionId }),
153
+ signal: expect.any(Object),
154
+ headers: {
155
+ 'Content-Type': 'application/json',
156
+ 'Wazo-Session-Type': 'mobile',
157
+ Accept: 'application/json',
158
+ },
159
+ agent: null,
160
+ });
161
+ });
162
+ });
163
+
143
164
  describe('logOut test', () => {
144
165
  it('should delete the specified token', async () => {
145
166
  const oldToken = 123;
package/src/api/auth.ts CHANGED
@@ -16,6 +16,12 @@ type LoginParams = {
16
16
  domainName?: string;
17
17
  };
18
18
 
19
+ type SamlLoginBody = {
20
+ saml_session_id: string,
21
+ access_type?: 'online' | 'offline';
22
+ client_id?: string;
23
+ };
24
+
19
25
  type SendResetPasswordArgs = {
20
26
  username?: string,
21
27
  email?: string,
@@ -110,13 +116,29 @@ export default ((client: ApiRequester, baseUrl: string): AuthD => ({
110
116
  },
111
117
 
112
118
  logOut: (token: Token): Promise<LogoutResponse> => client.delete(`${baseUrl}/token/${token}`, null, {}, ApiRequester.successResponseParser),
119
+
113
120
  samlLogIn: async (samlSessionId: string): Promise<Session | null | undefined> => {
114
- const body = {
121
+ const headers: Record<string, any> = {
122
+ Accept: 'application/json',
123
+ 'Content-Type': 'application/json',
124
+ };
125
+
126
+ const body: SamlLoginBody = {
115
127
  saml_session_id: samlSessionId,
116
128
  };
117
129
 
118
- return client.post(`${baseUrl}/token`, body).then(Session.parse);
130
+ if (client.clientId) {
131
+ body.access_type = 'offline';
132
+ body.client_id = client.clientId;
133
+ }
134
+
135
+ if (Wazo.Auth.mobile) {
136
+ headers['Wazo-Session-Type'] = 'mobile';
137
+ }
138
+
139
+ return client.post(`${baseUrl}/token`, body, headers).then(Session.parse);
119
140
  },
141
+
120
142
  initiateIdpAuthentication: async (domain: string, redirectUrl: string) => {
121
143
  const body = {
122
144
  domain,
@@ -128,7 +150,7 @@ export default ((client: ApiRequester, baseUrl: string): AuthD => ({
128
150
  'Content-Type': 'application/json',
129
151
  };
130
152
 
131
- return client.post(`${baseUrl}/saml/sso`, body, headers, (response: any) => response);
153
+ return client.post(`${baseUrl}/saml/sso`, body, headers);
132
154
  },
133
155
  refreshToken: (refreshToken: string, backend: string, expiration: number, isMobile?: boolean, tenantId?: string, domainName?: string): Promise<Session | null | undefined> => {
134
156
  const body: Record<string, any> = {
@@ -12,7 +12,7 @@ import WazoWebRTCClient from '../../web-rtc-client';
12
12
  import Emitter from '../../utils/Emitter';
13
13
  import IssueReporter from '../../service/IssueReporter';
14
14
  import { PeerConnection, WazoSession } from '../types';
15
- import { getSipSessionId } from '../../utils/sdp';
15
+ import { getCallDisplayName, getCallNumber, getSipSessionId } from '../../utils/sdp';
16
16
  import { downgradeToAudio, getStreamFromConstraints, getWebRtcStats, setLocalMediaStream, upgradeToVideo } from '../../utils/webrtc';
17
17
 
18
18
  export const ON_USER_AGENT = 'onUserAgent';
@@ -61,6 +61,8 @@ export const MESSAGE_TYPE_CHAT = 'message/TYPE_CHAT';
61
61
  export const MESSAGE_TYPE_SIGNAL = 'message/TYPE_SIGNAL';
62
62
  export const events = [ON_USER_AGENT, ON_REGISTERED, ON_UNREGISTERED, ON_PROGRESS, ON_CALL_ACCEPTED, ON_CALL_ANSWERED, ON_CALL_INCOMING, ON_CALL_OUTGOING, ON_CALL_MUTED, ON_CALL_UNMUTED, ON_CALL_RESUMED, ON_CALL_HELD, ON_CALL_UNHELD, ON_CAMERA_DISABLED, ON_CALL_FAILED, ON_CALL_ENDED, ON_CALL_REJECTED, ON_MESSAGE, ON_REINVITE, ON_TRACK, ON_AUDIO_STREAM, ON_VIDEO_STREAM, ON_REMOVE_STREAM, ON_SHARE_SCREEN_ENDED, ON_TERMINATE_SOUND, ON_PLAY_RING_SOUND, ON_PLAY_INBOUND_CALL_SIGNAL_SOUND, ON_PLAY_HANGUP_SOUND, ON_PLAY_PROGRESS_SOUND, ON_VIDEO_INPUT_CHANGE, ON_SHARE_SCREEN_STARTED, ON_CALL_ERROR, ON_CHAT, ON_SIGNAL, ON_NETWORK_STATS, ON_DISCONNECTED, ON_EARLY_MEDIA];
63
63
  const logger = IssueReporter.loggerFor('webrtc-phone');
64
+
65
+ // @deprecated: Use Wazo.Voice.Softphone instead
64
66
  export default class WebRTCPhone extends Emitter implements Phone {
65
67
  client: WazoWebRTCClient;
66
68
 
@@ -1783,20 +1785,17 @@ export default class WebRTCPhone extends Emitter implements Phone {
1783
1785
  }
1784
1786
 
1785
1787
  _createCallSession(sipSession: WazoSession, fromSession?: CallSession | null | undefined, extra: Record<string, any> = {}): CallSession {
1786
- // eslint-disable-next-line
1787
- const identity = sipSession ? sipSession.remoteIdentity || sipSession.assertedIdentity : null;
1788
- // @ts-ignore: private
1789
- const number = identity ? identity.uri._normal.user : null;
1790
- const {
1791
- state,
1792
- } = sipSession || {};
1788
+ const number = getCallNumber(sipSession);
1789
+ const displayName = getCallDisplayName(sipSession);
1790
+ const { state } = sipSession || {};
1793
1791
  const sessionId = getSipSessionId(sipSession);
1792
+
1794
1793
  fromSession = fromSession || this.callSessions[sessionId];
1795
1794
  const callSession = new CallSession({
1796
1795
  callId: fromSession && fromSession.callId,
1797
1796
  sipCallId: sessionId,
1798
1797
  sipStatus: state,
1799
- displayName: identity ? identity.displayName || number : number,
1798
+ displayName,
1800
1799
  // @ts-ignore: date / number
1801
1800
  startTime: fromSession ? fromSession.startTime : new Date(),
1802
1801
  creationTime: fromSession ? fromSession.creationTime : null,
@@ -1817,6 +1816,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1817
1816
  ...extra,
1818
1817
  });
1819
1818
  this.callSessions[sessionId] = callSession;
1819
+
1820
1820
  return callSession;
1821
1821
  }
1822
1822
 
package/src/index.ts CHANGED
@@ -4,6 +4,8 @@ export { default as Emitter } from './utils/Emitter';
4
4
  export { default as IssueReporter } from './service/IssueReporter';
5
5
  export { default as BadResponse } from './domain/BadResponse';
6
6
  export { default as ServerError } from './domain/ServerError';
7
+ export { default as InvalidStateTransition } from './domain/InvalidStateTransition';
8
+ export { default as InvalidState } from './domain/InvalidState';
7
9
  export { default as SFUNotAvailableError } from './domain/SFUNotAvailableError';
8
10
  export { default as Call } from './domain/Call';
9
11
  export { default as CallLog } from './domain/CallLog';
@@ -168,28 +168,21 @@ export class Auth {
168
168
  }
169
169
 
170
170
  async initiateIdpAuthentication(domain: string, redirectUrl: string): Promise<{ location: string, saml_session_id: string } | undefined> {
171
- let response;
172
171
  try {
173
- response = await getApiClient().auth.initiateIdpAuthentication(domain, redirectUrl);
172
+ return await getApiClient().auth.initiateIdpAuthentication(domain, redirectUrl);
174
173
  } catch (e: any) {
175
- logger.error('Error during IdP authentication initiation:', e.message);
176
- return;
177
- }
174
+ logger.error('Error during IdP authentication initialization:', e.message);
178
175
 
179
- if (!response.ok) {
180
- if (response.status === 404) {
181
- throw new NoSamlRouteError('No route found for saml sso');
176
+ if (e.status === 404) {
177
+ throw new NoSamlRouteError('No route found for SAML SSO');
182
178
  }
183
179
 
184
- if (response.status === 500) {
180
+ if (e.status === 500) {
185
181
  throw new SamlConfigError('SAML/server configuration error');
186
182
  }
187
183
 
188
- const error = await response.json();
189
- throw new Error(error.message || error.reason);
184
+ throw e;
190
185
  }
191
-
192
- return response.json();
193
186
  }
194
187
 
195
188
  async logInViaRefreshToken(refreshToken: string): Promise<Session | null> {
@@ -19,6 +19,7 @@ const sipLogger = IssueReporter.loggerFor('sip.js');
19
19
  const protocolLogger = IssueReporter.loggerFor('sip');
20
20
  const protocolDebugMessages = ['Received WebSocket text message:', 'Sending WebSocket message:'];
21
21
 
22
+ // @deprecated: Use Wazo.Voice.Softphone instead
22
23
  export class Phone extends Emitter {
23
24
  client: WazoWebRTCClient;
24
25
 
@@ -1,3 +1,5 @@
1
+ import BadResponse from '../../domain/BadResponse';
2
+ import ServerError from '../../domain/ServerError';
1
3
  import getApiClient, { setRefreshToken } from '../../service/getApiClient';
2
4
  import { Auth as AuthClass } from '../Auth';
3
5
 
@@ -124,13 +126,8 @@ describe('simple/Auth', () => {
124
126
  describe('initiateIdpAuthentication', () => {
125
127
  const validResponse = { location: 'idp.com/auth', saml_session_id: 'a1b2C3d4' };
126
128
 
127
- it('should return a `location` and `saml_session_id` if the response is ok', async () => {
128
- const initiateIdpAuthentication = jest.fn(() =>
129
- Promise.resolve({
130
- ok: true,
131
- status: 303,
132
- json: () => Promise.resolve(validResponse),
133
- }));
129
+ it('should return a `location` and `saml_session_id` if the response is valid', async () => {
130
+ const initiateIdpAuthentication = jest.fn(() => validResponse);
134
131
 
135
132
  (getApiClient as any).mockImplementation(() => ({
136
133
  auth: {
@@ -141,48 +138,9 @@ describe('simple/Auth', () => {
141
138
  const response = await Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz');
142
139
  expect(response).toBe(validResponse);
143
140
  });
144
- it('should return a `NoSamlRouteError` if the response is a 404', async () => {
145
- const initiateIdpAuthentication = jest.fn(() =>
146
- Promise.resolve({
147
- ok: false,
148
- status: 404,
149
- }));
150
-
151
- (getApiClient as any).mockImplementation(() => ({
152
- auth: {
153
- initiateIdpAuthentication,
154
- },
155
- }));
156
-
157
- await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow(
158
- 'No route found for saml sso',
159
- );
160
- });
161
141
 
162
- it('should return an error `message` if the response is NOT ok', async () => {
163
- const initiateIdpAuthentication = jest.fn(() =>
164
- Promise.resolve({
165
- ok: false,
166
- json: () => Promise.resolve({ message: 'an error message', reason: 'an error reason' }),
167
- }));
168
-
169
- (getApiClient as any).mockImplementation(() => ({
170
- auth: {
171
- initiateIdpAuthentication,
172
- },
173
- }));
174
-
175
- await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow(
176
- 'an error message',
177
- );
178
- });
179
-
180
- it('should return an error `reason` if there is no `message` and the response is NOT ok', async () => {
181
- const initiateIdpAuthentication = jest.fn(() =>
182
- Promise.resolve({
183
- ok: false,
184
- json: () => Promise.resolve({ reason: 'an error reason' }),
185
- }));
142
+ it('should return a `NoSamlRouteError` if the response is a 404', async () => {
143
+ const initiateIdpAuthentication = jest.fn(() => { throw BadResponse.fromText('an error message', 404); });
186
144
 
187
145
  (getApiClient as any).mockImplementation(() => ({
188
146
  auth: {
@@ -191,48 +149,12 @@ describe('simple/Auth', () => {
191
149
  }));
192
150
 
193
151
  await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow(
194
- 'an error reason',
152
+ 'No route found for SAML SSO',
195
153
  );
196
154
  });
197
155
 
198
- it('should return an error with a null message if there is no `message` and `reason`', async () => {
199
- const initiateIdpAuthentication = jest.fn(() =>
200
- Promise.resolve({
201
- ok: false,
202
- json: () => Promise.resolve({}),
203
- }));
204
-
205
- (getApiClient as any).mockImplementation(() => ({
206
- auth: {
207
- initiateIdpAuthentication,
208
- },
209
- }));
210
-
211
- await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('');
212
- });
213
-
214
- it('should return a NoSAMLRouteError if the response is a 404', async () => {
215
- const initiateIdpAuthentication = jest.fn(() =>
216
- Promise.resolve({
217
- ok: false,
218
- status: 404,
219
- }));
220
-
221
- (getApiClient as any).mockImplementation(() => ({
222
- auth: {
223
- initiateIdpAuthentication,
224
- },
225
- }));
226
-
227
- await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('No route found for saml sso');
228
- });
229
-
230
- it('should return a SamlConfigError if the response is a 500', async () => {
231
- const initiateIdpAuthentication = jest.fn(() =>
232
- Promise.resolve({
233
- ok: false,
234
- status: 500,
235
- }));
156
+ it('should return a `SamlConfigError` if the response is a 500', async () => {
157
+ const initiateIdpAuthentication = jest.fn(() => { throw ServerError.fromText('an error message', 500); });
236
158
 
237
159
  (getApiClient as any).mockImplementation(() => ({
238
160
  auth: {
@@ -43,7 +43,7 @@ export type EstablishedActionTypes = typeof EstablishedActions[keyof typeof Esta
43
43
 
44
44
  const callMachine = setup({
45
45
  guards: {
46
- isRegistered: () => softphone.getState() === SoftphoneStates.REGISTERED,
46
+ isRegistered: () => softphone.state === SoftphoneStates.REGISTERED,
47
47
  },
48
48
  }).createMachine({
49
49
  id: 'call',
@@ -8,8 +8,8 @@ import IssueReporter from '../service/IssueReporter';
8
8
 
9
9
  const logger = IssueReporter.loggerFor('state-machine');
10
10
 
11
- export const getState = (actor: SoftphoneActorRef | CallActorRef): SoftphoneStates | CallStates | EstablishedStateTypes => {
12
- return actor.getSnapshot().value as SoftphoneStates | CallStates | EstablishedStateTypes;
11
+ export const getState = (actor: SoftphoneActorRef | CallActorRef): SoftphoneStates | CallStates | EstablishedStateTypes | Record<CallStates, EstablishedStateTypes> => {
12
+ return actor.getSnapshot().value as SoftphoneStates | CallStates | EstablishedStateTypes | Record<CallStates, EstablishedStateTypes>;
13
13
  };
14
14
 
15
15
  export const can = (actor: SoftphoneActorRef | CallActorRef, action: SoftphoneActions | CallActions | EstablishedCallActions): boolean => {
@@ -158,3 +158,23 @@ describe('With a refresh token', () => {
158
158
  });
159
159
  });
160
160
  });
161
+
162
+ describe('base64Encode', () => {
163
+ it('should encode a string using base64', () => {
164
+ const input = 'Hello World!';
165
+ const expectedOutput = 'SGVsbG8gV29ybGQh';
166
+ expect(ApiRequester.base64Encode(input)).toBe(expectedOutput);
167
+ });
168
+
169
+ it('should handle special characters', () => {
170
+ const input = 'éà$@€';
171
+ const expectedOutput = 'w6nDoCRA4oKs';
172
+ expect(ApiRequester.base64Encode(input)).toBe(expectedOutput);
173
+ });
174
+
175
+ it('should handle empty string', () => {
176
+ const input = '';
177
+ const expectedOutput = '';
178
+ expect(ApiRequester.base64Encode(input)).toBe(expectedOutput);
179
+ });
180
+ });
@@ -64,7 +64,15 @@ export default class ApiRequester {
64
64
  }
65
65
 
66
66
  static base64Encode(str: string): string {
67
- return typeof btoa !== 'undefined' ? btoa(str) : Base64.encode(str);
67
+ if (typeof btoa !== 'undefined') {
68
+ try {
69
+ return btoa(str);
70
+ } catch (error) {
71
+ // fall back to Base64.encode if btoa fails
72
+ }
73
+ }
74
+
75
+ return Base64.encode(str);
68
76
  }
69
77
 
70
78
  // @see https://github.com/facebook/flow/issues/183#issuecomment-358607052
package/src/utils/sdp.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-underscore-dangle */
1
2
  import sdpParser from 'sdp-transform';
2
3
  import { UserAgent } from 'sip.js/lib/api/user-agent';
3
4
  import { Inviter } from 'sip.js/lib/api';
@@ -174,3 +175,18 @@ export const replaceLocalIpModifier = (description: Record<string, any>) => Prom
174
175
  });
175
176
 
176
177
  export const makeURI = (target: string, host: string): URI | undefined => UserAgent.makeURI(`sip:${target}@${host}`);
178
+
179
+ export const getCallNumber = (sipCall: SipCall): string => {
180
+ // eslint-disable-next-line
181
+ const identity = sipCall ? sipCall.remoteIdentity || sipCall.assertedIdentity : null;
182
+ // @ts-ignore: private
183
+ return identity ? identity.uri._normal.user : null;
184
+ };
185
+
186
+ export const getCallDisplayName = (sipCall: SipCall): string => {
187
+ // eslint-disable-next-line
188
+ const identity = sipCall ? sipCall.remoteIdentity || sipCall.assertedIdentity : null;
189
+ const number = getCallNumber(sipCall);
190
+
191
+ return identity ? identity.displayName || number : number;
192
+ };
@@ -1,4 +1,4 @@
1
- /* eslint-disable no-underscore-dangle, no-param-reassign */
1
+ /* eslint-disable no-underscore-dangle, no-param-reassign, no-restricted-syntax */
2
2
  import type { SessionDescriptionHandler } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler';
3
3
  import type { SessionDescriptionHandlerFactoryOptions } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler-factory-options';
4
4
  import type { SessionDescriptionHandlerConfiguration } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler-configuration';
@@ -246,6 +246,28 @@ export const toggleVideo = (sipCall: SipCall, muteCamera: boolean): void => {
246
246
  }
247
247
  };
248
248
 
249
+ export const isVideoMuted = (sipCall: SipCall): boolean => {
250
+ const sdh = sipCall.sessionDescriptionHandler as SessionDescriptionHandler;
251
+ const pc = sdh?.peerConnection as PeerConnection;
252
+
253
+ if (pc?.getSenders) {
254
+ for (const sender of pc.getSenders()) {
255
+ if (sender && sender.track && sender.track.kind === 'video') {
256
+ return !sender.track.enabled;
257
+ }
258
+ }
259
+ } else {
260
+ for (const stream of pc.getLocalStreams()) {
261
+ // eslint-disable-next-line no-unreachable-loop
262
+ for (const track of stream.getVideoTracks()) {
263
+ return !track.enabled;
264
+ }
265
+ }
266
+ }
267
+
268
+ return true;
269
+ };
270
+
249
271
  export const cleanupStream = (stream: MediaStream): void => {
250
272
  stream.getTracks().filter(track => track.enabled).forEach(track => track.stop());
251
273
  };
@@ -329,7 +351,7 @@ export const getMediaConfiguration = (enableVideo: boolean, conference = false,
329
351
  return {
330
352
  constraints: {
331
353
  // Exact constraint are not supported with `getDisplayMedia` and we must have a video=false in desktop screenshare
332
- audio: screenSharing ? !isDesktop : constraints?.audio,
354
+ audio: screenSharing ? !isDesktop : withAudio ? getAudioConstraints(true) : false,
333
355
  video: screenSharing ? isDesktop ? {
334
356
  mandatory: {
335
357
  chromeMediaSource: 'desktop',