@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');
@@ -339,7 +338,13 @@ class IssueReporter {
339
338
  }
340
339
 
341
340
  _isLevelAbove(level1 , level2 ) {
342
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
341
+ const index1 = LOG_LEVELS.indexOf(level1);
342
+ const index2 = LOG_LEVELS.indexOf(level2);
343
+ if (index1 === -1 || index2 === -1) {
344
+ return false;
345
+ }
346
+
347
+ return index1 > index2;
343
348
  }
344
349
 
345
350
  _makeCategory(category ) {
@@ -362,7 +367,7 @@ var IssueReporter$1 = new IssueReporter();
362
367
 
363
368
  const methods = ['head', 'get', 'post', 'put', 'delete'];
364
369
 
365
- const logger$a = IssueReporter$1 ? IssueReporter$1.loggerFor('api') : console;
370
+ const logger$b = IssueReporter$1 ? IssueReporter$1.loggerFor('api') : console;
366
371
 
367
372
  // Use a function here to be able to mock it in tests
368
373
  const realFetch = () => {
@@ -397,6 +402,7 @@ class ApiRequester {
397
402
 
398
403
 
399
404
 
405
+
400
406
 
401
407
 
402
408
 
@@ -436,6 +442,8 @@ class ApiRequester {
436
442
  this.token = token;
437
443
  }
438
444
 
445
+ this.shouldLogErrors = true;
446
+
439
447
  methods.forEach(method => {
440
448
  // $FlowFixMe
441
449
  ApiRequester.prototype[method] = function sugar(...args) {
@@ -459,6 +467,14 @@ class ApiRequester {
459
467
  this.fetchOptions = options;
460
468
  }
461
469
 
470
+ disableErrorLogging() {
471
+ this.shouldLogErrors = false;
472
+ }
473
+
474
+ enableErrorLogging() {
475
+ this.shouldLogErrors = true;
476
+ }
477
+
462
478
  async call(
463
479
  path ,
464
480
  method = 'get',
@@ -490,7 +506,7 @@ class ApiRequester {
490
506
  };
491
507
 
492
508
  if (this.refreshTokenPromise) {
493
- logger$a.info('A token is already refreshing, waiting ...', { url });
509
+ logger$b.info('A token is already refreshing, waiting ...', { url });
494
510
  await this.refreshTokenPromise;
495
511
  }
496
512
 
@@ -510,7 +526,7 @@ class ApiRequester {
510
526
  return promise.then(async (err ) => {
511
527
  // Check if the token is still valid
512
528
  if (firstCall && this._checkTokenExpired(response, err)) {
513
- logger$a.warn('token expired', { error: err.reason });
529
+ logger$b.warn('token expired', { error: err.reason });
514
530
  // Replay the call after refreshing the token
515
531
  return this._replayWithNewToken(err, path, method, body, headers, parse);
516
532
  }
@@ -519,7 +535,9 @@ class ApiRequester {
519
535
  ? exceptionClass.fromText(err, response.status)
520
536
  : exceptionClass.fromResponse(err, response.status);
521
537
 
522
- logger$a.error('API error', error);
538
+ if (this.shouldLogErrors) {
539
+ logger$b.error('API error', error);
540
+ }
523
541
 
524
542
  throw error;
525
543
  });
@@ -527,7 +545,9 @@ class ApiRequester {
527
545
 
528
546
  return newParse(response, isJson);
529
547
  }).catch(error => {
530
- logger$a.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
548
+ if (this.shouldLogErrors) {
549
+ logger$b.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
550
+ }
531
551
 
532
552
  throw error;
533
553
  });
@@ -554,13 +574,13 @@ class ApiRequester {
554
574
  ) {
555
575
  const isTokenNotFound = this._isTokenNotFound(err);
556
576
  let newPath = path;
557
- logger$a.info('refreshing token', { inProgress: !!this.refreshTokenPromise });
577
+ logger$b.info('refreshing token', { inProgress: !!this.refreshTokenPromise });
558
578
 
559
579
  this.refreshTokenPromise = this.refreshTokenPromise || this.refreshTokenCallback();
560
580
 
561
581
  return this.refreshTokenPromise.then(() => {
562
582
  this.refreshTokenPromise = null;
563
- logger$a.info('token refreshed', { isTokenNotFound });
583
+ logger$b.info('token refreshed', { isTokenNotFound });
564
584
  if (isTokenNotFound) {
565
585
  const pathParts = path.split('/');
566
586
  pathParts.pop();
@@ -1577,7 +1597,7 @@ class Contact {
1577
1597
 
1578
1598
  return new Contact({
1579
1599
  sourceId: single.id || '',
1580
- name: single.displayName,
1600
+ name: single.displayName || '',
1581
1601
  number: numbers.length ? numbers[0].number : '',
1582
1602
  numbers,
1583
1603
  emails,
@@ -1608,7 +1628,7 @@ class Contact {
1608
1628
 
1609
1629
  return new Contact({
1610
1630
  sourceId: single.id || '',
1611
- name: single.name,
1631
+ name: single.name || '',
1612
1632
  number: numbers.length ? numbers[0].number : '',
1613
1633
  numbers,
1614
1634
  emails,
@@ -1709,7 +1729,7 @@ class Contact {
1709
1729
  } = {}) {
1710
1730
  this.id = id;
1711
1731
  this.uuid = uuid;
1712
- this.name = name;
1732
+ this.name = name || '';
1713
1733
  this.number = number;
1714
1734
  this.numbers = numbers;
1715
1735
  this.email = email;
@@ -3323,7 +3343,7 @@ class CallSession {
3323
3343
  return newFrom(callSession, CallSession);
3324
3344
  }
3325
3345
 
3326
- static parseCall(session , call ) {
3346
+ static parseCall(call ) {
3327
3347
  return new CallSession({
3328
3348
  callId: call.id,
3329
3349
  sipCallId: call.sipCallId,
@@ -3376,17 +3396,66 @@ class Emitter {
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 Emitter {
3399
3468
 
3400
3469
 
3401
3470
 
3402
-
3471
+
3403
3472
 
3404
3473
 
3405
3474
 
@@ -3414,13 +3483,14 @@ class Emitter {
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
 
@@ -10387,10 +10903,7 @@ class Stream {
10387
10903
  element.onloadedmetadata = () => {
10388
10904
  const tracks = this.htmlStream ? this.htmlStream.getVideoTracks() : [];
10389
10905
  tracks.forEach(track => {
10390
- // Do not re-enable the track if already mounted.
10391
- if (!track.loaded) {
10392
- track.enabled = true;
10393
- }
10906
+ track.enabled = true;
10394
10907
  // $FlowFixMe
10395
10908
  track.loaded = true;
10396
10909
  });
@@ -10831,6 +11344,7 @@ var api = {
10831
11344
  check: async (server, session) => {
10832
11345
  const client = new ApiClient({ server });
10833
11346
  client.setToken(session.token);
11347
+ client.disableErrorLogging();
10834
11348
 
10835
11349
  const handleApiError = (apiName, error) => {
10836
11350
  const statusText = error instanceof ServerError ? 'server error' : 'api error';
@@ -10905,6 +11419,8 @@ var aor = {
10905
11419
  check: async (server, session) => {
10906
11420
  const apiClient = new ApiClient({ server });
10907
11421
  apiClient.setToken(session.token);
11422
+ apiClient.disableErrorLogging();
11423
+
10908
11424
  const line = session.primaryWebRtcLine();
10909
11425
  if (!line) {
10910
11426
  return 'No WebRTC line for this user';
@@ -11327,6 +11843,8 @@ const Wazo = {
11327
11843
  ServerError,
11328
11844
  Call,
11329
11845
  CallLog,
11846
+ CTIPhone,
11847
+ Recording,
11330
11848
  ChatMessage,
11331
11849
  ChatRoom,
11332
11850
  Contact,
@@ -11341,6 +11859,7 @@ const Wazo = {
11341
11859
  CallSession,
11342
11860
  IndirectTransfer,
11343
11861
  SwitchboardCall,
11862
+ WebRTCPhone,
11344
11863
  },
11345
11864
 
11346
11865
  // Api
@@ -11775,13 +12294,15 @@ class Room extends Emitter {
11775
12294
  }
11776
12295
  });
11777
12296
 
11778
- this.on(this.ON_VIDEO_STREAM, (stream, streamId) => {
12297
+ this.on(this.ON_VIDEO_STREAM, (stream, streamId, event, sipSession) => {
11779
12298
  logger.info('on room video stream', { streamId });
11780
12299
 
12300
+ this._mapMsid(sipSession.body.body);
12301
+
11781
12302
  // ON_VIDEO_STREAM is called before PARTICIPANT_JOINED, so we have to keep stream in `_unassociatedVideoStreams`.
11782
12303
  this._unassociatedVideoStreams[streamId] = stream;
11783
12304
 
11784
- const callId = this._getCallIdFromStreamId(streamId);
12305
+ const callId = this._getCallIdFromTrackId(streamId);
11785
12306
  const participant = callId ? this._getParticipantFromCallId(callId) : null;
11786
12307
  if (participant) {
11787
12308
  this.__associateStreams(participant);
@@ -11825,11 +12346,12 @@ class Room extends Emitter {
11825
12346
  const sdp = sdpParser__default['default'].parse(rawSdp);
11826
12347
  const labelMsidArray = sdp.media.filter(media => !!media.label).map(({ label, msid }) => ({
11827
12348
  label: String(label),
11828
- msid: msid.split(' ')[1],
12349
+ streamId: msid.split(' ')[0],
12350
+ trackId: msid.split(' ')[1],
11829
12351
  }));
11830
12352
 
11831
- labelMsidArray.forEach(({ label, msid }) => {
11832
- this._callIdStreamIdMap[String(label)] = msid;
12353
+ labelMsidArray.forEach(({ label, streamId, trackId }) => {
12354
+ this._callIdStreamIdMap[String(label)] = { streamId, trackId };
11833
12355
 
11834
12356
  const callId = String(label);
11835
12357
  const participant = this._unassociatedParticipants[callId] || this._getParticipantFromCallId(callId);
@@ -12059,9 +12581,9 @@ class Room extends Emitter {
12059
12581
  _onParticipantTrackUpdate(oldParticipant , update ) {
12060
12582
  const newParticipant = oldParticipant;
12061
12583
 
12062
- const label = this._callIdStreamIdMap[newParticipant.callId];
12584
+ const { trackId } = this._callIdStreamIdMap[newParticipant.callId] || {};
12063
12585
  const pc = Wazo.Phone.phone.currentSipSession.sessionDescriptionHandler.peerConnection;
12064
- const stream = pc.getRemoteStreams().find(otherStream => otherStream.getTracks().some(track => track.id === label));
12586
+ const stream = pc.getRemoteStreams().find(someStream => someStream.getTracks().some(track => track.id === trackId));
12065
12587
 
12066
12588
  if (update === 'downgrade') {
12067
12589
  newParticipant.streams = [];
@@ -12081,21 +12603,21 @@ class Room extends Emitter {
12081
12603
 
12082
12604
  // Associate audio/video streams to the participant and triggers events on it
12083
12605
  __associateStreams(participant ) {
12084
- const streamId = this._callIdStreamIdMap[participant.callId];
12085
- if (!streamId) {
12606
+ const { trackId } = this._callIdStreamIdMap[participant.callId] || {};
12607
+ if (!trackId) {
12086
12608
  this._unassociatedParticipants[participant.callId] = participant;
12087
12609
 
12088
12610
  return;
12089
12611
  }
12090
- if (!streamId || !participant || !this.localParticipant || participant.callId === this.localParticipant.callId) {
12612
+ if (!trackId || !participant || !this.localParticipant || participant.callId === this.localParticipant.callId) {
12091
12613
  return;
12092
12614
  }
12093
12615
 
12094
- if (this._unassociatedVideoStreams[streamId]) {
12616
+ if (this._unassociatedVideoStreams[trackId]) {
12095
12617
  // Try to associate stream
12096
- this._associateStreamTo(this._unassociatedVideoStreams[streamId], participant);
12618
+ this._associateStreamTo(this._unassociatedVideoStreams[trackId], participant);
12097
12619
 
12098
- delete this._unassociatedVideoStreams[streamId];
12620
+ delete this._unassociatedVideoStreams[trackId];
12099
12621
  delete this._unassociatedParticipants[participant.callId];
12100
12622
  }
12101
12623
  }
@@ -12108,8 +12630,8 @@ class Room extends Emitter {
12108
12630
  participant.onStreamSubscribed(stream);
12109
12631
  }
12110
12632
 
12111
- _getCallIdFromStreamId(streamId ) {
12112
- return Object.keys(this._callIdStreamIdMap).find(key => this._callIdStreamIdMap[key] === streamId);
12633
+ _getCallIdFromTrackId(trackId ) {
12634
+ return Object.keys(this._callIdStreamIdMap).find(key => this._callIdStreamIdMap[key].trackId === trackId);
12113
12635
  }
12114
12636
 
12115
12637
  _getParticipantFromCallId(callId ) {