@wazo/sdk 0.37.22 → 0.37.26

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 (118) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +37 -6
  3. package/esm/__tests__/web-rtc-client.test.js +78 -68
  4. package/esm/api/call-logd.js +7 -0
  5. package/esm/api/calld.js +10 -0
  6. package/esm/api/chatd.js +7 -0
  7. package/esm/api/confd.js +13 -5
  8. package/esm/api/ctid-ng.js +10 -0
  9. package/esm/api/dird.js +4 -1
  10. package/esm/api-client.js +37 -6
  11. package/esm/checker/Checker.js +78 -68
  12. package/esm/checker/checks/aor.js +37 -6
  13. package/esm/checker/checks/api.js +37 -6
  14. package/esm/checker/checks/index.js +78 -68
  15. package/esm/checker/checks/webrtc-transport.js +78 -68
  16. package/esm/checker/checks/webrtc.js +78 -68
  17. package/esm/domain/AdHocAPIConference.js +37 -6
  18. package/esm/domain/Call.js +3 -0
  19. package/esm/domain/CallLog.js +7 -0
  20. package/esm/domain/ChatMessage.js +4 -0
  21. package/esm/domain/ChatRoom.js +3 -0
  22. package/esm/domain/Contact.js +4 -1
  23. package/esm/domain/Features.js +37 -6
  24. package/esm/domain/Meeting.js +15 -5
  25. package/esm/domain/Phone/CTIPhone.js +41 -6
  26. package/esm/domain/Phone/WebRTCPhone.js +41 -15
  27. package/esm/domain/Recording.js +3 -0
  28. package/esm/domain/Voicemail.js +3 -0
  29. package/esm/domain/__tests__/Call.test.js +3 -0
  30. package/esm/domain/__tests__/CallLog.test.js +7 -0
  31. package/esm/domain/__tests__/Contact.test.js +4 -1
  32. package/esm/domain/__tests__/Meeting.test.js +144 -0
  33. package/esm/domain/__tests__/Voicemail.test.js +3 -0
  34. package/esm/index.js +122 -82
  35. package/esm/lib/WazoSessionDescriptionHandler.js +0 -38
  36. package/esm/service/CallApi.js +37 -6
  37. package/esm/service/getApiClient.js +37 -6
  38. package/esm/simple/Auth.js +122 -82
  39. package/esm/simple/Configuration.js +122 -82
  40. package/esm/simple/Directory.js +37 -6
  41. package/esm/simple/Phone.js +122 -82
  42. package/esm/simple/index.js +122 -82
  43. package/esm/simple/room/LocalParticipant.js +122 -82
  44. package/esm/simple/room/Participant.js +122 -82
  45. package/esm/simple/room/RemoteParticipant.js +122 -82
  46. package/esm/simple/room/Room.js +122 -82
  47. package/esm/simple/room/SipRoom.js +122 -82
  48. package/esm/simple/room/Stream.js +122 -82
  49. package/esm/simple/room/__tests__/RemoteParticipant.test.js +122 -82
  50. package/esm/simple/utils.js +122 -82
  51. package/esm/types/WebSocketMessage.js +1 -1
  52. package/esm/web-rtc-client.js +78 -68
  53. package/lib/__tests__/api-client.test.js +37 -6
  54. package/lib/__tests__/web-rtc-client.test.js +78 -68
  55. package/lib/api/call-logd.js +7 -0
  56. package/lib/api/calld.js +10 -0
  57. package/lib/api/chatd.js +7 -0
  58. package/lib/api/confd.js +13 -5
  59. package/lib/api/ctid-ng.js +10 -0
  60. package/lib/api/dird.js +4 -1
  61. package/lib/api-client.js +37 -6
  62. package/lib/checker/Checker.js +78 -68
  63. package/lib/checker/checks/aor.js +37 -6
  64. package/lib/checker/checks/api.js +37 -6
  65. package/lib/checker/checks/index.js +78 -68
  66. package/lib/checker/checks/webrtc-transport.js +78 -68
  67. package/lib/checker/checks/webrtc.js +78 -68
  68. package/lib/domain/AdHocAPIConference.js +37 -6
  69. package/lib/domain/Call.js +3 -0
  70. package/lib/domain/CallLog.js +7 -0
  71. package/lib/domain/ChatMessage.js +4 -0
  72. package/lib/domain/ChatRoom.js +3 -0
  73. package/lib/domain/Contact.js +4 -1
  74. package/lib/domain/Features.js +37 -6
  75. package/lib/domain/Meeting.js +19 -5
  76. package/lib/domain/Phone/CTIPhone.js +41 -6
  77. package/lib/domain/Phone/WebRTCPhone.js +41 -15
  78. package/lib/domain/Recording.js +3 -0
  79. package/lib/domain/Voicemail.js +3 -0
  80. package/lib/domain/__tests__/Call.test.js +3 -0
  81. package/lib/domain/__tests__/CallLog.test.js +7 -0
  82. package/lib/domain/__tests__/Contact.test.js +4 -1
  83. package/lib/domain/__tests__/Meeting.test.js +150 -0
  84. package/lib/domain/__tests__/Voicemail.test.js +3 -0
  85. package/lib/index.js +122 -82
  86. package/lib/lib/WazoSessionDescriptionHandler.js +0 -38
  87. package/lib/service/CallApi.js +37 -6
  88. package/lib/service/getApiClient.js +37 -6
  89. package/lib/simple/Auth.js +122 -82
  90. package/lib/simple/Configuration.js +122 -82
  91. package/lib/simple/Directory.js +37 -6
  92. package/lib/simple/Phone.js +122 -82
  93. package/lib/simple/index.js +122 -82
  94. package/lib/simple/room/LocalParticipant.js +122 -82
  95. package/lib/simple/room/Participant.js +122 -82
  96. package/lib/simple/room/RemoteParticipant.js +122 -82
  97. package/lib/simple/room/Room.js +122 -82
  98. package/lib/simple/room/SipRoom.js +122 -82
  99. package/lib/simple/room/Stream.js +122 -82
  100. package/lib/simple/room/__tests__/RemoteParticipant.test.js +122 -82
  101. package/lib/simple/utils.js +122 -82
  102. package/lib/types/WebSocketMessage.js +2 -0
  103. package/lib/web-rtc-client.js +78 -68
  104. package/package.json +1 -1
  105. package/src/domain/Call.js +3 -0
  106. package/src/domain/CallLog.js +4 -0
  107. package/src/domain/ChatMessage.js +4 -0
  108. package/src/domain/ChatRoom.js +3 -0
  109. package/src/domain/Contact.js +4 -1
  110. package/src/domain/Meeting.js +15 -5
  111. package/src/domain/Phone/CTIPhone.js +4 -0
  112. package/src/domain/Phone/Phone.js +2 -0
  113. package/src/domain/Phone/WebRTCPhone.js +41 -15
  114. package/src/domain/Recording.js +3 -0
  115. package/src/domain/Voicemail.js +3 -0
  116. package/src/domain/__tests__/Meeting.test.js +50 -0
  117. package/src/lib/WazoSessionDescriptionHandler.js +1 -35
  118. package/src/web-rtc-client.js +41 -24
@@ -735,10 +735,6 @@ const getSrflxOrRelay = (candidates ) =>
735
735
  candidates
736
736
  .filter(candidate => candidate && (candidate.type === 'srflx' || candidate.type === 'relay'));
737
737
 
738
- const areCandidateValid = (candidates ) =>
739
- getSrflxOrRelay(candidates)
740
- .length > 0;
741
-
742
738
  const getVideoDirection = (sdp ) => {
743
739
  const parsedSdp = sdpParser__default['default'].parse(sdp);
744
740
  const videoMedia = parsedSdp.media.find(media => media.type === 'video');
@@ -791,8 +787,6 @@ const fixSdp = (sdp , candidates , forcePort = true)
791
787
  /* global RTCSessionDescriptionInit, navigator */
792
788
 
793
789
  const wazoLogger = IssueReporter$1 ? IssueReporter$1.loggerFor('webrtc-sdh') : console;
794
- const MAX_WAIT_FOR_ICE_TRIES = 20;
795
- const WAIT_FOR_ICE_TIMEOUT = 500;
796
790
 
797
791
  // Customized mediaStreamFactory allowing to send screensharing stream directory when upgrading
798
792
  const wazoMediaStreamFactory = (constraints ) => {
@@ -982,38 +976,6 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
982
976
  this._peerConnection = undefined;
983
977
  }
984
978
 
985
- async _waitForValidGatheredIce() {
986
- let tries = 0;
987
-
988
- while (!areCandidateValid(this.gatheredCandidates) && tries < MAX_WAIT_FOR_ICE_TRIES) {
989
- wazoLogger.trace('SessionDescriptionHandler._waitForValidGatheredIce, waiting for ice', {
990
- tries,
991
- max: MAX_WAIT_FOR_ICE_TRIES,
992
- });
993
-
994
- tries++;
995
-
996
- // eslint-disable-next-line no-await-in-loop
997
- await new Promise(resolve => setTimeout(resolve, WAIT_FOR_ICE_TIMEOUT));
998
- }
999
-
1000
- if (tries >= MAX_WAIT_FOR_ICE_TRIES) {
1001
- const errorMsg = 'No valid candidates found, can\'t answer the call';
1002
- const error = new Error(errorMsg);
1003
- wazoLogger.error(errorMsg, { tries, max: MAX_WAIT_FOR_ICE_TRIES });
1004
-
1005
- // Emit an error, because sip.js catches the exception and switch to status Terminated.
1006
- this.eventEmitter.emit('error', error);
1007
-
1008
- wazoLogger.error('No valid candidates found', { tries, candidates: JSON.stringify(this.gatheredCandidates) });
1009
- }
1010
-
1011
- // eslint-disable-next-line
1012
- wazoLogger.trace('Found valid candidates', { tries, candidates: JSON.stringify(this.gatheredCandidates) });
1013
-
1014
- return this.getLocalSessionDescription();
1015
- }
1016
-
1017
979
  // Overridden to send `inactive` in conference
1018
980
  updateDirection(options , isConference = false,
1019
981
  audioOnly = false) {
@@ -2123,6 +2085,9 @@ class Contact {
2123
2085
  }
2124
2086
 
2125
2087
  static parseMany(response ) {
2088
+ if (!response || !response.results) {
2089
+ return [];
2090
+ }
2126
2091
  return response.results.map(r => Contact.parse(r, response.column_types));
2127
2092
  }
2128
2093
 
@@ -2493,7 +2458,7 @@ class Contact {
2493
2458
  this[key] = old[key] || this[key];
2494
2459
  });
2495
2460
 
2496
- if (old.lineState && !this.lineState) {
2461
+ if (old.lineState) {
2497
2462
  this.lineState = old.lineState;
2498
2463
  }
2499
2464
 
@@ -3200,8 +3165,9 @@ class ExternalApp {
3200
3165
 
3201
3166
 
3202
3167
 
3203
-
3168
+
3204
3169
 
3170
+
3205
3171
 
3206
3172
 
3207
3173
  class Meeting {
@@ -3212,9 +3178,10 @@ class Meeting {
3212
3178
 
3213
3179
 
3214
3180
 
3215
-
3181
+
3216
3182
 
3217
3183
 
3184
+
3218
3185
 
3219
3186
  static parse(plain ) {
3220
3187
  return new Meeting({
@@ -3224,12 +3191,16 @@ class Meeting {
3224
3191
  ownerUuids: plain.owner_uuids,
3225
3192
  port: plain.port,
3226
3193
  uuid: plain.uuid,
3227
- number: plain.number,
3194
+ extension: plain.exten,
3228
3195
  persistent: plain.persistent,
3196
+ creationTime: moment__default['default'](plain.creation_time).toDate(),
3229
3197
  });
3230
3198
  }
3231
3199
 
3232
3200
  static parseMany(items ) {
3201
+ if (!items) {
3202
+ return [];
3203
+ }
3233
3204
  return items.map(plain => Meeting.parse(plain));
3234
3205
  }
3235
3206
 
@@ -3244,8 +3215,9 @@ class Meeting {
3244
3215
  ownerUuids,
3245
3216
  port,
3246
3217
  uri,
3247
- number,
3218
+ extension,
3248
3219
  persistent,
3220
+ creationTime,
3249
3221
  } = {}) {
3250
3222
  this.guestSipAuthorization = guestSipAuthorization;
3251
3223
  this.uri = uri;
@@ -3253,8 +3225,9 @@ class Meeting {
3253
3225
  this.ownerUuids = ownerUuids;
3254
3226
  this.port = port;
3255
3227
  this.uuid = uuid;
3256
- this.number = number;
3228
+ this.extension = extension;
3257
3229
  this.persistent = persistent;
3230
+ this.creationTime = creationTime;
3258
3231
 
3259
3232
  // Useful to compare instead of instanceof with minified code
3260
3233
  this.type = 'Meeting';
@@ -3432,6 +3405,10 @@ class ChatMessage {
3432
3405
 
3433
3406
 
3434
3407
  static parseMany(plain ) {
3408
+ if (!plain || !plain.items) {
3409
+ return [];
3410
+ }
3411
+
3435
3412
  return plain.items.map(item => ChatMessage.parse(item));
3436
3413
  }
3437
3414
 
@@ -3541,6 +3518,9 @@ class Voicemail {
3541
3518
  }
3542
3519
 
3543
3520
  static parseMany(plain ) {
3521
+ if (!plain) {
3522
+ return [];
3523
+ }
3544
3524
  const plainUnread = plain.folders.filter(folder => folder.type === 'new')[0].messages;
3545
3525
  const plainRead = plain.folders.filter(folder => folder.type === 'old')[0].messages;
3546
3526
 
@@ -3650,6 +3630,9 @@ class Call {
3650
3630
 
3651
3631
 
3652
3632
  static parseMany(plain ) {
3633
+ if (!plain) {
3634
+ return [];
3635
+ }
3653
3636
  return plain.map((plainCall ) => Call.parse(plainCall));
3654
3637
  }
3655
3638
 
@@ -3822,6 +3805,9 @@ class Recording {
3822
3805
 
3823
3806
 
3824
3807
  static parseMany(recordings = []) {
3808
+ if (!recordings) {
3809
+ return [];
3810
+ }
3825
3811
  return recordings.map(item => Recording.parse(item));
3826
3812
  }
3827
3813
 
@@ -3923,6 +3909,10 @@ class CallLog {
3923
3909
  }
3924
3910
 
3925
3911
  static parseMany(plain ) {
3912
+ if (!plain || !plain.items) {
3913
+ return [];
3914
+ }
3915
+
3926
3916
  return plain.items.map(item => CallLog.parse(item));
3927
3917
  }
3928
3918
 
@@ -4495,6 +4485,9 @@ class ChatRoom {
4495
4485
 
4496
4486
 
4497
4487
  static parseMany(plain ) {
4488
+ if (!plain || !plain.items) {
4489
+ return [];
4490
+ }
4498
4491
  return plain.items.map(item => ChatRoom.parse(item));
4499
4492
  }
4500
4493
 
@@ -5726,7 +5719,7 @@ class WebRTCClient extends Emitter {
5726
5719
  }
5727
5720
 
5728
5721
  call(number , enableVideo , audioOnly = false, conference = false) {
5729
- logger.info('sdk webrtc creating call', { number, enableVideo, audioOnly });
5722
+ logger.info('sdk webrtc creating call', { number, enableVideo, audioOnly, conference });
5730
5723
  const inviterOptions = {
5731
5724
  sessionDescriptionHandlerOptionsReInvite: {
5732
5725
  conference,
@@ -6802,6 +6795,7 @@ class WebRTCClient extends Emitter {
6802
6795
  const screenSharing = constraints && 'screen' in constraints ? constraints.screen : false;
6803
6796
  const isDesktop = constraints && 'desktop' in constraints ? constraints.desktop : false;
6804
6797
  const withAudio = constraints && 'audio' in constraints ? constraints.audio : true;
6798
+ const mandatoryVideo = constraints && typeof constraints.video === 'object' ? constraints.video.mandatory : {};
6805
6799
 
6806
6800
  logger.info('Retrieving media a configuration', { enableVideo, screenSharing, isDesktop, withAudio, constraints });
6807
6801
 
@@ -6809,7 +6803,8 @@ class WebRTCClient extends Emitter {
6809
6803
  constraints: {
6810
6804
  // Exact constraint are not supported with `getDisplayMedia` and we must have a video=false in desktop screenshare
6811
6805
  audio: screenSharing ? !isDesktop : (withAudio ? this._getAudioConstraints() : false),
6812
- video: screenSharing ? (isDesktop ? ({ mandatory: { chromeMediaSource: 'desktop' } }) : { cursor: 'always' })
6806
+ video: screenSharing ? (isDesktop
6807
+ ? ({ mandatory: { chromeMediaSource: 'desktop', ...(mandatoryVideo || {}) } }) : { cursor: 'always' })
6813
6808
  : this._getVideoConstraints(enableVideo),
6814
6809
  screen: screenSharing,
6815
6810
  desktop: isDesktop,
@@ -7144,12 +7139,14 @@ class WebRTCClient extends Emitter {
7144
7139
 
7145
7140
  session.sessionDescriptionHandler.remoteMediaStream.onaddtrack = onTrack;
7146
7141
 
7147
- pc.oniceconnectionstatechange = () => {
7148
- logger.info('on ice connection state changed', { state: pc.iceConnectionState });
7149
- if (pc.iceConnectionState === 'disconnected') {
7150
- this.eventEmitter.emit(ON_DISCONNECTED);
7151
- }
7152
- };
7142
+ if (pc) {
7143
+ pc.oniceconnectionstatechange = () => {
7144
+ logger.info('on ice connection state changed', { state: pc.iceConnectionState });
7145
+ if (pc.iceConnectionState === 'disconnected') {
7146
+ this.eventEmitter.emit(ON_DISCONNECTED);
7147
+ }
7148
+ };
7149
+ }
7153
7150
 
7154
7151
  if (withEvent) {
7155
7152
  this.eventEmitter.emit(ACCEPTED, session, sessionDialog);
@@ -7359,13 +7356,17 @@ class WebRTCClient extends Emitter {
7359
7356
  const nbStats = this.sessionNetworkStats[sessionId].length;
7360
7357
  const lastNetworkStats = this.sessionNetworkStats[sessionId][nbStats - 1];
7361
7358
  const lastAudioSent = lastNetworkStats ? lastNetworkStats.audioBytesSent : 0;
7359
+ const lastAudioContentSent = lastNetworkStats ? lastNetworkStats.audioContentSent : 0;
7362
7360
  const lastVideoSent = lastNetworkStats ? lastNetworkStats.videoBytesSent : 0;
7363
7361
  const lastAudioReceived = lastNetworkStats ? lastNetworkStats.audioBytesReceived : 0;
7362
+ const lastAudioContentReceived = lastNetworkStats ? lastNetworkStats.audioContentReceived : 0;
7364
7363
  const lastVideoReceived = lastNetworkStats ? lastNetworkStats.videoBytesReceived : 0;
7365
7364
  const lastTransportSent = lastNetworkStats ? lastNetworkStats.transportSent : 0;
7366
7365
  const lastTransportReceived = lastNetworkStats ? lastNetworkStats.transportReceived : 0;
7367
- let audioBytesSent = 0;
7368
- let audioBytesReceived = 0;
7366
+ let audioBytesSent = 0; // content + header
7367
+ let audioBytesReceived = 0; // content + header
7368
+ let audioContentSent = 0; // useful to detect blank call
7369
+ let audioContentReceived = 0;
7369
7370
  let videoBytesSent = 0;
7370
7371
  let videoBytesReceived = 0;
7371
7372
  let transportSent = 0;
@@ -7374,25 +7375,27 @@ class WebRTCClient extends Emitter {
7374
7375
 
7375
7376
  stats.forEach(report => {
7376
7377
  if (report.type === 'outbound-rtp' && report.kind === 'audio') {
7377
- audioBytesSent += report.bytesSent + report.headerBytesSent;
7378
+ audioBytesSent += Number(report.bytesSent) + Number(report.headerBytesSent);
7379
+ audioContentSent += Number(report.bytesSent);
7378
7380
  }
7379
7381
  if (report.type === 'remote-outbound-rtp' && report.kind === 'audio') {
7380
- audioBytesSent += report.bytesSent;
7382
+ audioBytesSent += Number(report.bytesSent);
7381
7383
  }
7382
7384
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
7383
- videoBytesSent += report.bytesSent + report.headerBytesSent;
7385
+ videoBytesSent += Number(report.bytesSent) + Number(report.headerBytesSent);
7384
7386
  }
7385
7387
  if (report.type === 'inbound-rtp' && report.kind === 'audio') {
7386
- packetsLost += report.packetsLost;
7387
- networkStats.packetsReceived = report.packetsReceived;
7388
- audioBytesReceived += report.bytesReceived + report.headerBytesReceived;
7388
+ packetsLost += Number(report.packetsLost);
7389
+ networkStats.packetsReceived = Number(report.packetsReceived);
7390
+ audioBytesReceived += Number(report.bytesReceived) + Number(report.headerBytesReceived);
7391
+ audioContentReceived += Number(report.bytesReceived);
7389
7392
  }
7390
7393
  if (report.type === 'inbound-rtp' && report.kind === 'video') {
7391
- videoBytesReceived += report.bytesReceived + report.headerBytesReceived;
7394
+ videoBytesReceived += Number(report.bytesReceived) + Number(report.headerBytesReceived);
7392
7395
  }
7393
7396
  if (report.type === 'outbound-rtp' && report.kind === 'video') {
7394
7397
  if ('framesPerSecond' in report) {
7395
- networkStats.framesPerSecond = report.framesPerSecond;
7398
+ networkStats.framesPerSecond = Number(report.framesPerSecond);
7396
7399
  }
7397
7400
  if ('framerateMean' in report) {
7398
7401
  // framerateMean is only available in FF
@@ -7400,24 +7403,31 @@ class WebRTCClient extends Emitter {
7400
7403
  }
7401
7404
  }
7402
7405
  if (report.type === 'remote-inbound-rtp' && report.kind === 'audio') {
7403
- packetsLost += report.packetsLost;
7404
- networkStats.roundTripTime = report.roundTripTime;
7405
- networkStats.jitter = report.jitter;
7406
+ packetsLost += Number(report.packetsLost);
7407
+ networkStats.roundTripTime = Number(report.roundTripTime);
7408
+ networkStats.jitter = Number(report.jitter);
7406
7409
  }
7407
7410
  if (report.type === 'remote-inbound-rtp' && report.kind === 'video') {
7408
- packetsLost += report.packetsLost;
7411
+ packetsLost += Number(report.packetsLost);
7409
7412
  }
7410
7413
  if (report.type === 'transport') {
7411
- transportSent += report.bytesSent;
7412
- transportReceived += report.bytesReceived;
7414
+ transportSent += Number(report.bytesSent);
7415
+ transportReceived += Number(report.bytesReceived);
7413
7416
  }
7414
7417
  });
7415
7418
 
7416
7419
  networkStats.packetsLost = packetsLost;
7420
+
7417
7421
  networkStats.totalAudioBytesSent = audioBytesSent;
7418
7422
  networkStats.audioBytesSent = audioBytesSent - lastAudioSent;
7419
7423
  networkStats.totalAudioBytesReceived = audioBytesReceived - lastAudioReceived;
7420
7424
  networkStats.audioBytesReceived = audioBytesReceived;
7425
+
7426
+ networkStats.totalAudioContentSent = audioContentSent;
7427
+ networkStats.audioContentSent = audioContentSent - lastAudioContentSent;
7428
+ networkStats.totalAudioBytesReceived = audioContentReceived - lastAudioContentReceived;
7429
+ networkStats.audioContentReceived = audioContentReceived;
7430
+
7421
7431
  networkStats.totalVideoBytesSent = videoBytesSent;
7422
7432
  networkStats.videoBytesSent = videoBytesSent - lastVideoSent;
7423
7433
  networkStats.totalVideoBytesReceived = videoBytesReceived;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.37.22",
3
+ "version": "0.37.26",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "dist/wazo-sdk.js",
6
6
  "author": "Wazo (http://wazo.io)",
@@ -61,6 +61,9 @@ export default class Call {
61
61
  recording: boolean;
62
62
 
63
63
  static parseMany(plain: Array<CallResponse>): Array<Call> {
64
+ if (!plain) {
65
+ return [];
66
+ }
64
67
  return plain.map((plainCall: CallResponse) => Call.parse(plainCall));
65
68
  }
66
69
 
@@ -84,6 +84,10 @@ export default class CallLog {
84
84
  }
85
85
 
86
86
  static parseMany(plain: Response): Array<CallLog> {
87
+ if (!plain || !plain.items) {
88
+ return [];
89
+ }
90
+
87
91
  return plain.items.map(item => CallLog.parse(item));
88
92
  }
89
93
 
@@ -42,6 +42,10 @@ export default class ChatMessage {
42
42
  read: boolean;
43
43
 
44
44
  static parseMany(plain: ChatMessageListResponse): Array<ChatMessage> {
45
+ if (!plain || !plain.items) {
46
+ return [];
47
+ }
48
+
45
49
  return plain.items.map(item => ChatMessage.parse(item));
46
50
  }
47
51
 
@@ -22,6 +22,9 @@ export default class ChatRoom {
22
22
  users: Array<ChatUser>;
23
23
 
24
24
  static parseMany(plain: RootListResponse): Array<ChatRoom> {
25
+ if (!plain || !plain.items) {
26
+ return [];
27
+ }
25
28
  return plain.items.map(item => ChatRoom.parse(item));
26
29
  }
27
30
 
@@ -281,6 +281,9 @@ export default class Contact {
281
281
  }
282
282
 
283
283
  static parseMany(response: ContactsResponse): Array<Contact> {
284
+ if (!response || !response.results) {
285
+ return [];
286
+ }
284
287
  return response.results.map(r => Contact.parse(r, response.column_types));
285
288
  }
286
289
 
@@ -651,7 +654,7 @@ export default class Contact {
651
654
  this[key] = old[key] || this[key];
652
655
  });
653
656
 
654
- if (old.lineState && !this.lineState) {
657
+ if (old.lineState) {
655
658
  this.lineState = old.lineState;
656
659
  }
657
660
 
@@ -1,4 +1,6 @@
1
1
  // @flow
2
+ import moment from 'moment';
3
+
2
4
  import newFrom from '../utils/new-from';
3
5
 
4
6
  export type MeetingCreationResponse = {
@@ -8,8 +10,9 @@ export type MeetingCreationResponse = {
8
10
  owner_uuids: Array<string>,
9
11
  port: string,
10
12
  uuid: string,
11
- number: string,
13
+ exten: string,
12
14
  persistent: boolean,
15
+ creation_time: string,
13
16
  }
14
17
 
15
18
  export default class Meeting {
@@ -20,9 +23,10 @@ export default class Meeting {
20
23
  uuid: string;
21
24
  name: string;
22
25
  port: string;
23
- number: string;
26
+ extension: string;
24
27
  persistent: boolean;
25
28
  ownerUuids: Array<string>;
29
+ creationTime: Date;
26
30
 
27
31
  static parse(plain: MeetingCreationResponse): Meeting {
28
32
  return new Meeting({
@@ -32,12 +36,16 @@ export default class Meeting {
32
36
  ownerUuids: plain.owner_uuids,
33
37
  port: plain.port,
34
38
  uuid: plain.uuid,
35
- number: plain.number,
39
+ extension: plain.exten,
36
40
  persistent: plain.persistent,
41
+ creationTime: moment(plain.creation_time).toDate(),
37
42
  });
38
43
  }
39
44
 
40
45
  static parseMany(items: MeetingCreationResponse[]): Array<Meeting> {
46
+ if (!items) {
47
+ return [];
48
+ }
41
49
  return items.map(plain => Meeting.parse(plain));
42
50
  }
43
51
 
@@ -52,8 +60,9 @@ export default class Meeting {
52
60
  ownerUuids,
53
61
  port,
54
62
  uri,
55
- number,
63
+ extension,
56
64
  persistent,
65
+ creationTime,
57
66
  }: Object = {}) {
58
67
  this.guestSipAuthorization = guestSipAuthorization;
59
68
  this.uri = uri;
@@ -61,8 +70,9 @@ export default class Meeting {
61
70
  this.ownerUuids = ownerUuids;
62
71
  this.port = port;
63
72
  this.uuid = uuid;
64
- this.number = number;
73
+ this.extension = extension;
65
74
  this.persistent = persistent;
75
+ this.creationTime = creationTime;
66
76
 
67
77
  // Useful to compare instead of instanceof with minified code
68
78
  this.type = 'Meeting';
@@ -280,6 +280,10 @@ export default class CTIPhone extends Emitter implements Phone {
280
280
  return false;
281
281
  }
282
282
 
283
+ hasAVideoTrack(): boolean {
284
+ return false;
285
+ }
286
+
283
287
  getLocalStreamForCall(): ?MediaStream {
284
288
  return null;
285
289
  }
@@ -80,6 +80,8 @@ export interface Phone {
80
80
 
81
81
  hasVideo(callSession: CallSession): boolean;
82
82
 
83
+ hasAVideoTrack(callSession: CallSession): boolean;
84
+
83
85
  isWebRTC(): boolean;
84
86
 
85
87
  getUserAgent(): string;
@@ -203,6 +203,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
203
203
  constraints,
204
204
  audioOnly,
205
205
  conference,
206
+ iceRestart,
206
207
  });
207
208
 
208
209
  if (!sipSession) {
@@ -306,6 +307,11 @@ export default class WebRTCPhone extends Emitter implements Phone {
306
307
 
307
308
  this.client.downgradeToAudio(sipSession);
308
309
 
310
+ if (callSession) {
311
+ callSession.cameraEnabled = false;
312
+ this._updateCallSession(callSession);
313
+ }
314
+
309
315
  if (withMessage) {
310
316
  this._sendReinviteMessage(callSession, false);
311
317
  }
@@ -333,6 +339,11 @@ export default class WebRTCPhone extends Emitter implements Phone {
333
339
 
334
340
  const newStream = await this.client.upgradeToVideo(sipSession, constraints, isConference);
335
341
 
342
+ if (callSession) {
343
+ callSession.cameraEnabled = true;
344
+ this._updateCallSession(callSession);
345
+ }
346
+
336
347
  // If no stream is returned, it means we have to reinvite
337
348
  if (!newStream) {
338
349
  return true;
@@ -571,18 +582,20 @@ export default class WebRTCPhone extends Emitter implements Phone {
571
582
  screenTrack,
572
583
  });
573
584
 
574
- screenTrack.onended = () => {
575
- logger.info('WebRTC phone - on screenshare ended', {
576
- hadVideo,
577
- id: this.getSipSessionId(sipSession),
578
- screenTrack,
579
- });
580
-
581
- this.eventEmitter.emit(
582
- ON_SHARE_SCREEN_ENDING,
583
- this._createCallSession(sipSession, callSession),
584
- );
585
- };
585
+ if (screenTrack) {
586
+ screenTrack.onended = () => {
587
+ logger.info('WebRTC phone - on screenshare ended', {
588
+ hadVideo,
589
+ id: this.getSipSessionId(sipSession),
590
+ screenTrack,
591
+ });
592
+
593
+ this.eventEmitter.emit(
594
+ ON_SHARE_SCREEN_ENDING,
595
+ this._createCallSession(sipSession, callSession),
596
+ );
597
+ };
598
+ }
586
599
 
587
600
  this.currentScreenShare = {
588
601
  stream: screenStream,
@@ -602,7 +615,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
602
615
  }
603
616
 
604
617
  _onCallAccepted(sipSession: Session, cameraEnabled: boolean): CallSession {
605
- logger.info('WebRTC phone - on call accepted', { sipId: sipSession.id, cameraEnabled });
618
+ logger.info('WebRTC phone - on call accepted', { sipId: sipSession.id, cameraEnabled });
606
619
 
607
620
  const callSession = this._createAcceptedCallSession(sipSession, cameraEnabled);
608
621
  this.currentSipSession = sipSession;
@@ -1114,7 +1127,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1114
1127
  // @TODO: line is not used here
1115
1128
  async makeCall(number: string, line: any, cameraEnabled?: boolean,
1116
1129
  audioOnly: boolean = false, conference: boolean = false): Promise<?CallSession> {
1117
- logger.info('make WebRTC call', { number, lineId: line ? line.id : null, cameraEnabled });
1130
+ logger.info('make WebRTC call', { number, lineId: line ? line.id : null, cameraEnabled, audioOnly, conference });
1118
1131
  if (!number) {
1119
1132
  return Promise.resolve(null);
1120
1133
  }
@@ -1475,9 +1488,13 @@ export default class WebRTCPhone extends Emitter implements Phone {
1475
1488
  if (this.shouldSendReinvite && this.currentSipSession
1476
1489
  && this.currentSipSession.state === SessionState.Established) {
1477
1490
  this.shouldSendReinvite = false;
1491
+ const pc = this.currentSipSession.sessionDescriptionHandler.peerConnection;
1492
+ const isConference = pc ? pc.sfu : false;
1493
+ const hasVideo = this.currentCallSession && this.currentCallSession.cameraEnabled;
1494
+
1478
1495
  try {
1479
1496
  // Send reinvite with iceRestart
1480
- this.sendReinvite(this.currentCallSession, null, false, false, true);
1497
+ this.sendReinvite(this.currentCallSession, null, isConference, !hasVideo, true);
1481
1498
  } catch (e) {
1482
1499
  logger.error('WebRTC reinvite after register, error', { message: e.message, stack: e.stack });
1483
1500
  }
@@ -1561,6 +1578,15 @@ export default class WebRTCPhone extends Emitter implements Phone {
1561
1578
  return this.client.getSipSessionId(sipSession);
1562
1579
  }
1563
1580
 
1581
+ _updateCallSession(callSession: CallSession) {
1582
+ const sipSession = this.findSipSession(callSession);
1583
+ const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
1584
+
1585
+ if (sipSessionId) {
1586
+ this.callSessions[sipSessionId] = callSession;
1587
+ }
1588
+ }
1589
+
1564
1590
  _onMessage(message: Message) {
1565
1591
  if (!message || message.method !== 'MESSAGE') {
1566
1592
  return;
@@ -25,6 +25,9 @@ export default class Recording {
25
25
  uuid: string;
26
26
 
27
27
  static parseMany(recordings: RecordingResponse[] = []): Recording[] {
28
+ if (!recordings) {
29
+ return [];
30
+ }
28
31
  return recordings.map(item => Recording.parse(item));
29
32
  }
30
33
 
@@ -61,6 +61,9 @@ export default class Voicemail {
61
61
  }
62
62
 
63
63
  static parseMany(plain: Response): Array<Voicemail> {
64
+ if (!plain) {
65
+ return [];
66
+ }
64
67
  const plainUnread = plain.folders.filter(folder => folder.type === 'new')[0].messages;
65
68
  const plainRead = plain.folders.filter(folder => folder.type === 'old')[0].messages;
66
69