@wazo/sdk 0.37.2 → 0.37.6

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 (99) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +84 -22
  3. package/esm/__tests__/web-rtc-client.test.js +156 -55
  4. package/esm/api/call-logd.js +10 -22
  5. package/esm/api/calld.js +3 -0
  6. package/esm/api/confd.js +71 -0
  7. package/esm/api-client.js +84 -22
  8. package/esm/checker/Checker.js +160 -55
  9. package/esm/checker/checks/aor.js +84 -22
  10. package/esm/checker/checks/api.js +84 -22
  11. package/esm/checker/checks/index.js +160 -55
  12. package/esm/checker/checks/wazo-websocket.js +4 -0
  13. package/esm/checker/checks/webrtc-transport.js +156 -55
  14. package/esm/checker/checks/webrtc.js +156 -55
  15. package/esm/domain/AdHocAPIConference.js +84 -22
  16. package/esm/domain/CallLog.js +10 -22
  17. package/esm/domain/Features.js +84 -22
  18. package/esm/domain/Meeting.js +71 -0
  19. package/esm/domain/Phone/CTIPhone.js +84 -22
  20. package/esm/domain/Phone/WebRTCPhone.js +22 -8
  21. package/esm/domain/__tests__/CallLog.test.js +10 -22
  22. package/esm/index.js +726 -371
  23. package/esm/lib/WazoSessionDescriptionHandler.js +17 -9
  24. package/esm/service/CallApi.js +84 -22
  25. package/esm/service/getApiClient.js +84 -22
  26. package/esm/simple/Auth.js +717 -363
  27. package/esm/simple/Configuration.js +717 -363
  28. package/esm/simple/Directory.js +84 -22
  29. package/esm/simple/Phone.js +709 -355
  30. package/esm/simple/Websocket.js +6 -0
  31. package/esm/simple/index.js +719 -365
  32. package/esm/simple/room/LocalParticipant.js +689 -335
  33. package/esm/simple/room/Participant.js +689 -335
  34. package/esm/simple/room/RemoteParticipant.js +689 -335
  35. package/esm/simple/room/Room.js +689 -335
  36. package/esm/simple/room/SipRoom.js +13001 -0
  37. package/esm/simple/room/Stream.js +719 -365
  38. package/esm/simple/room/__tests__/RemoteParticipant.test.js +4130 -3774
  39. package/esm/simple/utils.js +719 -365
  40. package/esm/web-rtc-client.js +156 -55
  41. package/esm/websocket-client.js +5 -1
  42. package/lib/__tests__/api-client.test.js +84 -22
  43. package/lib/__tests__/web-rtc-client.test.js +156 -55
  44. package/lib/api/call-logd.js +10 -22
  45. package/lib/api/calld.js +3 -0
  46. package/lib/api/confd.js +71 -0
  47. package/lib/api-client.js +84 -22
  48. package/lib/checker/Checker.js +160 -55
  49. package/lib/checker/checks/aor.js +84 -22
  50. package/lib/checker/checks/api.js +84 -22
  51. package/lib/checker/checks/index.js +160 -55
  52. package/lib/checker/checks/wazo-websocket.js +4 -0
  53. package/lib/checker/checks/webrtc-transport.js +156 -55
  54. package/lib/checker/checks/webrtc.js +156 -55
  55. package/lib/domain/AdHocAPIConference.js +84 -22
  56. package/lib/domain/CallLog.js +10 -22
  57. package/lib/domain/Features.js +84 -22
  58. package/lib/domain/Meeting.js +73 -0
  59. package/lib/domain/Phone/CTIPhone.js +84 -22
  60. package/lib/domain/Phone/WebRTCPhone.js +22 -8
  61. package/lib/domain/__tests__/CallLog.test.js +10 -22
  62. package/lib/index.js +726 -371
  63. package/lib/lib/WazoSessionDescriptionHandler.js +17 -9
  64. package/lib/service/CallApi.js +84 -22
  65. package/lib/service/getApiClient.js +84 -22
  66. package/lib/simple/Auth.js +717 -363
  67. package/lib/simple/Configuration.js +717 -363
  68. package/lib/simple/Directory.js +84 -22
  69. package/lib/simple/Phone.js +709 -355
  70. package/lib/simple/Websocket.js +6 -0
  71. package/lib/simple/index.js +719 -365
  72. package/lib/simple/room/LocalParticipant.js +689 -335
  73. package/lib/simple/room/Participant.js +689 -335
  74. package/lib/simple/room/RemoteParticipant.js +689 -335
  75. package/lib/simple/room/Room.js +689 -335
  76. package/lib/simple/room/SipRoom.js +13011 -0
  77. package/lib/simple/room/Stream.js +719 -365
  78. package/lib/simple/room/__tests__/RemoteParticipant.test.js +4131 -3775
  79. package/lib/simple/utils.js +719 -365
  80. package/lib/web-rtc-client.js +156 -55
  81. package/lib/websocket-client.js +6 -0
  82. package/package.json +1 -1
  83. package/src/api/calld.js +3 -0
  84. package/src/api/confd.js +13 -0
  85. package/src/domain/CallLog.js +10 -22
  86. package/src/domain/Meeting.js +59 -0
  87. package/src/domain/Phone/WebRTCPhone.js +22 -8
  88. package/src/index.js +2 -0
  89. package/src/lib/WazoSessionDescriptionHandler.js +17 -9
  90. package/src/simple/Phone.js +13 -4
  91. package/src/simple/index.js +4 -0
  92. package/src/simple/room/Participant.js +7 -0
  93. package/src/simple/room/Room.js +158 -79
  94. package/src/simple/room/SipRoom.js +147 -0
  95. package/src/simple/room/Stream.js +1 -3
  96. package/src/simple/room/__tests__/RemoteParticipant.test.js +3 -2
  97. package/src/types/WebSocketMessage.js +21 -0
  98. package/src/web-rtc-client.js +55 -24
  99. package/src/websocket-client.js +4 -0
@@ -780,7 +780,9 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
780
780
  .then(() => {
781
781
  if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options) && !audioOnly) {
782
782
  // Add a video an empty bundle to be able to replaceTrack when joining a conference without video
783
- this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
783
+ if (this.peerConnection.addTransceiver) {
784
+ this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
785
+ }
784
786
  }
785
787
  })
786
788
  .then(() => this.createLocalOfferOrAnswer(options))
@@ -807,12 +809,6 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
807
809
  };
808
810
  })
809
811
  .then((sessionDescription) => this.applyModifiers(sessionDescription, modifiers))
810
- .then((sessionDescription) => {
811
- // Set new constraints to avoid the constraints check issue in `sdh.getLocalMediaStream` later.
812
- this.localMediaStreamConstraints = options.constraints;
813
-
814
- return sessionDescription;
815
- })
816
812
  .then((sessionDescription) => ({ body: sessionDescription.sdp, contentType: 'application/sdp' }))
817
813
  .catch((error) => {
818
814
  wazoLogger.error('error when creating media', error);
@@ -1007,6 +1003,9 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
1007
1003
 
1008
1004
  // set the transceiver direction to the answer direction
1009
1005
  this._peerConnection.getTransceivers().forEach((transceiver) => {
1006
+ if (transceiver.stopped) {
1007
+ return;
1008
+ }
1010
1009
  if (transceiver.direction /* guarding, but should always be true */) {
1011
1010
  const { receiver } = transceiver;
1012
1011
  if (isConference && audioOnly && receiver.track && receiver.track.kind === 'video') {
@@ -1051,10 +1050,15 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
1051
1050
  if (kind !== 'audio' && kind !== 'video') {
1052
1051
  throw new Error(`Unknown new track kind ${kind}.`);
1053
1052
  }
1054
- const sender = pc.getSenders().find((otherSender) => otherSender.track && otherSender.track.kind === kind);
1053
+ const sender = pc.getSenders && pc.getSenders().find((otherSender) =>
1054
+ otherSender.track && otherSender.track.kind === kind);
1055
1055
 
1056
1056
  // Do not reuse sender video tracks in SFU
1057
1057
  if (sender && (!sfu || newTrack.kind === 'audio')) {
1058
+ if (sender.track) {
1059
+ // eslint-disable-next-line no-param-reassign
1060
+ newTrack.enabled = sender.track.enabled;
1061
+ }
1058
1062
  trackUpdates.push(
1059
1063
  new Promise((resolve) => {
1060
1064
  this.logger.debug(`SessionDescriptionHandler.setLocalMediaStream - replacing sender ${kind} track`);
@@ -1088,7 +1092,11 @@ class WazoSessionDescriptionHandler extends sessionDescriptionHandler.SessionDes
1088
1092
  // Review: could make streamless tracks a configurable option?
1089
1093
  // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTrack#Usage_notes
1090
1094
  try {
1091
- pc.addTrack(newTrack, localStream);
1095
+ if (pc.addTrack) {
1096
+ pc.addTrack(newTrack, localStream);
1097
+ } else {
1098
+ pc.addStream(localStream);
1099
+ }
1092
1100
  } catch (error) {
1093
1101
  this.logger.error(`SessionDescriptionHandler.setLocalMediaStream - failed to add sender ${kind} track`);
1094
1102
  throw error;
@@ -3045,6 +3053,65 @@ class ExternalApp {
3045
3053
  }
3046
3054
  }
3047
3055
 
3056
+ //
3057
+
3058
+
3059
+
3060
+
3061
+
3062
+
3063
+
3064
+
3065
+
3066
+
3067
+
3068
+ class Meeting {
3069
+
3070
+
3071
+
3072
+
3073
+
3074
+
3075
+
3076
+
3077
+
3078
+
3079
+ static parse(plain ) {
3080
+ return new Meeting({
3081
+ guestSipAuthorization: plain.guest_sip_authorization,
3082
+ hostname: plain.hostname,
3083
+ name: plain.name,
3084
+ ownerUuids: plain.owner_uuids,
3085
+ port: plain.port,
3086
+ uuid: plain.uuid,
3087
+ number: plain.number,
3088
+ });
3089
+ }
3090
+
3091
+ static newFrom(meeting ) {
3092
+ return newFrom(meeting, Meeting);
3093
+ }
3094
+
3095
+ constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, hostname, number } = {}) {
3096
+ this.guestSipAuthorization = guestSipAuthorization;
3097
+ this.hostname = hostname;
3098
+ this.name = name;
3099
+ this.ownerUuids = ownerUuids;
3100
+ this.port = port;
3101
+ this.uuid = uuid;
3102
+ this.number = number;
3103
+
3104
+ // Useful to compare instead of instanceof with minified code
3105
+ this.type = 'Meeting';
3106
+ }
3107
+
3108
+ getGuestSipCredentials() {
3109
+ // eslint-disable-next-line no-undef
3110
+ const [username, secret] = atob(this.guestSipAuthorization).split(':');
3111
+ return { username, secret };
3112
+ }
3113
+ }
3114
+
3048
3115
  /* */
3049
3116
 
3050
3117
  var confdMethods = (client , baseUrl ) => ({
@@ -3109,6 +3176,18 @@ var confdMethods = (client , baseUrl ) => ({
3109
3176
  return null;
3110
3177
  }
3111
3178
  },
3179
+
3180
+ createMyMeeting: (name ) =>
3181
+ client.post(`${baseUrl}/users/me/meetings`, { name }).then(Meeting.parse),
3182
+
3183
+ deleteMyMeeting: (meetingUuid ) =>
3184
+ client.delete(`${baseUrl}/users/me/meetings/${meetingUuid}`, null),
3185
+
3186
+ getMeeting: (meetingUuid ) =>
3187
+ client.get(`${baseUrl}/meetings/${meetingUuid}`, null).then(Meeting.parse),
3188
+
3189
+ guestGetMeeting: (meetingUuid ) =>
3190
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}`, null).then(Meeting.parse),
3112
3191
  });
3113
3192
 
3114
3193
  //
@@ -3693,7 +3772,11 @@ class CallLog {
3693
3772
  answered: plain.answered,
3694
3773
  callDirection: plain.call_direction,
3695
3774
  destination: {
3696
- extension: plain.requested_extension || plain.destination_extension,
3775
+ // @TEMP: Temporarily assuming empty numbers are meetings
3776
+ // which is admittedly a very dangerous assumption. Did i mention it was temporary?
3777
+ extension: plain.requested_extension
3778
+ || plain.destination_extension
3779
+ || `meeting-${plain.requested_name || plain.destination_name || ''}`,
3697
3780
  name: plain.requested_name || plain.destination_name || '',
3698
3781
  },
3699
3782
  source: {
@@ -3709,27 +3792,11 @@ class CallLog {
3709
3792
  }
3710
3793
 
3711
3794
  static parseNew(plain , session ) {
3712
- return new CallLog({
3713
- answer: plain.answer ? moment__default['default'](plain.answer).toDate() : null,
3714
- answered: plain.answered,
3715
- callDirection: plain.call_direction,
3716
- destination: {
3717
- extension: plain.requested_extension || plain.destination_extension,
3718
- name: plain.requested_name || plain.destination_name || '',
3719
- },
3720
- source: {
3721
- extension: plain.source_extension,
3722
- name: plain.source_name,
3723
- },
3724
- id: plain.id,
3725
- duration: (plain.duration || 0) * 1000, // duration is in seconds
3726
- start: moment__default['default'](plain.start).toDate(),
3727
- end: plain.end ? moment__default['default'](plain.end).toDate() : null,
3728
- recordings: Recording.parseMany(plain.recordings),
3729
- // @TODO: FIXME add verification declined vs missed call
3730
- newMissedCall: session
3731
- && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
3732
- });
3795
+ const callLog = CallLog.parse(plain);
3796
+ // @TODO: FIXME add verification declined vs missed call
3797
+ callLog.newMissedCall = session
3798
+ && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered;
3799
+ return callLog;
3733
3800
  }
3734
3801
 
3735
3802
  static newFrom(profile ) {
@@ -4486,6 +4553,9 @@ var calldMethods = (client , baseUrl ) => ({
4486
4553
  getConferenceParticipantsAsUser: async (conferenceId ) =>
4487
4554
  client.get(`${baseUrl}/users/me/conferences/${conferenceId}/participants`),
4488
4555
 
4556
+ getMeetingParticipantsAsUser: async (meetingUuid ) =>
4557
+ client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/participants`),
4558
+
4489
4559
  listTrunks: () => client.get(`${baseUrl}/trunks`),
4490
4560
 
4491
4561
  mute: (callId ) =>
@@ -5121,6 +5191,7 @@ const MAX_REGISTER_TRIES = 10;
5121
5191
 
5122
5192
 
5123
5193
 
5194
+
5124
5195
 
5125
5196
 
5126
5197
 
@@ -5147,6 +5218,7 @@ class WebRTCClient extends Emitter {
5147
5218
 
5148
5219
 
5149
5220
 
5221
+
5150
5222
 
5151
5223
  // sugar
5152
5224
 
@@ -5185,6 +5257,8 @@ class WebRTCClient extends Emitter {
5185
5257
  super();
5186
5258
  this.uaConfigOverrides = uaConfigOverrides;
5187
5259
  this.config = config;
5260
+ this.skipRegister = config.skipRegister;
5261
+
5188
5262
  this._buildConfig(config, session).then((newConfig ) => {
5189
5263
  this.config = newConfig;
5190
5264
  this.userAgent = this.createUserAgent(uaConfigOverrides);
@@ -5312,14 +5386,22 @@ class WebRTCClient extends Emitter {
5312
5386
  }
5313
5387
 
5314
5388
  register(tries = 0) {
5315
- logger.info('sdk webrtc registering...', {
5389
+ const logInfo = {
5316
5390
  userAgent: !!this.userAgent,
5317
5391
  registered: this.isRegistered(),
5318
5392
  connectionPromise: !!this.connectionPromise,
5319
5393
  registerer: !!this.registerer,
5320
5394
  waiting: this.registerer && this.registerer.waiting,
5321
5395
  tries,
5322
- });
5396
+ skipRegister: this.skipRegister,
5397
+ };
5398
+
5399
+ if (this.skipRegister) {
5400
+ logger.info('sdk webrtc skip register...', logInfo);
5401
+ return Promise.resolve();
5402
+ }
5403
+
5404
+ logger.info('sdk webrtc registering...', logInfo);
5323
5405
 
5324
5406
  if (!this.userAgent) {
5325
5407
  logger.info('sdk webrtc recreating User Agent');
@@ -5650,12 +5732,18 @@ class WebRTCClient extends Emitter {
5650
5732
  }
5651
5733
 
5652
5734
  if (pc.getSenders) {
5735
+ if (!pc.getSenders().length) {
5736
+ return false;
5737
+ }
5653
5738
  pc.getSenders().forEach(sender => {
5654
5739
  if (sender && sender.track && sender.track.kind === 'audio') {
5655
5740
  muted = muted && !sender.track.enabled;
5656
5741
  }
5657
5742
  });
5658
5743
  } else {
5744
+ if (!pc.getLocalStreams().length) {
5745
+ return false;
5746
+ }
5659
5747
  pc.getLocalStreams().forEach(stream => {
5660
5748
  stream.getAudioTracks().forEach(track => {
5661
5749
  muted = muted && !track.enabled;
@@ -5714,6 +5802,8 @@ class WebRTCClient extends Emitter {
5714
5802
  }
5715
5803
  options.sessionDescriptionHandlerOptions = {
5716
5804
  constraints: options.constraints,
5805
+ hold: true,
5806
+ conference: isConference,
5717
5807
  };
5718
5808
 
5719
5809
  // Send re-INVITE
@@ -5756,6 +5846,8 @@ class WebRTCClient extends Emitter {
5756
5846
  }
5757
5847
  options.sessionDescriptionHandlerOptions = {
5758
5848
  constraints: options.constraints,
5849
+ hold: false,
5850
+ conference: isConference,
5759
5851
  };
5760
5852
 
5761
5853
  // Send re-INVITE
@@ -5939,7 +6031,8 @@ class WebRTCClient extends Emitter {
5939
6031
  }
5940
6032
  const sdh = session.sessionDescriptionHandler;
5941
6033
  const pc = sdh.peerConnection;
5942
- const localStream = this.getLocalStream(this.getSipSessionId(session));
6034
+ const sessionId = this.getSipSessionId(session);
6035
+ const localStream = this.getLocalStream(sessionId);
5943
6036
 
5944
6037
  // Release old video stream
5945
6038
  if (localStream) {
@@ -5965,11 +6058,8 @@ class WebRTCClient extends Emitter {
5965
6058
 
5966
6059
  // let's update the local stream
5967
6060
  this.eventEmitter.emit('onVideoInputChange', stream);
5968
- try {
5969
- await sdh.setLocalMediaStream(stream);
5970
- } catch (_) {
5971
- // Nothing to do
5972
- }
6061
+ this.setLocalMediaStream(sessionId, stream);
6062
+
5973
6063
  return stream;
5974
6064
  });
5975
6065
  }
@@ -6271,9 +6361,11 @@ class WebRTCClient extends Emitter {
6271
6361
  remoteStream.removeTrack(track);
6272
6362
  });
6273
6363
 
6274
- pc.getReceivers().forEach(receiver => {
6275
- remoteStream.addTrack(receiver.track);
6276
- });
6364
+ if (pc.getReceivers) {
6365
+ pc.getReceivers().forEach(receiver => {
6366
+ remoteStream.addTrack(receiver.track);
6367
+ });
6368
+ }
6277
6369
  }
6278
6370
 
6279
6371
  getMediaConfiguration(enableVideo , conference = false, constraints = null) {
@@ -6308,6 +6400,25 @@ class WebRTCClient extends Emitter {
6308
6400
  return sessionId in this.conferences;
6309
6401
  }
6310
6402
 
6403
+ createAudioElementFor(sessionId ) {
6404
+ const audio = document.createElement('audio');
6405
+ audio.setAttribute('id', `audio-${sessionId}`);
6406
+
6407
+ if (audio.setSinkId && this.audioOutputDeviceId) {
6408
+ audio.setSinkId(this.audioOutputDeviceId);
6409
+ }
6410
+
6411
+ if (document.body) {
6412
+ document.body.appendChild(audio);
6413
+ }
6414
+ this.audioElements[sessionId] = audio;
6415
+
6416
+ audio.volume = this.audioOutputVolume;
6417
+ audio.autoplay = true;
6418
+
6419
+ return audio;
6420
+ }
6421
+
6311
6422
  _onTransportError() {
6312
6423
  this.eventEmitter.emit(TRANSPORT_ERROR);
6313
6424
  this.attemptReconnection();
@@ -6537,7 +6648,7 @@ class WebRTCClient extends Emitter {
6537
6648
  let hadRemoteVideo = false;
6538
6649
 
6539
6650
  // Monkey patch `onInviteRequest` to be able to know if there was a remote video stream before `onInvite` is called
6540
- // Becase when `onInvite` is called we already got the video track
6651
+ // Because when `onInvite` is called we already got the video track
6541
6652
  session.onInviteRequest = (request) => {
6542
6653
  hadRemoteVideo = this.hasARemoteVideoTrack(sipSessionId);
6543
6654
 
@@ -6612,20 +6723,10 @@ class WebRTCClient extends Emitter {
6612
6723
  const isConference = this.isConference(sessionId);
6613
6724
 
6614
6725
  if (this._hasAudio() && this._isWeb() && !(sessionId in this.audioElements)) {
6615
- const audio = document.createElement('audio');
6616
- audio.setAttribute('id', `audio-${sessionId}`);
6617
-
6618
- if (audio.setSinkId && this.audioOutputDeviceId) {
6619
- audio.setSinkId(this.audioOutputDeviceId);
6620
- }
6621
-
6622
- if (document.body) {
6623
- document.body.appendChild(audio);
6624
- }
6625
- this.audioElements[sessionId] = audio;
6726
+ this.createAudioElementFor(sessionId);
6626
6727
  }
6627
6728
 
6628
- const audioElement = this.audioElements[this.getSipSessionId(session)];
6729
+ const audioElement = this.audioElements[sessionId];
6629
6730
  const stream = newStream || this.getRemoteStream(sessionId);
6630
6731
 
6631
6732
  if (!this._isWeb() || !stream) {
@@ -475,6 +475,8 @@ const CONFERENCE_USER_PARTICIPANT_JOINED = 'conference_user_participant_joined';
475
475
  const CONFERENCE_USER_PARTICIPANT_LEFT = 'conference_user_participant_left';
476
476
  const CONFERENCE_USER_PARTICIPANT_TALK_STARTED = 'conference_user_participant_talk_started';
477
477
  const CONFERENCE_USER_PARTICIPANT_TALK_STOPPED = 'conference_user_participant_talk_stopped';
478
+ const MEETING_USER_PARTICIPANT_JOINED = 'meeting_user_participant_joined';
479
+ const MEETING_USER_PARTICIPANT_LEFT = 'meeting_user_participant_left';
478
480
  const SWITCHBOARD_QUEUED_CALLS_UPDATED = 'switchboard_queued_calls_updated';
479
481
  const SWITCHBOARD_QUEUED_CALL_ANSWERED = 'switchboard_queued_call_answered';
480
482
  const SWITCHBOARD_HELD_CALLS_UPDATED = 'switchboard_held_calls_updated';
@@ -821,6 +823,8 @@ WebSocketClient.eventLists = [
821
823
  CHATD_USER_ROOM_CREATED,
822
824
  CONFERENCE_USER_PARTICIPANT_JOINED,
823
825
  CONFERENCE_USER_PARTICIPANT_LEFT,
826
+ MEETING_USER_PARTICIPANT_JOINED,
827
+ MEETING_USER_PARTICIPANT_LEFT,
824
828
  CONFERENCE_USER_PARTICIPANT_TALK_STARTED,
825
829
  CONFERENCE_USER_PARTICIPANT_TALK_STOPPED,
826
830
  SWITCHBOARD_QUEUED_CALLS_UPDATED,
@@ -908,6 +912,8 @@ exports.FAX_OUTBOUND_USER_FAILED = FAX_OUTBOUND_USER_FAILED;
908
912
  exports.FAX_OUTBOUND_USER_SUCCEEDED = FAX_OUTBOUND_USER_SUCCEEDED;
909
913
  exports.HEARTBEAT_ENGINE_VERSION = HEARTBEAT_ENGINE_VERSION;
910
914
  exports.LINE_STATUS_UPDATED = LINE_STATUS_UPDATED;
915
+ exports.MEETING_USER_PARTICIPANT_JOINED = MEETING_USER_PARTICIPANT_JOINED;
916
+ exports.MEETING_USER_PARTICIPANT_LEFT = MEETING_USER_PARTICIPANT_LEFT;
911
917
  exports.SOCKET_EVENTS = SOCKET_EVENTS;
912
918
  exports.SWITCHBOARD_HELD_CALLS_UPDATED = SWITCHBOARD_HELD_CALLS_UPDATED;
913
919
  exports.SWITCHBOARD_HELD_CALL_ANSWERED = SWITCHBOARD_HELD_CALL_ANSWERED;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.37.2",
3
+ "version": "0.37.6",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "dist/wazo-sdk.js",
6
6
  "author": "Wazo (http://wazo.io)",
package/src/api/calld.js CHANGED
@@ -133,6 +133,9 @@ export default (client: ApiRequester, baseUrl: string) => ({
133
133
  getConferenceParticipantsAsUser: async (conferenceId: string): Promise<Object> =>
134
134
  client.get(`${baseUrl}/users/me/conferences/${conferenceId}/participants`),
135
135
 
136
+ getMeetingParticipantsAsUser: async (meetingUuid: string): Promise<Object> =>
137
+ client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/participants`),
138
+
136
139
  listTrunks: () => client.get(`${baseUrl}/trunks`),
137
140
 
138
141
  mute: (callId: string) =>
package/src/api/confd.js CHANGED
@@ -4,6 +4,7 @@ import type { UUID, ListConfdUsersResponse, ListApplicationsResponse } from '../
4
4
  import Profile from '../domain/Profile';
5
5
  import SipLine from '../domain/SipLine';
6
6
  import ExternalApp from '../domain/ExternalApp';
7
+ import Meeting from '../domain/Meeting';
7
8
 
8
9
  export default (client: ApiRequester, baseUrl: string) => ({
9
10
  listUsers: (): Promise<ListConfdUsersResponse> => client.get(`${baseUrl}/users`, null),
@@ -67,4 +68,16 @@ export default (client: ApiRequester, baseUrl: string) => ({
67
68
  return null;
68
69
  }
69
70
  },
71
+
72
+ createMyMeeting: (name: string): Promise<Meeting> =>
73
+ client.post(`${baseUrl}/users/me/meetings`, { name }).then(Meeting.parse),
74
+
75
+ deleteMyMeeting: (meetingUuid: string): Promise<Meeting> =>
76
+ client.delete(`${baseUrl}/users/me/meetings/${meetingUuid}`, null),
77
+
78
+ getMeeting: (meetingUuid: string): Promise<Meeting> =>
79
+ client.get(`${baseUrl}/meetings/${meetingUuid}`, null).then(Meeting.parse),
80
+
81
+ guestGetMeeting: (meetingUuid: string): Promise<Meeting> =>
82
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}`, null).then(Meeting.parse),
70
83
  });
@@ -91,7 +91,11 @@ export default class CallLog {
91
91
  answered: plain.answered,
92
92
  callDirection: plain.call_direction,
93
93
  destination: {
94
- extension: plain.requested_extension || plain.destination_extension,
94
+ // @TEMP: Temporarily assuming empty numbers are meetings
95
+ // which is admittedly a very dangerous assumption. Did i mention it was temporary?
96
+ extension: plain.requested_extension
97
+ || plain.destination_extension
98
+ || `meeting-${plain.requested_name || plain.destination_name || ''}`,
95
99
  name: plain.requested_name || plain.destination_name || '',
96
100
  },
97
101
  source: {
@@ -107,27 +111,11 @@ export default class CallLog {
107
111
  }
108
112
 
109
113
  static parseNew(plain: CallLogResponse, session: Session): CallLog {
110
- return new CallLog({
111
- answer: plain.answer ? moment(plain.answer).toDate() : null,
112
- answered: plain.answered,
113
- callDirection: plain.call_direction,
114
- destination: {
115
- extension: plain.requested_extension || plain.destination_extension,
116
- name: plain.requested_name || plain.destination_name || '',
117
- },
118
- source: {
119
- extension: plain.source_extension,
120
- name: plain.source_name,
121
- },
122
- id: plain.id,
123
- duration: (plain.duration || 0) * 1000, // duration is in seconds
124
- start: moment(plain.start).toDate(),
125
- end: plain.end ? moment(plain.end).toDate() : null,
126
- recordings: Recording.parseMany(plain.recordings),
127
- // @TODO: FIXME add verification declined vs missed call
128
- newMissedCall: session
129
- && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
130
- });
114
+ const callLog: CallLog = CallLog.parse(plain);
115
+ // @TODO: FIXME add verification declined vs missed call
116
+ callLog.newMissedCall = session
117
+ && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered;
118
+ return callLog;
131
119
  }
132
120
 
133
121
  static newFrom(profile: CallLog) {
@@ -0,0 +1,59 @@
1
+ // @flow
2
+ import newFrom from '../utils/new-from';
3
+
4
+ export type MeetingCreationResponse = {
5
+ guest_sip_authorization: string,
6
+ hostname: string,
7
+ name: string,
8
+ owner_uuids: Array<string>,
9
+ port: string,
10
+ uuid: string,
11
+ number: string
12
+ }
13
+
14
+ export default class Meeting {
15
+ type: string;
16
+
17
+ guestSipAuthorization: string;
18
+ hostname: string;
19
+ uuid: string;
20
+ name: string;
21
+ port: string;
22
+ number: string;
23
+ ownerUuids: Array<string>;
24
+
25
+ static parse(plain: MeetingCreationResponse): Meeting {
26
+ return new Meeting({
27
+ guestSipAuthorization: plain.guest_sip_authorization,
28
+ hostname: plain.hostname,
29
+ name: plain.name,
30
+ ownerUuids: plain.owner_uuids,
31
+ port: plain.port,
32
+ uuid: plain.uuid,
33
+ number: plain.number,
34
+ });
35
+ }
36
+
37
+ static newFrom(meeting: Meeting) {
38
+ return newFrom(meeting, Meeting);
39
+ }
40
+
41
+ constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, hostname, number }: Object = {}) {
42
+ this.guestSipAuthorization = guestSipAuthorization;
43
+ this.hostname = hostname;
44
+ this.name = name;
45
+ this.ownerUuids = ownerUuids;
46
+ this.port = port;
47
+ this.uuid = uuid;
48
+ this.number = number;
49
+
50
+ // Useful to compare instead of instanceof with minified code
51
+ this.type = 'Meeting';
52
+ }
53
+
54
+ getGuestSipCredentials() {
55
+ // eslint-disable-next-line no-undef
56
+ const [username, secret] = atob(this.guestSipAuthorization).split(':');
57
+ return { username, secret };
58
+ }
59
+ }
@@ -200,7 +200,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
200
200
  });
201
201
 
202
202
  if (!sipSession) {
203
- return null;
203
+ return false;
204
204
  }
205
205
 
206
206
  // $FlowFixMe
@@ -210,19 +210,17 @@ export default class WebRTCPhone extends Emitter implements Phone {
210
210
  // Downgrade
211
211
  if (constraints && !isUpgrade) {
212
212
  // No reinvite needed
213
- return this._downgradeToAudio(callSession);
213
+ this._downgradeToAudio(callSession);
214
+ return false;
214
215
  }
215
216
 
216
217
  if (isUpgrade) {
217
218
  const shouldReinvite = await this._upgradeToVideo(callSession, constraints, conference);
218
219
  if (!shouldReinvite) {
219
- return;
220
+ return false;
220
221
  }
221
222
  }
222
223
 
223
- // Force reinvite in SDH
224
- sipSession.sessionDescriptionHandler.localMediaStreamConstraints = null;
225
-
226
224
  return this.client.reinvite(sipSession, constraints, conference, audioOnly);
227
225
  }
228
226
 
@@ -295,7 +293,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
295
293
  _downgradeToAudio(callSession: ?CallSession) {
296
294
  const sipSession = this.findSipSession(callSession);
297
295
  if (!sipSession) {
298
- return;
296
+ return false;
299
297
  }
300
298
 
301
299
  // Release local video stream when downgrading to audio
@@ -499,6 +497,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
499
497
  localStream.removeTrack(videoTrack);
500
498
  }
501
499
 
500
+ if (localStream) {
501
+ localStream.addTrack(screenTrack);
502
+ }
503
+
502
504
  if (sender) {
503
505
  sender.replaceTrack(screenTrack);
504
506
  }
@@ -514,6 +516,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
514
516
  }
515
517
 
516
518
  logger.info('WebRTC phone - stop screen sharing');
519
+ let reinvited = false;
517
520
 
518
521
  try {
519
522
  if (this.currentScreenShare.stream) {
@@ -528,7 +531,8 @@ export default class WebRTCPhone extends Emitter implements Phone {
528
531
  // When upgrading directly to screenshare (eg: we don't have a videoLocalStream to replace)
529
532
  // We have to downgrade to audio.
530
533
  const screenshareStopped = this.currentScreenShare.sender && this.currentScreenShare.hadVideo;
531
- await this.sendReinvite(targetCallSession, { audio: false, video: screenshareStopped }, conference);
534
+ const constraints = { audio: false, video: screenshareStopped };
535
+ reinvited = await this.sendReinvite(targetCallSession, constraints, conference);
532
536
  }
533
537
  } catch (e) {
534
538
  console.warn(e);
@@ -542,6 +546,8 @@ export default class WebRTCPhone extends Emitter implements Phone {
542
546
  );
543
547
 
544
548
  this.currentScreenShare = null;
549
+
550
+ return reinvited;
545
551
  }
546
552
 
547
553
  async _getStreamFromConstraints(constraints: Object, conference: boolean = false): Promise<?MediaStream> {
@@ -602,6 +608,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
602
608
  this.client.changeAudioOutputDevice(id);
603
609
  }
604
610
 
611
+ createAudioElementFor(sessionId: string) {
612
+ return this.client.createAudioElementFor(sessionId);
613
+ }
614
+
605
615
  changeRingDevice(id: string) {
606
616
  this.audioRingDeviceId = id;
607
617
  }
@@ -1249,6 +1259,10 @@ export default class WebRTCPhone extends Emitter implements Phone {
1249
1259
  return callSession ? this.client.hasLocalVideo(callSession.sipCallId) : null;
1250
1260
  }
1251
1261
 
1262
+ hasALocalVideoTrack(callSession: CallSession) {
1263
+ return callSession ? this.client.hasALocalVideoTrack(callSession.sipCallId) : null;
1264
+ }
1265
+
1252
1266
  getRemoteStream(callSession: CallSession): ?MediaStream {
1253
1267
  return callSession ? this.client.getRemoteStream(callSession.sipCallId) : null;
1254
1268
  }
package/src/index.js CHANGED
@@ -30,6 +30,7 @@ import IndirectTransfer from './domain/IndirectTransfer';
30
30
  import SwitchboardCall from './domain/SwitchboardCall';
31
31
  import WebRTCPhone from './domain/Phone/WebRTCPhone';
32
32
  import CTIPhone from './domain/Phone/CTIPhone';
33
+ import Meeting from './domain/Meeting';
33
34
  import type {
34
35
  NewContact as NewContactType,
35
36
  ContactResponse as ContactResponseType,
@@ -122,4 +123,5 @@ export default {
122
123
  SOCKET_EVENTS,
123
124
  Wazo,
124
125
  WebRTCPhone,
126
+ Meeting,
125
127
  };