@wazo/sdk 0.37.0-rc.5 → 0.37.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 (149) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +93 -16
  3. package/esm/__tests__/web-rtc-client.test.js +153 -54
  4. package/esm/api/agentd.js +24 -3
  5. package/esm/api/amid.js +7 -1
  6. package/esm/api/application.js +24 -3
  7. package/esm/api/auth.js +24 -3
  8. package/esm/api/call-logd.js +78 -20
  9. package/esm/api/calld.js +24 -20
  10. package/esm/api/chatd.js +24 -3
  11. package/esm/api/confd.js +24 -3
  12. package/esm/api/ctid-ng.js +24 -3
  13. package/esm/api/dird.js +27 -6
  14. package/esm/api/webhookd.js +7 -1
  15. package/esm/api-client.js +93 -16
  16. package/esm/checker/Checker.js +156 -54
  17. package/esm/checker/checks/aor.js +95 -16
  18. package/esm/checker/checks/api.js +94 -16
  19. package/esm/checker/checks/index.js +156 -54
  20. package/esm/checker/checks/wazo-websocket.js +7 -1
  21. package/esm/checker/checks/webrtc-transport.js +153 -54
  22. package/esm/checker/checks/webrtc.js +153 -54
  23. package/esm/domain/AdHocAPIConference.js +1717 -1640
  24. package/esm/domain/CallLog.js +71 -19
  25. package/esm/domain/CallSession.js +1 -18
  26. package/esm/domain/Contact.js +3 -3
  27. package/esm/domain/Features.js +93 -16
  28. package/esm/domain/IndirectTransfer.js +0 -17
  29. package/esm/domain/Phone/CTIPhone.js +100 -19
  30. package/esm/domain/Phone/Phone.js +0 -17
  31. package/esm/domain/Phone/WebRTCPhone.js +51 -36
  32. package/esm/domain/Recording.js +51 -0
  33. package/esm/domain/Room.js +0 -17
  34. package/esm/domain/SwitchboardCall.js +0 -17
  35. package/esm/domain/__tests__/CallLog.test.js +89 -19
  36. package/esm/domain/__tests__/CallSession.test.js +1 -18
  37. package/esm/domain/__tests__/Contact.test.js +3 -3
  38. package/esm/domain/__tests__/IndirectTransfer.test.js +1 -18
  39. package/esm/domain/__tests__/Room.test.js +1 -18
  40. package/esm/domain/__tests__/Session.test.js +7 -0
  41. package/esm/index.js +3436 -2911
  42. package/esm/lib/WazoSessionDescriptionHandler.js +28 -10
  43. package/esm/service/CallApi.js +93 -16
  44. package/esm/service/IssueReporter.js +7 -1
  45. package/esm/service/__tests__/IssueReporter.test.js +26 -2
  46. package/esm/service/getApiClient.js +93 -16
  47. package/esm/simple/Auth.js +619 -97
  48. package/esm/simple/Configuration.js +619 -97
  49. package/esm/simple/Directory.js +93 -16
  50. package/esm/simple/Phone.js +3261 -2739
  51. package/esm/simple/Websocket.js +7 -1
  52. package/esm/simple/index.js +619 -97
  53. package/esm/simple/room/LocalParticipant.js +619 -97
  54. package/esm/simple/room/Participant.js +619 -97
  55. package/esm/simple/room/RemoteParticipant.js +619 -97
  56. package/esm/simple/room/Room.js +619 -97
  57. package/esm/simple/room/Stream.js +619 -97
  58. package/esm/simple/room/__tests__/RemoteParticipant.test.js +619 -97
  59. package/esm/simple/utils.js +619 -97
  60. package/esm/utils/__tests__/api-requester.test.js +32 -6
  61. package/esm/utils/api-requester.js +24 -3
  62. package/esm/web-rtc-client.js +153 -54
  63. package/esm/websocket-client.js +7 -1
  64. package/lib/__tests__/api-client.test.js +93 -16
  65. package/lib/__tests__/web-rtc-client.test.js +154 -55
  66. package/lib/api/agentd.js +24 -3
  67. package/lib/api/amid.js +7 -1
  68. package/lib/api/application.js +24 -3
  69. package/lib/api/auth.js +24 -3
  70. package/lib/api/call-logd.js +78 -20
  71. package/lib/api/calld.js +24 -20
  72. package/lib/api/chatd.js +24 -3
  73. package/lib/api/confd.js +24 -3
  74. package/lib/api/ctid-ng.js +24 -3
  75. package/lib/api/dird.js +27 -6
  76. package/lib/api/webhookd.js +7 -1
  77. package/lib/api-client.js +93 -16
  78. package/lib/checker/Checker.js +157 -55
  79. package/lib/checker/checks/aor.js +95 -16
  80. package/lib/checker/checks/api.js +94 -16
  81. package/lib/checker/checks/index.js +157 -55
  82. package/lib/checker/checks/wazo-websocket.js +7 -1
  83. package/lib/checker/checks/webrtc-transport.js +154 -55
  84. package/lib/checker/checks/webrtc.js +154 -55
  85. package/lib/domain/AdHocAPIConference.js +1717 -1640
  86. package/lib/domain/CallLog.js +71 -19
  87. package/lib/domain/CallSession.js +1 -18
  88. package/lib/domain/Contact.js +3 -3
  89. package/lib/domain/Features.js +93 -16
  90. package/lib/domain/IndirectTransfer.js +0 -17
  91. package/lib/domain/Phone/CTIPhone.js +100 -19
  92. package/lib/domain/Phone/Phone.js +0 -16
  93. package/lib/domain/Phone/WebRTCPhone.js +51 -36
  94. package/lib/domain/Recording.js +57 -0
  95. package/lib/domain/Room.js +0 -17
  96. package/lib/domain/SwitchboardCall.js +0 -17
  97. package/lib/domain/__tests__/CallLog.test.js +89 -19
  98. package/lib/domain/__tests__/CallSession.test.js +1 -18
  99. package/lib/domain/__tests__/Contact.test.js +3 -3
  100. package/lib/domain/__tests__/IndirectTransfer.test.js +1 -18
  101. package/lib/domain/__tests__/Room.test.js +1 -18
  102. package/lib/domain/__tests__/Session.test.js +7 -0
  103. package/lib/index.js +3437 -2912
  104. package/lib/lib/WazoSessionDescriptionHandler.js +28 -10
  105. package/lib/service/CallApi.js +93 -16
  106. package/lib/service/IssueReporter.js +7 -1
  107. package/lib/service/__tests__/IssueReporter.test.js +26 -2
  108. package/lib/service/getApiClient.js +93 -16
  109. package/lib/simple/Auth.js +620 -98
  110. package/lib/simple/Configuration.js +620 -98
  111. package/lib/simple/Directory.js +93 -16
  112. package/lib/simple/Phone.js +3262 -2740
  113. package/lib/simple/Websocket.js +7 -1
  114. package/lib/simple/index.js +620 -98
  115. package/lib/simple/room/LocalParticipant.js +620 -98
  116. package/lib/simple/room/Participant.js +620 -98
  117. package/lib/simple/room/RemoteParticipant.js +620 -98
  118. package/lib/simple/room/Room.js +620 -98
  119. package/lib/simple/room/Stream.js +620 -98
  120. package/lib/simple/room/__tests__/RemoteParticipant.test.js +620 -98
  121. package/lib/simple/utils.js +620 -98
  122. package/lib/utils/__tests__/api-requester.test.js +32 -6
  123. package/lib/utils/api-requester.js +24 -3
  124. package/lib/web-rtc-client.js +154 -55
  125. package/lib/websocket-client.js +7 -1
  126. package/package.json +1 -1
  127. package/src/api-client.js +4 -0
  128. package/src/checker/checks/aor.js +2 -0
  129. package/src/checker/checks/api.js +1 -0
  130. package/src/domain/CallLog.js +18 -11
  131. package/src/domain/CallSession.js +1 -2
  132. package/src/domain/Contact.js +3 -3
  133. package/src/domain/Phone/CTIPhone.js +6 -2
  134. package/src/domain/Phone/Phone.js +2 -0
  135. package/src/domain/Phone/WebRTCPhone.js +26 -16
  136. package/src/domain/Recording.js +48 -0
  137. package/src/domain/__tests__/CallLog.test.js +19 -0
  138. package/src/domain/__tests__/Session.test.js +7 -0
  139. package/src/index.js +6 -0
  140. package/src/lib/WazoSessionDescriptionHandler.js +21 -9
  141. package/src/service/IssueReporter.js +7 -1
  142. package/src/service/__tests__/IssueReporter.test.js +20 -1
  143. package/src/simple/Phone.js +5 -1
  144. package/src/simple/index.js +6 -0
  145. package/src/simple/room/Room.js +18 -15
  146. package/src/simple/room/Stream.js +1 -4
  147. package/src/utils/__tests__/api-requester.test.js +9 -4
  148. package/src/utils/api-requester.js +17 -2
  149. package/src/web-rtc-client.js +40 -13
@@ -10,7 +10,6 @@ var jsonToGraphQLQuery = require('json-to-graphql-query/lib/jsonToGraphQLQuery')
10
10
  var invitation = require('sip.js/lib/api/invitation');
11
11
  require('webrtc-adapter');
12
12
  var userAgentState = require('sip.js/lib/api/user-agent-state');
13
- require('sip.js/lib/api/message');
14
13
  var parser = require('sip.js/lib/core/messages/parser');
15
14
  var constants = require('sip.js/lib/core/messages/methods/constants');
16
15
  var uri = require('sip.js/lib/grammar/uri');
@@ -139,7 +138,7 @@ const isMobile = () => typeof navigator !== 'undefined' && navigator.product ===
139
138
 
140
139
  const methods = ['head', 'get', 'post', 'put', 'delete'];
141
140
 
142
- const logger$a = IssueReporter$1 ? IssueReporter$1.loggerFor('api') : console;
141
+ const logger$b = IssueReporter$1 ? IssueReporter$1.loggerFor('api') : console;
143
142
 
144
143
  // Use a function here to be able to mock it in tests
145
144
  const realFetch = () => {
@@ -174,6 +173,7 @@ class ApiRequester {
174
173
 
175
174
 
176
175
 
176
+
177
177
 
178
178
 
179
179
 
@@ -213,6 +213,8 @@ class ApiRequester {
213
213
  this.token = token;
214
214
  }
215
215
 
216
+ this.shouldLogErrors = true;
217
+
216
218
  methods.forEach(method => {
217
219
  // $FlowFixMe
218
220
  ApiRequester.prototype[method] = function sugar(...args) {
@@ -236,6 +238,14 @@ class ApiRequester {
236
238
  this.fetchOptions = options;
237
239
  }
238
240
 
241
+ disableErrorLogging() {
242
+ this.shouldLogErrors = false;
243
+ }
244
+
245
+ enableErrorLogging() {
246
+ this.shouldLogErrors = true;
247
+ }
248
+
239
249
  async call(
240
250
  path ,
241
251
  method = 'get',
@@ -267,7 +277,7 @@ class ApiRequester {
267
277
  };
268
278
 
269
279
  if (this.refreshTokenPromise) {
270
- logger$a.info('A token is already refreshing, waiting ...', { url });
280
+ logger$b.info('A token is already refreshing, waiting ...', { url });
271
281
  await this.refreshTokenPromise;
272
282
  }
273
283
 
@@ -287,7 +297,7 @@ class ApiRequester {
287
297
  return promise.then(async (err ) => {
288
298
  // Check if the token is still valid
289
299
  if (firstCall && this._checkTokenExpired(response, err)) {
290
- logger$a.warn('token expired', { error: err.reason });
300
+ logger$b.warn('token expired', { error: err.reason });
291
301
  // Replay the call after refreshing the token
292
302
  return this._replayWithNewToken(err, path, method, body, headers, parse);
293
303
  }
@@ -296,7 +306,9 @@ class ApiRequester {
296
306
  ? exceptionClass.fromText(err, response.status)
297
307
  : exceptionClass.fromResponse(err, response.status);
298
308
 
299
- logger$a.error('API error', error);
309
+ if (this.shouldLogErrors) {
310
+ logger$b.error('API error', error);
311
+ }
300
312
 
301
313
  throw error;
302
314
  });
@@ -304,7 +316,9 @@ class ApiRequester {
304
316
 
305
317
  return newParse(response, isJson);
306
318
  }).catch(error => {
307
- logger$a.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
319
+ if (this.shouldLogErrors) {
320
+ logger$b.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
321
+ }
308
322
 
309
323
  throw error;
310
324
  });
@@ -331,13 +345,13 @@ class ApiRequester {
331
345
  ) {
332
346
  const isTokenNotFound = this._isTokenNotFound(err);
333
347
  let newPath = path;
334
- logger$a.info('refreshing token', { inProgress: !!this.refreshTokenPromise });
348
+ logger$b.info('refreshing token', { inProgress: !!this.refreshTokenPromise });
335
349
 
336
350
  this.refreshTokenPromise = this.refreshTokenPromise || this.refreshTokenCallback();
337
351
 
338
352
  return this.refreshTokenPromise.then(() => {
339
353
  this.refreshTokenPromise = null;
340
- logger$a.info('token refreshed', { isTokenNotFound });
354
+ logger$b.info('token refreshed', { isTokenNotFound });
341
355
  if (isTokenNotFound) {
342
356
  const pathParts = path.split('/');
343
357
  pathParts.pop();
@@ -615,7 +629,13 @@ class IssueReporter {
615
629
  }
616
630
 
617
631
  _isLevelAbove(level1 , level2 ) {
618
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
632
+ const index1 = LOG_LEVELS.indexOf(level1);
633
+ const index2 = LOG_LEVELS.indexOf(level2);
634
+ if (index1 === -1 || index2 === -1) {
635
+ return false;
636
+ }
637
+
638
+ return index1 > index2;
619
639
  }
620
640
 
621
641
  _makeCategory(category ) {
@@ -1603,7 +1623,7 @@ class Contact {
1603
1623
 
1604
1624
  return new Contact({
1605
1625
  sourceId: single.id || '',
1606
- name: single.displayName,
1626
+ name: single.displayName || '',
1607
1627
  number: numbers.length ? numbers[0].number : '',
1608
1628
  numbers,
1609
1629
  emails,
@@ -1634,7 +1654,7 @@ class Contact {
1634
1654
 
1635
1655
  return new Contact({
1636
1656
  sourceId: single.id || '',
1637
- name: single.name,
1657
+ name: single.name || '',
1638
1658
  number: numbers.length ? numbers[0].number : '',
1639
1659
  numbers,
1640
1660
  emails,
@@ -1735,7 +1755,7 @@ class Contact {
1735
1755
  } = {}) {
1736
1756
  this.id = id;
1737
1757
  this.uuid = uuid;
1738
- this.name = name;
1758
+ this.name = name || '';
1739
1759
  this.number = number;
1740
1760
  this.numbers = numbers;
1741
1761
  this.email = email;
@@ -3349,7 +3369,7 @@ class CallSession {
3349
3369
  return newFrom(callSession, CallSession);
3350
3370
  }
3351
3371
 
3352
- static parseCall(session , call ) {
3372
+ static parseCall(call ) {
3353
3373
  return new CallSession({
3354
3374
  callId: call.id,
3355
3375
  sipCallId: call.sipCallId,
@@ -3376,17 +3396,66 @@ class CallSession {
3376
3396
 
3377
3397
  //
3378
3398
 
3399
+
3400
+
3401
+
3402
+
3403
+
3404
+
3405
+
3406
+
3407
+
3408
+
3409
+
3410
+
3411
+
3412
+
3413
+
3414
+
3415
+ class Recording {
3416
+
3417
+
3418
+
3419
+
3420
+
3421
+
3422
+ static parseMany(recordings ) {
3423
+ return recordings.map(item => Recording.parse(item));
3424
+ }
3425
+
3426
+ static parse(plain ) {
3427
+ return new Recording({
3428
+ deleted: plain.deleted,
3429
+ fileName: plain.filename,
3430
+ end: plain.end_time ? moment__default['default'](plain.end_time).toDate() : null,
3431
+ start: moment__default['default'](plain.start_time).toDate(),
3432
+ uuid: plain.uuid,
3433
+ });
3434
+ }
3435
+
3436
+ constructor({ deleted, end, fileName, start, uuid } ) {
3437
+ this.deleted = deleted;
3438
+ this.end = end;
3439
+ this.fileName = fileName;
3440
+ this.start = start;
3441
+ this.uuid = uuid;
3442
+ }
3443
+ }
3444
+
3445
+ //
3446
+
3379
3447
 
3380
-
3448
+
3381
3449
 
3382
3450
 
3383
3451
 
3384
3452
 
3385
3453
 
3386
-
3454
+
3387
3455
 
3388
3456
 
3389
3457
 
3458
+
3390
3459
 
3391
3460
 
3392
3461
 
@@ -3399,7 +3468,7 @@ class CallSession {
3399
3468
 
3400
3469
 
3401
3470
 
3402
-
3471
+
3403
3472
 
3404
3473
 
3405
3474
 
@@ -3414,13 +3483,14 @@ class CallSession {
3414
3483
 
3415
3484
 
3416
3485
 
3417
-
3486
+
3487
+
3418
3488
 
3419
3489
 
3420
3490
  class CallLog {
3421
3491
 
3422
3492
 
3423
-
3493
+
3424
3494
 
3425
3495
 
3426
3496
 
@@ -3437,7 +3507,7 @@ class CallLog {
3437
3507
 
3438
3508
 
3439
3509
 
3440
-
3510
+
3441
3511
 
3442
3512
  static merge(current , toMerge ) {
3443
3513
  const onlyUnique = (value, index, self) => self.indexOf(value) === index;
@@ -3454,7 +3524,7 @@ class CallLog {
3454
3524
 
3455
3525
  static parse(plain ) {
3456
3526
  return new CallLog({
3457
- answer: moment__default['default'](plain.answer).toDate(),
3527
+ answer: plain.answer ? moment__default['default'](plain.answer).toDate() : null,
3458
3528
  answered: plain.answered,
3459
3529
  callDirection: plain.call_direction,
3460
3530
  destination: {
@@ -3468,13 +3538,14 @@ class CallLog {
3468
3538
  id: plain.id,
3469
3539
  duration: (plain.duration || 0) * 1000, // duration is in seconds
3470
3540
  start: moment__default['default'](plain.start).toDate(),
3471
- end: moment__default['default'](plain.end).toDate(),
3541
+ end: plain.end ? moment__default['default'](plain.end).toDate() : null,
3542
+ recordings: Recording.parseMany(plain.recordings),
3472
3543
  });
3473
3544
  }
3474
3545
 
3475
3546
  static parseNew(plain , session ) {
3476
3547
  return new CallLog({
3477
- answer: moment__default['default'](plain.answer).toDate(),
3548
+ answer: plain.answer ? moment__default['default'](plain.answer).toDate() : null,
3478
3549
  answered: plain.answered,
3479
3550
  callDirection: plain.call_direction,
3480
3551
  destination: {
@@ -3488,7 +3559,8 @@ class CallLog {
3488
3559
  id: plain.id,
3489
3560
  duration: (plain.duration || 0) * 1000, // duration is in seconds
3490
3561
  start: moment__default['default'](plain.start).toDate(),
3491
- end: moment__default['default'](plain.end).toDate(),
3562
+ end: plain.end ? moment__default['default'](plain.end).toDate() : null,
3563
+ recordings: Recording.parseMany(plain.recordings),
3492
3564
  // @TODO: FIXME add verification declined vs missed call
3493
3565
  newMissedCall: session
3494
3566
  && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
@@ -3670,11 +3742,414 @@ class IndirectTransfer {
3670
3742
  }
3671
3743
 
3672
3744
  //
3745
+
3746
+ class CallApi {
3747
+ static async fetchCallLogs(offset , limit ) {
3748
+ return getApiClient().callLogd.listCallLogs(offset, limit);
3749
+ }
3750
+
3751
+ static async fetchDistinctCallLogs(offset , limit , distinct = 'peer_exten') {
3752
+ return getApiClient().callLogd.listDistinctCallLogs(offset, limit, distinct);
3753
+ }
3754
+
3755
+ static async fetchActiveCalls() {
3756
+ return getApiClient().calld.listCalls();
3757
+ }
3758
+
3759
+ static async fetchCallLogsFromDate(from , number ) {
3760
+ return getApiClient().callLogd.listCallLogsFromDate(from, number);
3761
+ }
3762
+
3763
+ static async search(query , limit ) {
3764
+ return getApiClient().callLogd.search(query, limit);
3765
+ }
3766
+
3767
+ static async searchBy(field , value , limit ) {
3768
+ return getApiClient().callLogd.searchBy(field, value, limit);
3769
+ }
3770
+
3771
+ static async fetchSIP(session , line ) {
3772
+ const lineToUse = line || session.primaryLine();
3773
+ return getApiClient().confd.getUserLineSip(session.uuid, lineToUse ? lineToUse.id : null);
3774
+ }
3775
+
3776
+ static async cancelCall(callSession ) {
3777
+ return getApiClient().calld.cancelCall(callSession.callId);
3778
+ }
3779
+
3780
+ static async makeCall(callFromLine , extension , isMobile = false,
3781
+ callbackAllLines = false) {
3782
+ const lineId = callFromLine ? callFromLine.id : null;
3783
+
3784
+ const response = await getApiClient().calld.makeCall(extension, isMobile, lineId, callbackAllLines);
3785
+ return Call.parse(response);
3786
+ }
3787
+
3788
+ static async relocateCall(callId , line , contactIdentifier ) {
3789
+ return getApiClient().calld.relocateCall(callId, 'line', line, contactIdentifier);
3790
+ }
3791
+
3792
+ static async hold(callId ) {
3793
+ return getApiClient().calld.hold(callId);
3794
+ }
3795
+
3796
+ static async resume(callId ) {
3797
+ return getApiClient().calld.resume(callId);
3798
+ }
3799
+
3800
+ static async mute(callId ) {
3801
+ return getApiClient().calld.mute(callId);
3802
+ }
3803
+
3804
+ static async sendDTMF(callId , digits ) {
3805
+ return getApiClient().calld.sendDTMF(callId, digits);
3806
+ }
3807
+
3808
+ static async unmute(callId ) {
3809
+ return getApiClient().calld.unmute(callId);
3810
+ }
3811
+
3812
+ static async transferCall(callId , number , flow ) {
3813
+ return getApiClient().calld.transferCall(callId, number, flow);
3814
+ }
3815
+
3816
+ static async cancelCallTransfer(transferId ) {
3817
+ return getApiClient().calld.cancelCallTransfer(transferId);
3818
+ }
3819
+
3820
+ static async confirmCallTransfer(transferId ) {
3821
+ return getApiClient().calld.confirmCallTransfer(transferId);
3822
+ }
3823
+ }
3824
+
3825
+ //
3826
+
3827
+ const TRANSFER_FLOW_ATTENDED = 'attended';
3673
3828
  const TRANSFER_FLOW_BLIND = 'blind';
3674
3829
 
3675
3830
  // const MINIMUM_WAZO_ENGINE_VERSION_FOR_CTI_HOLD = '20.11';
3676
3831
 
3677
- IssueReporter$1 ? IssueReporter$1.loggerFor('cti-phone') : console;
3832
+ const logger$a = IssueReporter$1 ? IssueReporter$1.loggerFor('cti-phone') : console;
3833
+
3834
+ class CTIPhone extends Emitter {
3835
+
3836
+
3837
+
3838
+
3839
+
3840
+
3841
+
3842
+
3843
+ constructor(session , isMobile = false, callbackAllLines = false) {
3844
+ super();
3845
+ logger$a.info('CTI Phone created');
3846
+ this.session = session;
3847
+ this.isMobile = isMobile;
3848
+ this.callbackAllLines = callbackAllLines;
3849
+ }
3850
+
3851
+ getOptions() {
3852
+ // @FIXME: temporarily disabling this option
3853
+ // const hold = this.session.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_CTI_HOLD);
3854
+
3855
+ return {
3856
+ accept: false,
3857
+ decline: true,
3858
+ mute: true,
3859
+ hold: false,
3860
+ transfer: true,
3861
+ sendKey: true,
3862
+ addParticipant: false,
3863
+ record: true,
3864
+ merge: false,
3865
+ };
3866
+ }
3867
+
3868
+ hasAnActiveCall() {
3869
+ return !!this.currentCall;
3870
+ }
3871
+
3872
+ callCount() {
3873
+ return this.currentCall ? 1 : 0;
3874
+ }
3875
+
3876
+ isWebRTC() {
3877
+ return false;
3878
+ }
3879
+
3880
+ getUserAgent() {
3881
+ return 'cti-phone';
3882
+ }
3883
+
3884
+ startHeartbeat() {
3885
+ }
3886
+
3887
+ setOnHeartbeatTimeout() {
3888
+ }
3889
+
3890
+ setOnHeartbeatCallback() {
3891
+ }
3892
+
3893
+ stopHeartbeat() {
3894
+ }
3895
+
3896
+ bindClientEvents() {
3897
+ }
3898
+
3899
+ onConnect() {
3900
+ }
3901
+
3902
+ onDisconnect() {
3903
+ }
3904
+
3905
+ async makeCall(number , line ) {
3906
+ logger$a.info('make CTI call', { number });
3907
+
3908
+ if (!number) {
3909
+ return null;
3910
+ }
3911
+ try {
3912
+ this.currentCall = await CallApi.makeCall(line, number, this.isMobile, this.callbackAllLines);
3913
+ } catch (_) {
3914
+ // We have to deal with error like `User has no mobile phone number` error in the UI.
3915
+ }
3916
+ if (!this.currentCall) {
3917
+ return null;
3918
+ }
3919
+ const callSession = CallSession.parseCall(this.currentCall);
3920
+ this.eventEmitter.emit('onCallOutgoing', callSession);
3921
+
3922
+ return callSession;
3923
+ }
3924
+
3925
+ accept(callSession ) {
3926
+ if (!callSession) {
3927
+ return Promise.resolve(null);
3928
+ }
3929
+ logger$a.info('accept CTI call', { callId: callSession.getId(), number: callSession.number });
3930
+
3931
+ if (!this.currentCall) {
3932
+ this.currentCall = callSession.call;
3933
+ }
3934
+
3935
+ return Promise.resolve(callSession.getId());
3936
+ }
3937
+
3938
+ endCurrentCall(callSession ) {
3939
+ if (!callSession) {
3940
+ return;
3941
+ }
3942
+ logger$a.info('end current CTI call', { callId: callSession.getId(), number: callSession.number });
3943
+
3944
+ this.currentCall = undefined;
3945
+ this.eventEmitter.emit('onCallEnded', callSession);
3946
+ }
3947
+
3948
+ async hangup(callSession ) {
3949
+ if (!callSession) {
3950
+ return Promise.resolve(false);
3951
+ }
3952
+ logger$a.info('hangup CTI call', { callId: callSession.getId(), number: callSession.number });
3953
+
3954
+ try {
3955
+ await CallApi.cancelCall(callSession);
3956
+ if (this.currentCall && callSession.callId === this.currentCall.id) {
3957
+ this.endCurrentCall(callSession);
3958
+ }
3959
+
3960
+ this.eventEmitter.emit('onCallEnded', callSession);
3961
+ return true;
3962
+ } catch (e) {
3963
+ logger$a.error('hangup CTI call, error', e);
3964
+
3965
+ this.eventEmitter.emit('onCallFailed', callSession);
3966
+ return false;
3967
+ }
3968
+ }
3969
+
3970
+ ignore() {}
3971
+
3972
+ async reject(callSession ) {
3973
+ if (!callSession) {
3974
+ return;
3975
+ }
3976
+ logger$a.info('reject CTI call', { callId: callSession.getId(), number: callSession.number });
3977
+
3978
+ await CallApi.cancelCall(callSession);
3979
+ this.eventEmitter.emit('onCallEnded', callSession);
3980
+ }
3981
+
3982
+ async transfer(callSession , number ) {
3983
+ if (!callSession) {
3984
+ return;
3985
+ }
3986
+ logger$a.info('transfer CTI call', { callId: callSession.getId(), number: callSession.number, to: number });
3987
+
3988
+ await CallApi.transferCall(callSession.callId, number, TRANSFER_FLOW_BLIND);
3989
+ }
3990
+
3991
+ indirectTransfer() {}
3992
+
3993
+ async initiateCTIIndirectTransfer(callSession , number ) {
3994
+ if (!callSession) {
3995
+ return;
3996
+ }
3997
+ logger$a.info('indirect CTI transfer', { callId: callSession.getId(), number: callSession.number, to: number });
3998
+
3999
+ return CallApi.transferCall(callSession.callId, number, TRANSFER_FLOW_ATTENDED);
4000
+ }
4001
+
4002
+ async cancelCTIIndirectTransfer(transferId ) {
4003
+ logger$a.info('cancel CTI transfer', { transferId });
4004
+
4005
+ return CallApi.cancelCallTransfer(transferId);
4006
+ }
4007
+
4008
+ async confirmCTIIndirectTransfer(transferId ) {
4009
+ logger$a.info('confirm CTI transfer', { transferId });
4010
+
4011
+ return CallApi.confirmCallTransfer(transferId);
4012
+ }
4013
+
4014
+ sendKey(callSession , digits ) {
4015
+ if (!callSession) {
4016
+ return;
4017
+ }
4018
+ logger$a.info('send CTI key', { callId: callSession.getId(), number: callSession.number, digits });
4019
+
4020
+ return CallApi.sendDTMF(callSession.callId, digits);
4021
+ }
4022
+
4023
+ onConnectionMade() {
4024
+ logger$a.info('on CTI connection made');
4025
+
4026
+ this.eventEmitter.emit('onCallAccepted');
4027
+ }
4028
+
4029
+ async close() {
4030
+ logger$a.info('CTI close');
4031
+
4032
+ return Promise.resolve();
4033
+ }
4034
+
4035
+ async hold(callSession ) {
4036
+ if (!callSession) {
4037
+ return;
4038
+ }
4039
+ logger$a.info('CTI hold', { callId: callSession.getId(), number: callSession.number });
4040
+
4041
+ return CallApi.hold(callSession.callId);
4042
+ }
4043
+
4044
+ async resume(callSession ) {
4045
+ if (!callSession) {
4046
+ return;
4047
+ }
4048
+ logger$a.info('CTI resume', { callId: callSession.getId(), number: callSession.number });
4049
+
4050
+ return CallApi.resume(callSession.callId);
4051
+ }
4052
+
4053
+ async mute(callSession ) {
4054
+ if (!callSession) {
4055
+ return;
4056
+ }
4057
+ logger$a.info('CTI mute', { callId: callSession.getId(), number: callSession.number });
4058
+
4059
+ return CallApi.mute(callSession.callId);
4060
+ }
4061
+
4062
+ async unmute(callSession ) {
4063
+ if (!callSession) {
4064
+ return;
4065
+ }
4066
+ logger$a.info('CTI unmute', { callId: callSession.getId(), number: callSession.number });
4067
+
4068
+ return CallApi.unmute(callSession.callId);
4069
+ }
4070
+
4071
+ putOnSpeaker() {}
4072
+
4073
+ putOffSpeaker() {}
4074
+
4075
+ turnCameraOff() {}
4076
+
4077
+ turnCameraOn() {}
4078
+
4079
+ changeAudioInputDevice() {}
4080
+
4081
+ changeVideoInputDevice() {}
4082
+
4083
+ changeAudioDevice() {}
4084
+
4085
+ changeRingDevice() {}
4086
+
4087
+ changeAudioVolume() {}
4088
+
4089
+ changeRingVolume() {}
4090
+
4091
+ hasVideo() {
4092
+ return false;
4093
+ }
4094
+
4095
+ getLocalStreamForCall() {
4096
+ return null;
4097
+ }
4098
+
4099
+ getRemoteStreamForCall() {
4100
+ return null;
4101
+ }
4102
+
4103
+ getLocalVideoStream() {
4104
+ return null;
4105
+ }
4106
+
4107
+ setActiveSipSession() {}
4108
+
4109
+ isRegistered() {
4110
+ return true;
4111
+ }
4112
+
4113
+ hasIncomingCallSession() {
4114
+ return true;
4115
+ }
4116
+
4117
+ hasActiveRemoteVideoStream() {
4118
+ return false;
4119
+ }
4120
+
4121
+ getCurrentCallSession() {
4122
+ return this.currentCall ? CallSession.parseCall(this.currentCall) : null;
4123
+ }
4124
+
4125
+ enableRinging() {}
4126
+
4127
+ sendMessage() {}
4128
+
4129
+ disableRinging() {}
4130
+
4131
+ getLocalStream() {
4132
+ return null;
4133
+ }
4134
+
4135
+ getRemoteStream() {
4136
+ return null;
4137
+ }
4138
+
4139
+ getRemoteVideoStream() {
4140
+ return null;
4141
+ }
4142
+
4143
+ getRemoteAudioStream() {
4144
+ return null;
4145
+ }
4146
+
4147
+ hasLocalVideo() {
4148
+ return false;
4149
+ }
4150
+
4151
+ useLocalVideoElement() {}
4152
+ }
3678
4153
 
3679
4154
  /* */
3680
4155
 
@@ -4709,6 +5184,10 @@ class ApiClient {
4709
5184
  this.fetchOptions = fetchOptions;
4710
5185
  this.client.setFetchOptions(fetchOptions);
4711
5186
  }
5187
+
5188
+ disableErrorLogging() {
5189
+ this.client.disableErrorLogging();
5190
+ }
4712
5191
  }
4713
5192
 
4714
5193
  /* eslint-disable dot-notation */
@@ -5250,23 +5729,6 @@ const areCandidateValid = (candidates ) =>
5250
5729
  getSrflxOrRelay(candidates)
5251
5730
  .length > 0;
5252
5731
 
5253
- const toggleVideoDirection = (sdp , direction ) => {
5254
- const parsedSdp = sdpParser__default['default'].parse(sdp);
5255
- parsedSdp.media = parsedSdp.media.map(media => ({
5256
- ...media,
5257
- ...(media.type === 'video' ? { direction } : {}),
5258
- }));
5259
-
5260
- return sdpParser__default['default'].write(parsedSdp);
5261
- };
5262
-
5263
- const deactivateVideoModifier = (rawDescription ) => {
5264
- const description = rawDescription;
5265
- description.sdp = toggleVideoDirection(description.sdp, 'inactive');
5266
-
5267
- return Promise.resolve(description);
5268
- };
5269
-
5270
5732
  const hasAnActiveVideo = (sdp ) => {
5271
5733
  if (!sdp) {
5272
5734
  return false;
@@ -5378,6 +5840,7 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
5378
5840
  const iceRestart = options.offerOptions ? options.offerOptions.iceRestart : false;
5379
5841
 
5380
5842
  const isConference = options ? !!options.conference : false;
5843
+ const audioOnly = options ? !!options.audioOnly : false;
5381
5844
 
5382
5845
  // We should wait for ice when iceRestart (reinvite) or for the first invite
5383
5846
  // We shouldn't wait for ice when holding or resuming the call
@@ -5404,16 +5867,16 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
5404
5867
  };
5405
5868
 
5406
5869
  return this.getLocalMediaStream(options)
5407
- .then(() => this.updateDirection(options, isConference))
5870
+ .then(() => this.updateDirection(options, isConference, audioOnly))
5408
5871
  .then(() => this.createDataChannel(options))
5409
5872
  .then(() => {
5410
- if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options)) {
5873
+ if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options) && !audioOnly) {
5411
5874
  // Add a video an empty bundle to be able to replaceTrack when joining a conference without video
5412
5875
  this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
5413
5876
  }
5414
5877
  })
5415
5878
  .then(() => this.createLocalOfferOrAnswer(options))
5416
- .then((sessionDescription) => this.setLocalSessionDescription(sessionDescription))
5879
+ .then(sessionDescription => this.setLocalSessionDescription(sessionDescription))
5417
5880
  .then(() => this.waitForIceGatheringComplete(iceRestart, iceTimeout))
5418
5881
  .then(() => this.getLocalSessionDescription())
5419
5882
  .then(description => {
@@ -5536,7 +5999,8 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
5536
5999
  }
5537
6000
 
5538
6001
  // Overridden to send `inactive` in conference
5539
- updateDirection(options , isConference = false) {
6002
+ updateDirection(options , isConference = false,
6003
+ audioOnly = false) {
5540
6004
  if (this._peerConnection === undefined) {
5541
6005
  return Promise.reject(new Error('Peer connection closed.'));
5542
6006
  }
@@ -5551,14 +6015,17 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
5551
6015
  // if we are stable, assume we are creating a local offer
5552
6016
  this.logger.debug('SessionDescriptionHandler.updateDirection - setting offer direction');
5553
6017
  // determine the direction to offer given the current direction and hold state
5554
- const directionToOffer = (currentDirection ) => {
6018
+ const directionToOffer = (currentDirection , transceiver /* RTCRtpTransceiver */) => {
5555
6019
  if (isConference) {
6020
+ if (audioOnly && transceiver.receiver.track && transceiver.receiver.track.kind === 'video') {
6021
+ return 'sendonly';
6022
+ }
5556
6023
  return options && options.hold ? 'sendonly' : 'sendrecv';
5557
6024
  }
5558
6025
 
5559
6026
  switch (currentDirection) {
5560
6027
  case 'inactive':
5561
- return options && options.hold ? 'inactive' : 'recvonly';
6028
+ return options && options.hold ? 'inactive' : (isConference && !audioOnly ? 'sendrecv' : 'recvonly');
5562
6029
  case 'recvonly':
5563
6030
  return options && options.hold ? 'inactive' : 'recvonly';
5564
6031
  case 'sendonly':
@@ -5574,7 +6041,7 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
5574
6041
  // set the transceiver direction to the offer direction
5575
6042
  this._peerConnection.getTransceivers().forEach((transceiver) => {
5576
6043
  if (transceiver.direction /* guarding, but should always be true */) {
5577
- const offerDirection = directionToOffer(transceiver.direction);
6044
+ const offerDirection = directionToOffer(transceiver.direction, transceiver);
5578
6045
  if (transceiver.direction !== offerDirection) {
5579
6046
  // eslint-disable-next-line no-param-reassign
5580
6047
  transceiver.direction = offerDirection;
@@ -5633,6 +6100,13 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
5633
6100
  // set the transceiver direction to the answer direction
5634
6101
  this._peerConnection.getTransceivers().forEach((transceiver) => {
5635
6102
  if (transceiver.direction /* guarding, but should always be true */) {
6103
+ const { receiver } = transceiver;
6104
+ if (isConference && audioOnly && receiver.track && receiver.track.kind === 'video') {
6105
+ // eslint-disable-next-line no-param-reassign
6106
+ transceiver.direction = 'inactive';
6107
+ return;
6108
+ }
6109
+
5636
6110
  if (transceiver.direction !== 'stopped' && transceiver.direction !== answerDirection) {
5637
6111
  // eslint-disable-next-line no-param-reassign
5638
6112
  transceiver.direction = answerDirection;
@@ -5671,8 +6145,8 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
5671
6145
  }
5672
6146
  const sender = pc.getSenders().find((otherSender) => otherSender.track && otherSender.track.kind === kind);
5673
6147
 
5674
- // Do not reuse sender tracks in SFU
5675
- if (sender && !sfu) {
6148
+ // Do not reuse sender video tracks in SFU
6149
+ if (sender && (!sfu || newTrack.kind === 'audio')) {
5676
6150
  trackUpdates.push(
5677
6151
  new Promise((resolve) => {
5678
6152
  this.logger.debug(`SessionDescriptionHandler.setLocalMediaStream - replacing sender ${kind} track`);
@@ -6224,9 +6698,15 @@ class WebRTCClient extends Emitter {
6224
6698
 
6225
6699
  call(number , enableVideo , audioOnly = false, conference = false) {
6226
6700
  logger$7.info('sdk webrtc creating call', { number, enableVideo, audioOnly });
6227
- const inviterOptions = {};
6701
+ const inviterOptions = {
6702
+ sessionDescriptionHandlerOptionsReInvite: {
6703
+ conference,
6704
+ audioOnly,
6705
+ },
6706
+ };
6707
+
6228
6708
  if (audioOnly) {
6229
- inviterOptions.sessionDescriptionHandlerModifiersReInvite = [deactivateVideoModifier];
6709
+ inviterOptions.sessionDescriptionHandlerModifiersReInvite = [modifiers.stripVideo];
6230
6710
  }
6231
6711
 
6232
6712
  const session = new inviter.Inviter(this.userAgent, this._makeURI(number), inviterOptions);
@@ -6255,10 +6735,11 @@ class WebRTCClient extends Emitter {
6255
6735
  sessionDescriptionHandlerOptions: this.getMediaConfiguration(enableVideo || false, conference),
6256
6736
  };
6257
6737
 
6258
- inviteOptions.sessionDescriptionHandlerModifiers = [replaceLocalIpModifier];
6738
+ inviteOptions.sessionDescriptionHandlerOptions.audioOnly = audioOnly;
6259
6739
 
6740
+ inviteOptions.sessionDescriptionHandlerModifiers = [replaceLocalIpModifier];
6260
6741
  if (audioOnly) {
6261
- inviteOptions.sessionDescriptionHandlerModifiers.push(deactivateVideoModifier);
6742
+ inviteOptions.sessionDescriptionHandlerModifiers.push(modifiers.stripVideo);
6262
6743
  }
6263
6744
 
6264
6745
  // Do not await invite here or we'll miss the Establishing state transition
@@ -6472,6 +6953,9 @@ class WebRTCClient extends Emitter {
6472
6953
  if (!this._isWeb()) {
6473
6954
  options.sessionDescriptionHandlerModifiers = [modifiers.holdModifier];
6474
6955
  }
6956
+ options.sessionDescriptionHandlerOptions = {
6957
+ constraints: options.constraints,
6958
+ };
6475
6959
 
6476
6960
  // Send re-INVITE
6477
6961
  return session.invite(options);
@@ -6511,6 +6995,9 @@ class WebRTCClient extends Emitter {
6511
6995
  // (eg: holdModifier)
6512
6996
  options.sessionDescriptionHandlerModifiers = [];
6513
6997
  }
6998
+ options.sessionDescriptionHandlerOptions = {
6999
+ constraints: options.constraints,
7000
+ };
6514
7001
 
6515
7002
  // Send re-INVITE
6516
7003
  return session.invite(options);
@@ -6740,7 +7227,8 @@ class WebRTCClient extends Emitter {
6740
7227
  ? this.video.deviceId.exact : undefined;
6741
7228
  }
6742
7229
 
6743
- reinvite(sipSession , newConstraints = null, conference = false) {
7230
+ reinvite(sipSession , newConstraints = null, conference = false,
7231
+ audioOnly = false) {
6744
7232
  if (!sipSession) {
6745
7233
  return false;
6746
7234
  }
@@ -6750,16 +7238,17 @@ class WebRTCClient extends Emitter {
6750
7238
  }
6751
7239
 
6752
7240
  const wasMuted = this.isAudioMuted(sipSession);
6753
- // When upgrading to video, remove the `deactivateVideoModifier` modifiers
7241
+ // When upgrading to video, remove the `stripVideo` modifiers
6754
7242
  if (newConstraints && newConstraints.video) {
6755
- const modifiers = sipSession.sessionDescriptionHandlerModifiersReInvite;
6756
- sipSession.sessionDescriptionHandlerModifiersReInvite = modifiers.filter(modifier =>
6757
- modifier !== deactivateVideoModifier);
7243
+ const modifiers$1 = sipSession.sessionDescriptionHandlerModifiersReInvite;
7244
+ sipSession.sessionDescriptionHandlerModifiersReInvite = modifiers$1.filter(modifier =>
7245
+ modifier !== modifiers.stripVideo);
6758
7246
  }
6759
7247
 
6760
7248
  sipSession.sessionDescriptionHandlerOptionsReInvite = {
6761
7249
  ...sipSession.sessionDescriptionHandlerOptionsReInvite,
6762
7250
  conference,
7251
+ audioOnly,
6763
7252
  };
6764
7253
 
6765
7254
  const shouldDoVideo = newConstraints ? newConstraints.video : this.sessionWantsToDoVideo(sipSession);
@@ -6799,6 +7288,8 @@ class WebRTCClient extends Emitter {
6799
7288
  },
6800
7289
  sessionDescriptionHandlerOptions: {
6801
7290
  constraints,
7291
+ conference,
7292
+ audioOnly,
6802
7293
  offerOptions: {
6803
7294
  iceRestart: false,
6804
7295
  },
@@ -7258,6 +7749,8 @@ class WebRTCClient extends Emitter {
7258
7749
 
7259
7750
  // Invitation and Inviter extends Session
7260
7751
  _setupSession(session ) {
7752
+ const sipSessionId = this.getSipSessionId(session);
7753
+
7261
7754
  // When receiving an Invitation, the delegate is not defined.
7262
7755
  if (!session.delegate) {
7263
7756
  session.delegate = {};
@@ -7281,8 +7774,18 @@ class WebRTCClient extends Emitter {
7281
7774
  };
7282
7775
  };
7283
7776
 
7777
+ const oldInviteRequest = session.onInviteRequest.bind(session);
7778
+ let hadRemoteVideo = false;
7779
+
7780
+ // Monkey patch `onInviteRequest` to be able to know if there was a remote video stream before `onInvite` is called
7781
+ // Becase when `onInvite` is called we already got the video track
7782
+ session.onInviteRequest = (request) => {
7783
+ hadRemoteVideo = this.hasARemoteVideoTrack(sipSessionId);
7784
+
7785
+ oldInviteRequest(request);
7786
+ };
7787
+
7284
7788
  session.delegate.onInvite = (request ) => {
7285
- const sipSessionId = this.getSipSessionId(session);
7286
7789
  let updatedCalleeName = null;
7287
7790
  let updatedNumber = null;
7288
7791
  if (session.assertedIdentity) {
@@ -7292,7 +7795,6 @@ class WebRTCClient extends Emitter {
7292
7795
  logger$7.info('re-invite received', { updatedCalleeName, updatedNumber });
7293
7796
 
7294
7797
  // Useful to know if it's a video upgrade or an unhold from a remote peer with a video stream
7295
- const hadRemoteVideo = this.hasARemoteVideoTrack(sipSessionId);
7296
7798
 
7297
7799
  // Update SDP
7298
7800
  // Remote video is handled by the `track` event. Here we're dealing with video stream removal.
@@ -7342,7 +7844,7 @@ class WebRTCClient extends Emitter {
7342
7844
  }
7343
7845
 
7344
7846
  _isAudioOnly(session ) {
7345
- return session.sessionDescriptionHandlerModifiersReInvite.find(modifier => modifier === deactivateVideoModifier);
7847
+ return session.sessionDescriptionHandlerModifiersReInvite.find(modifier => modifier === modifiers.stripVideo);
7346
7848
  }
7347
7849
 
7348
7850
  _setupMedias(session , newStream ) {
@@ -7715,9 +8217,14 @@ class WebRTCPhone extends Emitter {
7715
8217
  return true;
7716
8218
  }
7717
8219
 
7718
- async sendReinvite(callSession , constraints = null, conference = false) {
8220
+ async sendReinvite(callSession , constraints = null, conference = false,
8221
+ audioOnly = false) {
7719
8222
  const sipSession = this.findSipSession(callSession);
7720
- logger$6.info('WebRTC phone - send reinvite', { sessionId: sipSession ? sipSession.id : null, constraints });
8223
+ logger$6.info('WebRTC phone - send reinvite', {
8224
+ sessionId: sipSession ? sipSession.id : null,
8225
+ constraints,
8226
+ audioOnly,
8227
+ });
7721
8228
 
7722
8229
  if (!sipSession) {
7723
8230
  return null;
@@ -7743,7 +8250,7 @@ class WebRTCPhone extends Emitter {
7743
8250
  // Force reinvite in SDH
7744
8251
  sipSession.sessionDescriptionHandler.localMediaStreamConstraints = null;
7745
8252
 
7746
- return this.client.reinvite(sipSession, constraints, conference);
8253
+ return this.client.reinvite(sipSession, constraints, conference, audioOnly);
7747
8254
  }
7748
8255
 
7749
8256
  getUserAgent() {
@@ -7819,9 +8326,9 @@ class WebRTCPhone extends Emitter {
7819
8326
  }
7820
8327
 
7821
8328
  // Release local video stream when downgrading to audio
7822
- const localVideoStream = sipSession.sessionDescriptionHandler.localMediaStream;
8329
+ const localStream = sipSession.sessionDescriptionHandler.localMediaStream;
7823
8330
  const pc = sipSession.sessionDescriptionHandler.peerConnection;
7824
- const videoTracks = localVideoStream.getVideoTracks();
8331
+ const videoTracks = localStream.getVideoTracks();
7825
8332
 
7826
8333
  // Remove video senders
7827
8334
  pc.getSenders().filter(sender => sender.track && sender.track.kind === 'video').forEach(videoSender => {
@@ -7836,7 +8343,7 @@ class WebRTCPhone extends Emitter {
7836
8343
  videoTracks.forEach(videoTrack => {
7837
8344
  videoTrack.enabled = false;
7838
8345
  videoTrack.stop();
7839
- localVideoStream.removeTrack(videoTrack);
8346
+ localStream.removeTrack(videoTrack);
7840
8347
  });
7841
8348
 
7842
8349
  this._sendReinviteMessage(callSession, false);
@@ -7853,12 +8360,14 @@ class WebRTCPhone extends Emitter {
7853
8360
 
7854
8361
  const pc = sipSession.sessionDescriptionHandler.peerConnection;
7855
8362
  const shouldScreenShare = constraints && constraints.screen;
8363
+ const options = sipSession.sessionDescriptionHandlerOptionsReInvite;
8364
+ const wasAudioOnly = options && options.audioOnly;
7856
8365
 
7857
8366
  // Check if a video sender already exists
7858
8367
  let videoSender;
7859
8368
  if (isConference) {
7860
8369
  const trans = pc.getTransceivers().find(transceiver => transceiver.mid === '1');
7861
- videoSender = trans.sender;
8370
+ videoSender = trans ? trans.sender : null;
7862
8371
  } else {
7863
8372
  videoSender = pc.getSenders().find(sender => sender.track === null);
7864
8373
  }
@@ -7895,6 +8404,11 @@ class WebRTCPhone extends Emitter {
7895
8404
  this._onScreenSharing(newStream, sipSession, callSession, false);
7896
8405
  }
7897
8406
 
8407
+ // We have to reinvite to change the direction on the bundle when upgrading from an audioOnly conference
8408
+ if (isConference && wasAudioOnly) {
8409
+ return true;
8410
+ }
8411
+
7898
8412
  // No reinvite needed here
7899
8413
  return false;
7900
8414
  }
@@ -7979,7 +8493,7 @@ class WebRTCPhone extends Emitter {
7979
8493
  event.track.enabled = false;
7980
8494
  }
7981
8495
 
7982
- return this.eventEmitter.emit(ON_VIDEO_STREAM, stream, event.track.id, event);
8496
+ return this.eventEmitter.emit(ON_VIDEO_STREAM, stream, event.track.id, event, sipSession);
7983
8497
  };
7984
8498
 
7985
8499
  peerConnection.onremovestream = event => {
@@ -8035,14 +8549,11 @@ class WebRTCPhone extends Emitter {
8035
8549
  const targetCallSession = callSession || this.currentCallSession;
8036
8550
  const conference = this.isConference(targetCallSession);
8037
8551
 
8038
- if (this.currentScreenShare.sender && this.currentScreenShare.hadVideo) {
8039
- // When stopping screenshare and we had video before that, we have to re-upgrade
8040
- await this.sendReinvite(targetCallSession, { audio: false, video: true }, conference);
8041
- } else {
8042
- // When upgrading directly to screenshare (eg: we don't have a videoLocalStream to replace)
8043
- // We have to downgrade to audio.
8044
- await this.sendReinvite(targetCallSession, { audio: false, video: false }, conference);
8045
- }
8552
+ // When stopping screenshare and we had video before that, we have to re-upgrade
8553
+ // When upgrading directly to screenshare (eg: we don't have a videoLocalStream to replace)
8554
+ // We have to downgrade to audio.
8555
+ const screenshareStopped = this.currentScreenShare.sender && this.currentScreenShare.hadVideo;
8556
+ await this.sendReinvite(targetCallSession, { audio: false, video: screenshareStopped }, conference);
8046
8557
  }
8047
8558
  } catch (e) {
8048
8559
  console.warn(e);
@@ -8379,6 +8890,7 @@ class WebRTCPhone extends Emitter {
8379
8890
  }
8380
8891
  }
8381
8892
 
8893
+ // @Deprecated
8382
8894
  unhold(callSession , withEvent = true, isConference = false) {
8383
8895
  logger$6.info('WebRTC unhold', { id: callSession ? callSession.getId() : null });
8384
8896
 
@@ -9376,6 +9888,7 @@ class Phone extends Emitter {
9376
9888
  return this.phone && this.phone.hasLocalVideo(callSession);
9377
9889
  }
9378
9890
 
9891
+ // @Deprecated
9379
9892
  getLocalMediaStream(callSession ) {
9380
9893
  logger$5.warn('Phone.getLocalMediaStream is deprecated, use Phone.getLocalStream instead');
9381
9894
 
@@ -9394,17 +9907,20 @@ class Phone extends Emitter {
9394
9907
  return this.phone ? this.phone.getRemoteVideoStream(callSession) : null;
9395
9908
  }
9396
9909
 
9910
+ // @Deprecated
9397
9911
  getRemoteStreamForCall(callSession ) {
9398
9912
  logger$5.warn('Phone.getRemoteStreamForCall is deprecated, use Phone.getRemoteStream instead');
9399
9913
  return this.getRemoteStream(callSession);
9400
9914
  }
9401
9915
 
9402
9916
  // Returns remote streams directly from the peerConnection
9917
+ // @Deprecated
9403
9918
  getRemoteStreamsForCall(callSession ) {
9404
- logger$5.warn('Phone.getRe is deprecated, use Phone.getLocalStream instead');
9919
+ logger$5.warn('Phone.getRemoteStreamsForCall is deprecated, use Phone.getLocalStream instead');
9405
9920
  return this.getLocalStream(callSession);
9406
9921
  }
9407
9922
 
9923
+ // @Deprecated
9408
9924
  getRemoteVideoStreamForCall(callSession ) {
9409
9925
  logger$5.warn('Phone.getRemoteVideoStreamForCall is deprecated, use Phone.getRemoteVideoStream instead');
9410
9926
 
@@ -10114,10 +10630,7 @@ class Stream {
10114
10630
  element.onloadedmetadata = () => {
10115
10631
  const tracks = this.htmlStream ? this.htmlStream.getVideoTracks() : [];
10116
10632
  tracks.forEach(track => {
10117
- // Do not re-enable the track if already mounted.
10118
- if (!track.loaded) {
10119
- track.enabled = true;
10120
- }
10633
+ track.enabled = true;
10121
10634
  // $FlowFixMe
10122
10635
  track.loaded = true;
10123
10636
  });
@@ -10558,6 +11071,7 @@ var api = {
10558
11071
  check: async (server, session) => {
10559
11072
  const client = new ApiClient({ server });
10560
11073
  client.setToken(session.token);
11074
+ client.disableErrorLogging();
10561
11075
 
10562
11076
  const handleApiError = (apiName, error) => {
10563
11077
  const statusText = error instanceof ServerError ? 'server error' : 'api error';
@@ -10632,6 +11146,8 @@ var aor = {
10632
11146
  check: async (server, session) => {
10633
11147
  const apiClient = new ApiClient({ server });
10634
11148
  apiClient.setToken(session.token);
11149
+ apiClient.disableErrorLogging();
11150
+
10635
11151
  const line = session.primaryWebRtcLine();
10636
11152
  if (!line) {
10637
11153
  return 'No WebRTC line for this user';
@@ -11054,6 +11570,8 @@ const Wazo = {
11054
11570
  ServerError,
11055
11571
  Call,
11056
11572
  CallLog,
11573
+ CTIPhone,
11574
+ Recording,
11057
11575
  ChatMessage,
11058
11576
  ChatRoom,
11059
11577
  Contact,
@@ -11068,6 +11586,7 @@ const Wazo = {
11068
11586
  CallSession,
11069
11587
  IndirectTransfer,
11070
11588
  SwitchboardCall,
11589
+ WebRTCPhone,
11071
11590
  },
11072
11591
 
11073
11592
  // Api
@@ -11502,13 +12021,15 @@ class Room extends Emitter {
11502
12021
  }
11503
12022
  });
11504
12023
 
11505
- this.on(this.ON_VIDEO_STREAM, (stream, streamId) => {
12024
+ this.on(this.ON_VIDEO_STREAM, (stream, streamId, event, sipSession) => {
11506
12025
  logger$1.info('on room video stream', { streamId });
11507
12026
 
12027
+ this._mapMsid(sipSession.body.body);
12028
+
11508
12029
  // ON_VIDEO_STREAM is called before PARTICIPANT_JOINED, so we have to keep stream in `_unassociatedVideoStreams`.
11509
12030
  this._unassociatedVideoStreams[streamId] = stream;
11510
12031
 
11511
- const callId = this._getCallIdFromStreamId(streamId);
12032
+ const callId = this._getCallIdFromTrackId(streamId);
11512
12033
  const participant = callId ? this._getParticipantFromCallId(callId) : null;
11513
12034
  if (participant) {
11514
12035
  this.__associateStreams(participant);
@@ -11552,11 +12073,12 @@ class Room extends Emitter {
11552
12073
  const sdp = sdpParser__default['default'].parse(rawSdp);
11553
12074
  const labelMsidArray = sdp.media.filter(media => !!media.label).map(({ label, msid }) => ({
11554
12075
  label: String(label),
11555
- msid: msid.split(' ')[1],
12076
+ streamId: msid.split(' ')[0],
12077
+ trackId: msid.split(' ')[1],
11556
12078
  }));
11557
12079
 
11558
- labelMsidArray.forEach(({ label, msid }) => {
11559
- this._callIdStreamIdMap[String(label)] = msid;
12080
+ labelMsidArray.forEach(({ label, streamId, trackId }) => {
12081
+ this._callIdStreamIdMap[String(label)] = { streamId, trackId };
11560
12082
 
11561
12083
  const callId = String(label);
11562
12084
  const participant = this._unassociatedParticipants[callId] || this._getParticipantFromCallId(callId);
@@ -11786,9 +12308,9 @@ class Room extends Emitter {
11786
12308
  _onParticipantTrackUpdate(oldParticipant , update ) {
11787
12309
  const newParticipant = oldParticipant;
11788
12310
 
11789
- const label = this._callIdStreamIdMap[newParticipant.callId];
12311
+ const { trackId } = this._callIdStreamIdMap[newParticipant.callId] || {};
11790
12312
  const pc = Wazo.Phone.phone.currentSipSession.sessionDescriptionHandler.peerConnection;
11791
- const stream = pc.getRemoteStreams().find(otherStream => otherStream.getTracks().some(track => track.id === label));
12313
+ const stream = pc.getRemoteStreams().find(someStream => someStream.getTracks().some(track => track.id === trackId));
11792
12314
 
11793
12315
  if (update === 'downgrade') {
11794
12316
  newParticipant.streams = [];
@@ -11808,21 +12330,21 @@ class Room extends Emitter {
11808
12330
 
11809
12331
  // Associate audio/video streams to the participant and triggers events on it
11810
12332
  __associateStreams(participant ) {
11811
- const streamId = this._callIdStreamIdMap[participant.callId];
11812
- if (!streamId) {
12333
+ const { trackId } = this._callIdStreamIdMap[participant.callId] || {};
12334
+ if (!trackId) {
11813
12335
  this._unassociatedParticipants[participant.callId] = participant;
11814
12336
 
11815
12337
  return;
11816
12338
  }
11817
- if (!streamId || !participant || !this.localParticipant || participant.callId === this.localParticipant.callId) {
12339
+ if (!trackId || !participant || !this.localParticipant || participant.callId === this.localParticipant.callId) {
11818
12340
  return;
11819
12341
  }
11820
12342
 
11821
- if (this._unassociatedVideoStreams[streamId]) {
12343
+ if (this._unassociatedVideoStreams[trackId]) {
11822
12344
  // Try to associate stream
11823
- this._associateStreamTo(this._unassociatedVideoStreams[streamId], participant);
12345
+ this._associateStreamTo(this._unassociatedVideoStreams[trackId], participant);
11824
12346
 
11825
- delete this._unassociatedVideoStreams[streamId];
12347
+ delete this._unassociatedVideoStreams[trackId];
11826
12348
  delete this._unassociatedParticipants[participant.callId];
11827
12349
  }
11828
12350
  }
@@ -11835,8 +12357,8 @@ class Room extends Emitter {
11835
12357
  participant.onStreamSubscribed(stream);
11836
12358
  }
11837
12359
 
11838
- _getCallIdFromStreamId(streamId ) {
11839
- return Object.keys(this._callIdStreamIdMap).find(key => this._callIdStreamIdMap[key] === streamId);
12360
+ _getCallIdFromTrackId(trackId ) {
12361
+ return Object.keys(this._callIdStreamIdMap).find(key => this._callIdStreamIdMap[key].trackId === trackId);
11840
12362
  }
11841
12363
 
11842
12364
  _getParticipantFromCallId(callId ) {