@wazo/sdk 0.37.0 → 0.37.4

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 (144) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +91 -23
  3. package/esm/__tests__/web-rtc-client.test.js +165 -56
  4. package/esm/api/agentd.js +7 -1
  5. package/esm/api/amid.js +7 -1
  6. package/esm/api/application.js +7 -1
  7. package/esm/api/auth.js +7 -1
  8. package/esm/api/call-logd.js +17 -23
  9. package/esm/api/calld.js +10 -18
  10. package/esm/api/chatd.js +7 -1
  11. package/esm/api/confd.js +78 -1
  12. package/esm/api/ctid-ng.js +7 -1
  13. package/esm/api/dird.js +7 -1
  14. package/esm/api/webhookd.js +7 -1
  15. package/esm/api-client.js +91 -23
  16. package/esm/checker/Checker.js +169 -56
  17. package/esm/checker/checks/aor.js +91 -23
  18. package/esm/checker/checks/api.js +91 -23
  19. package/esm/checker/checks/index.js +169 -56
  20. package/esm/checker/checks/wazo-websocket.js +11 -1
  21. package/esm/checker/checks/webrtc-transport.js +165 -56
  22. package/esm/checker/checks/webrtc.js +165 -56
  23. package/esm/domain/AdHocAPIConference.js +1722 -1654
  24. package/esm/domain/CallLog.js +10 -22
  25. package/esm/domain/CallSession.js +1 -18
  26. package/esm/domain/Features.js +91 -23
  27. package/esm/domain/IndirectTransfer.js +0 -17
  28. package/esm/domain/Meeting.js +71 -0
  29. package/esm/domain/Phone/CTIPhone.js +98 -26
  30. package/esm/domain/Phone/Phone.js +0 -17
  31. package/esm/domain/Phone/WebRTCPhone.js +47 -27
  32. package/esm/domain/Room.js +0 -17
  33. package/esm/domain/SwitchboardCall.js +0 -17
  34. package/esm/domain/__tests__/CallLog.test.js +10 -22
  35. package/esm/domain/__tests__/CallSession.test.js +1 -18
  36. package/esm/domain/__tests__/IndirectTransfer.test.js +1 -18
  37. package/esm/domain/__tests__/Room.test.js +1 -18
  38. package/esm/index.js +3880 -3111
  39. package/esm/lib/WazoSessionDescriptionHandler.js +24 -10
  40. package/esm/service/CallApi.js +91 -23
  41. package/esm/service/IssueReporter.js +7 -1
  42. package/esm/service/__tests__/IssueReporter.test.js +9 -1
  43. package/esm/service/getApiClient.js +91 -23
  44. package/esm/simple/Auth.js +1126 -360
  45. package/esm/simple/Configuration.js +1126 -360
  46. package/esm/simple/Directory.js +91 -23
  47. package/esm/simple/Phone.js +3724 -2958
  48. package/esm/simple/Websocket.js +13 -1
  49. package/esm/simple/index.js +1128 -362
  50. package/esm/simple/room/LocalParticipant.js +1097 -331
  51. package/esm/simple/room/Participant.js +1097 -331
  52. package/esm/simple/room/RemoteParticipant.js +1097 -331
  53. package/esm/simple/room/Room.js +1097 -331
  54. package/esm/simple/room/SipRoom.js +12996 -0
  55. package/esm/simple/room/Stream.js +1128 -362
  56. package/esm/simple/room/__tests__/RemoteParticipant.test.js +5157 -4389
  57. package/esm/simple/utils.js +1128 -362
  58. package/esm/utils/__tests__/api-requester.test.js +7 -1
  59. package/esm/utils/api-requester.js +7 -1
  60. package/esm/web-rtc-client.js +165 -56
  61. package/esm/websocket-client.js +12 -2
  62. package/lib/__tests__/api-client.test.js +91 -23
  63. package/lib/__tests__/web-rtc-client.test.js +165 -56
  64. package/lib/api/agentd.js +7 -1
  65. package/lib/api/amid.js +7 -1
  66. package/lib/api/application.js +7 -1
  67. package/lib/api/auth.js +7 -1
  68. package/lib/api/call-logd.js +17 -23
  69. package/lib/api/calld.js +10 -18
  70. package/lib/api/chatd.js +7 -1
  71. package/lib/api/confd.js +78 -1
  72. package/lib/api/ctid-ng.js +7 -1
  73. package/lib/api/dird.js +7 -1
  74. package/lib/api/webhookd.js +7 -1
  75. package/lib/api-client.js +91 -23
  76. package/lib/checker/Checker.js +169 -56
  77. package/lib/checker/checks/aor.js +91 -23
  78. package/lib/checker/checks/api.js +91 -23
  79. package/lib/checker/checks/index.js +169 -56
  80. package/lib/checker/checks/wazo-websocket.js +11 -1
  81. package/lib/checker/checks/webrtc-transport.js +165 -56
  82. package/lib/checker/checks/webrtc.js +165 -56
  83. package/lib/domain/AdHocAPIConference.js +1722 -1654
  84. package/lib/domain/CallLog.js +10 -22
  85. package/lib/domain/CallSession.js +1 -18
  86. package/lib/domain/Features.js +91 -23
  87. package/lib/domain/IndirectTransfer.js +0 -17
  88. package/lib/domain/Meeting.js +73 -0
  89. package/lib/domain/Phone/CTIPhone.js +98 -26
  90. package/lib/domain/Phone/Phone.js +0 -16
  91. package/lib/domain/Phone/WebRTCPhone.js +47 -27
  92. package/lib/domain/Room.js +0 -17
  93. package/lib/domain/SwitchboardCall.js +0 -17
  94. package/lib/domain/__tests__/CallLog.test.js +10 -22
  95. package/lib/domain/__tests__/CallSession.test.js +1 -18
  96. package/lib/domain/__tests__/IndirectTransfer.test.js +1 -18
  97. package/lib/domain/__tests__/Room.test.js +1 -18
  98. package/lib/index.js +3880 -3111
  99. package/lib/lib/WazoSessionDescriptionHandler.js +24 -10
  100. package/lib/service/CallApi.js +91 -23
  101. package/lib/service/IssueReporter.js +7 -1
  102. package/lib/service/__tests__/IssueReporter.test.js +9 -1
  103. package/lib/service/getApiClient.js +91 -23
  104. package/lib/simple/Auth.js +1126 -360
  105. package/lib/simple/Configuration.js +1126 -360
  106. package/lib/simple/Directory.js +91 -23
  107. package/lib/simple/Phone.js +3724 -2958
  108. package/lib/simple/Websocket.js +13 -1
  109. package/lib/simple/index.js +1128 -362
  110. package/lib/simple/room/LocalParticipant.js +1097 -331
  111. package/lib/simple/room/Participant.js +1097 -331
  112. package/lib/simple/room/RemoteParticipant.js +1097 -331
  113. package/lib/simple/room/Room.js +1097 -331
  114. package/lib/simple/room/SipRoom.js +13006 -0
  115. package/lib/simple/room/Stream.js +1128 -362
  116. package/lib/simple/room/__tests__/RemoteParticipant.test.js +5157 -4389
  117. package/lib/simple/utils.js +1128 -362
  118. package/lib/utils/__tests__/api-requester.test.js +7 -1
  119. package/lib/utils/api-requester.js +7 -1
  120. package/lib/web-rtc-client.js +165 -56
  121. package/lib/websocket-client.js +13 -1
  122. package/package.json +1 -1
  123. package/src/api/calld.js +3 -0
  124. package/src/api/confd.js +13 -0
  125. package/src/domain/CallLog.js +10 -22
  126. package/src/domain/CallSession.js +1 -2
  127. package/src/domain/Meeting.js +59 -0
  128. package/src/domain/Phone/CTIPhone.js +6 -2
  129. package/src/domain/Phone/Phone.js +2 -0
  130. package/src/domain/Phone/WebRTCPhone.js +22 -8
  131. package/src/index.js +6 -0
  132. package/src/lib/WazoSessionDescriptionHandler.js +17 -9
  133. package/src/service/IssueReporter.js +7 -1
  134. package/src/service/__tests__/IssueReporter.test.js +2 -0
  135. package/src/simple/Phone.js +13 -4
  136. package/src/simple/index.js +8 -0
  137. package/src/simple/room/Participant.js +7 -0
  138. package/src/simple/room/Room.js +157 -79
  139. package/src/simple/room/SipRoom.js +147 -0
  140. package/src/simple/room/Stream.js +1 -3
  141. package/src/simple/room/__tests__/RemoteParticipant.test.js +3 -2
  142. package/src/types/WebSocketMessage.js +21 -0
  143. package/src/web-rtc-client.js +57 -24
  144. package/src/websocket-client.js +4 -0
@@ -1,4 +1,3 @@
1
- /* global document */
2
1
  // @flow
3
2
  import type { Message } from 'sip.js/lib/api/message';
4
3
  import sdpParser from 'sdp-transform';
@@ -10,6 +9,7 @@ import Wazo from '../index';
10
9
  import Participant from './Participant';
11
10
  import RemoteParticipant from './RemoteParticipant';
12
11
  import IssueReporter from '../../service/IssueReporter';
12
+ import LocalParticipant from './LocalParticipant';
13
13
 
14
14
  export const SIGNAL_TYPE_PARTICIPANT_UPDATE = 'signal/PARTICIPANT_UPDATE';
15
15
  export const SIGNAL_TYPE_PARTICIPANT_REQUEST = 'signal/PARTICIPANT_REQUEST';
@@ -21,6 +21,7 @@ class Room extends Emitter {
21
21
  name: string;
22
22
  extension: string;
23
23
  sourceId: ?number;
24
+ meetingUuid: ?string;
24
25
  participants: Participant[];
25
26
  callId: ?string;
26
27
  connected: boolean;
@@ -42,6 +43,8 @@ class Room extends Emitter {
42
43
 
43
44
  CONFERENCE_USER_PARTICIPANT_JOINED: string;
44
45
  CONFERENCE_USER_PARTICIPANT_LEFT: string;
46
+ MEETING_USER_PARTICIPANT_JOINED: string;
47
+ MEETING_USER_PARTICIPANT_LEFT: string;
45
48
  ON_SHARE_SCREEN_ENDED: string;
46
49
  ON_MESSAGE: string;
47
50
  ON_CHAT: string;
@@ -59,6 +62,7 @@ class Room extends Emitter {
59
62
  * @param extension string
60
63
  * @param sourceId number
61
64
  * @param callId string
65
+ * @param meetingUuid string
62
66
  * @param extra Object
63
67
  */
64
68
  constructor(
@@ -66,15 +70,17 @@ class Room extends Emitter {
66
70
  extension: string,
67
71
  sourceId: ?number,
68
72
  callId: ?string,
73
+ meetingUuid: ?string,
69
74
  extra: Object = {},
70
75
  ) {
71
76
  super();
72
- logger.info('room initialized', { callId, extension, sourceId });
77
+ logger.info('room initialized', { callId, extension, sourceId, meetingUuid });
73
78
 
74
79
  // Represents the room callSession
75
80
  this.callSession = callSession;
76
81
  this.extension = extension;
77
82
  this.sourceId = sourceId;
83
+ this.meetingUuid = meetingUuid;
78
84
  this.callId = callId;
79
85
  this.participants = [];
80
86
  this.connected = false;
@@ -94,6 +100,8 @@ class Room extends Emitter {
94
100
  // Sugar syntax for `room.EVENT_NAME`
95
101
  this.CONFERENCE_USER_PARTICIPANT_JOINED = Wazo.Websocket.CONFERENCE_USER_PARTICIPANT_JOINED;
96
102
  this.CONFERENCE_USER_PARTICIPANT_LEFT = Wazo.Websocket.CONFERENCE_USER_PARTICIPANT_LEFT;
103
+ this.MEETING_USER_PARTICIPANT_JOINED = Wazo.Websocket.MEETING_USER_PARTICIPANT_JOINED;
104
+ this.MEETING_USER_PARTICIPANT_LEFT = Wazo.Websocket.MEETING_USER_PARTICIPANT_LEFT;
97
105
  this.ON_SHARE_SCREEN_ENDED = Wazo.Phone.ON_SHARE_SCREEN_ENDED;
98
106
  this.ON_MESSAGE = Wazo.Phone.ON_MESSAGE;
99
107
  this.ON_CHAT = Wazo.Phone.ON_CHAT;
@@ -128,9 +136,10 @@ class Room extends Emitter {
128
136
  * @param audioOnly boolean
129
137
  * @param extra Object
130
138
  * @param room ?Room
139
+ * @param meeting ?Meeting
131
140
  * @returns {Promise<Room>}
132
141
  */
133
- static async connect({ extension, constraints, audioOnly = false, extra, room }: Object) {
142
+ static async connect({ extension, constraints, audioOnly = false, extra, room, meeting }: Object) {
134
143
  logger.info('connecting to room', { extension, audioOnly, room: !!room });
135
144
 
136
145
  if (!room) {
@@ -165,21 +174,31 @@ class Room extends Emitter {
165
174
  room.setCallId(room.callSession.call.id);
166
175
  }
167
176
 
168
- // Fetch conference source
169
- const sources = await getApiClient().dird.fetchConferenceSource('default');
170
- // Retrieve conference sources
171
- const contacts = await getApiClient().dird.fetchConferenceContacts(sources.items[0]);
172
- // Retrieve conference
173
- const conference = contacts.find(contact => contact.numbers.find(number => number.number === extension));
177
+ if (!meeting) {
178
+ // Fetch conference source
179
+ const sources = await getApiClient().dird.fetchConferenceSource('default');
180
+ // Retrieve conference sources
181
+ const contacts = await getApiClient().dird.fetchConferenceContacts(sources.items[0]);
182
+ // Retrieve conference
183
+ const conference = contacts.find(contact => contact.numbers.find(number => number.number === extension));
174
184
 
175
- logger.info('connected to room', {
176
- sourceId: conference ? conference.sourceId : null,
177
- name: conference ? conference.name : null,
178
- });
185
+ logger.info('connected to room', {
186
+ sourceId: conference ? conference.sourceId : null,
187
+ name: conference ? conference.name : null,
188
+ });
189
+
190
+ if (conference) {
191
+ room.setSourceId(conference.sourceId);
192
+ room.setName(conference.name);
193
+ }
194
+ } else if (meeting) {
195
+ logger.info('Already connected to meeting', {
196
+ uuid: meeting.uuid,
197
+ name: meeting.name,
198
+ });
179
199
 
180
- if (conference) {
181
- room.setSourceId(conference.sourceId);
182
- room.setName(conference.name);
200
+ room.setMeetingUuid(meeting.uuid);
201
+ room.setName(meeting.name);
183
202
  }
184
203
 
185
204
  return room;
@@ -207,10 +226,8 @@ class Room extends Emitter {
207
226
  Wazo.Phone.phone.off(Wazo.Phone.phone.client.ON_REINVITE, this._boundOnReinvite);
208
227
  Wazo.Websocket.off(this.CONFERENCE_USER_PARTICIPANT_JOINED, this._boundOnParticipantJoined);
209
228
  Wazo.Websocket.off(this.CONFERENCE_USER_PARTICIPANT_LEFT, this._boundOnParticipantLeft);
210
-
211
- if (this.roomAudioElement && document.body) {
212
- document.body.removeChild(this.roomAudioElement);
213
- }
229
+ Wazo.Websocket.off(this.MEETING_USER_PARTICIPANT_JOINED, this._boundOnParticipantJoined);
230
+ Wazo.Websocket.off(this.MEETING_USER_PARTICIPANT_LEFT, this._boundOnParticipantLeft);
214
231
  }
215
232
 
216
233
  setSourceId(sourceId: number) {
@@ -219,6 +236,12 @@ class Room extends Emitter {
219
236
  this.sourceId = sourceId;
220
237
  }
221
238
 
239
+ setMeetingUuid(meetingUuid: string) {
240
+ logger.info('set meeting uuid', { meetingUuid });
241
+
242
+ this.meetingUuid = meetingUuid;
243
+ }
244
+
222
245
  setCallId(callId: string) {
223
246
  logger.info('set room call id', { callId });
224
247
 
@@ -260,14 +283,22 @@ class Room extends Emitter {
260
283
  return screensharingStream;
261
284
  }
262
285
 
263
- stopScreenSharing(restoreLocalStream: boolean = true) {
286
+ async stopScreenSharing(restoreLocalStream: boolean = true) {
264
287
  logger.info('stop room screen sharing');
265
288
 
266
- Wazo.Phone.stopScreenSharing(this.callSession, restoreLocalStream);
289
+ const reinvited = await Wazo.Phone.stopScreenSharing(this.callSession, restoreLocalStream);
267
290
 
268
291
  if (this.localParticipant) {
269
292
  this.localParticipant.onStopScreensharing();
270
293
  }
294
+
295
+ if (!reinvited && this.localParticipant) {
296
+ // When we had video and we made a screenshare we don't reinvite when stopping the screensahre
297
+ // So not REINVITE even is fired, we have to restore the video stream manually
298
+ const localVideoStream = Wazo.Phone.getLocalVideoStream(this.callSession);
299
+ // $FlowFixMe
300
+ this._associateStreamTo(localVideoStream, this.localParticipant);
301
+ }
271
302
  }
272
303
 
273
304
  turnCameraOff() {
@@ -370,6 +401,22 @@ class Room extends Emitter {
370
401
  return response;
371
402
  }
372
403
 
404
+ hasALocalVideoTrack() {
405
+ return Wazo.Phone.hasALocalVideoTrack(this.callSession);
406
+ }
407
+
408
+ getLocalStream() {
409
+ return Wazo.Phone.getLocalStream(this.callSession);
410
+ }
411
+
412
+ getRemoteStream() {
413
+ return Wazo.Phone.getRemoteStream(this.callSession);
414
+ }
415
+
416
+ getRemoteVideoStream() {
417
+ return Wazo.Phone.getRemoteVideoStream(this.callSession);
418
+ }
419
+
373
420
  _bindEvents() {
374
421
  if (!Wazo.Phone.phone || !Wazo.Phone.phone.currentSipSession) {
375
422
  return;
@@ -391,27 +438,10 @@ class Room extends Emitter {
391
438
  logger.info('on room audio stream');
392
439
 
393
440
  this.audioStream = stream;
394
- if (document.createElement) {
395
- // $FlowFixMe
396
- let roomAudioElement: HTMLAudioElement = document.getElementById('audio-room');
397
- if (!this.roomAudioElement) {
398
- roomAudioElement = document.createElement('audio');
399
- // $FlowFixMe
400
- document.body.appendChild(roomAudioElement);
401
- }
402
- if (!roomAudioElement) {
403
- return;
404
- }
405
- this.roomAudioElement = roomAudioElement;
441
+ if (!this.roomAudioElement) {
442
+ const sessionId = Wazo.Phone.phone.getSipSessionId(Wazo.Phone.phone.currentSipSession);
443
+ this.roomAudioElement = Wazo.Phone.phone.createAudioElementFor(sessionId);
406
444
  this.roomAudioElement.srcObject = stream;
407
- this.roomAudioElement.autoplay = true;
408
- this.roomAudioElement.id = 'audio-room';
409
-
410
- // $FlowFixMe
411
- if (this.roomAudioElement.setSinkId) {
412
- // $FlowFixMe
413
- this.roomAudioElement.setSinkId(Wazo.Phone.getOutputDevice());
414
- }
415
445
  }
416
446
  });
417
447
 
@@ -486,6 +516,9 @@ class Room extends Emitter {
486
516
  Wazo.Websocket.on(this.CONFERENCE_USER_PARTICIPANT_JOINED, this._boundOnParticipantJoined);
487
517
  Wazo.Websocket.on(this.CONFERENCE_USER_PARTICIPANT_LEFT, this._boundOnParticipantLeft);
488
518
 
519
+ Wazo.Websocket.on(this.MEETING_USER_PARTICIPANT_JOINED, this._boundOnParticipantJoined);
520
+ Wazo.Websocket.on(this.MEETING_USER_PARTICIPANT_LEFT, this._boundOnParticipantLeft);
521
+
489
522
  // Phone events
490
523
  Wazo.Phone.on(this.ON_MESSAGE, this._boundOnMessage);
491
524
  Wazo.Phone.on(this.ON_CHAT, this._boundOnChat);
@@ -496,15 +529,15 @@ class Room extends Emitter {
496
529
  Wazo.Phone.on(event, (...args) => this.eventEmitter.emit.apply(this.eventEmitter, [event, ...args])));
497
530
  }
498
531
 
499
- _onMessage(message: Message) {
532
+ _onMessage(message: Message): ?Object {
500
533
  if (message.method !== 'MESSAGE') {
501
- return;
534
+ return null;
502
535
  }
503
536
  let body;
504
537
  try {
505
538
  body = JSON.parse(message.body);
506
539
  } catch (e) {
507
- return;
540
+ return null;
508
541
  }
509
542
 
510
543
  switch (body.type) {
@@ -525,6 +558,8 @@ class Room extends Emitter {
525
558
  }
526
559
 
527
560
  this.eventEmitter.emit(this.ON_MESSAGE, body);
561
+
562
+ return body;
528
563
  }
529
564
 
530
565
  _onChat(content: Object) {
@@ -589,23 +624,24 @@ class Room extends Emitter {
589
624
  const session = Wazo.Auth.getSession();
590
625
  let participants = [];
591
626
 
592
- // @TODO: we could use a better function name here
593
- const isJoining = part => {
594
- this.__associateStreams(part);
595
- // @VALIDATE: no need to publicize ourselves, no?
596
- if (part instanceof RemoteParticipant) {
597
- this.eventEmitter.emit(this.CONFERENCE_USER_PARTICIPANT_JOINED, part);
598
- }
599
- };
600
-
601
627
  // When we join the room, we can call `getConferenceParticipantsAsUser`, not before.
602
628
  if (participant.user_uuid === session.uuid) {
603
629
  logger.info('room current user joined');
604
630
 
605
631
  // Retrieve participants via an API calls
606
632
  const conferenceId = this.sourceId || payload.data.conference_id;
607
- const response = await getApiClient().calld.getConferenceParticipantsAsUser(conferenceId);
608
- logger.info('fetching conference participants', { conferenceId });
633
+ let response;
634
+ try {
635
+ if (this.meetingUuid) {
636
+ logger.info('fetching meeting participants', { meetingUuid: this.meetingUuid });
637
+ response = await getApiClient().calld.getMeetingParticipantsAsUser(this.meetingUuid);
638
+ } else {
639
+ logger.info('fetching conference participants', { conferenceId });
640
+ response = await getApiClient().calld.getConferenceParticipantsAsUser(conferenceId);
641
+ }
642
+ } catch (e) {
643
+ logger.error('room participants fetching, error', e);
644
+ }
609
645
  if (response) {
610
646
  logger.info('conference participants fetched', { nb: response.items.length });
611
647
  participants = response.items.map(item => {
@@ -620,25 +656,10 @@ class Room extends Emitter {
620
656
 
621
657
  const localParticipant = participants.find(someParticipant => someParticipant instanceof Wazo.LocalParticipant);
622
658
  if (!this.localParticipant && localParticipant) {
623
- this.localParticipant = localParticipant;
624
-
625
- const localVideoStream = this._getLocalVideoStream();
626
- if (localVideoStream) {
627
- this._saveLocalVideoStream(localVideoStream);
628
- }
629
-
630
- this.connected = true;
631
-
632
- // we're in the room, now let's request everyone's status
633
- if (this.localParticipant) {
634
- this.sendSignal({
635
- type: SIGNAL_TYPE_PARTICIPANT_REQUEST,
636
- origin: this.localParticipant.getStatus(),
637
- });
638
- }
659
+ this._onLocalParticipantJoined(localParticipant);
639
660
  }
640
661
 
641
- participants.forEach(someParticipant => isJoining(someParticipant));
662
+ participants.forEach(someParticipant => this._isParticipantJoining(someParticipant));
642
663
  this.eventEmitter.emit(this.ON_JOINED, localParticipant, participants);
643
664
  }
644
665
 
@@ -653,12 +674,41 @@ class Room extends Emitter {
653
674
 
654
675
  if (remoteParticipant) {
655
676
  this.participants.push(remoteParticipant);
656
- isJoining(remoteParticipant);
677
+ this._isParticipantJoining(remoteParticipant);
657
678
  }
658
679
 
659
680
  return remoteParticipant;
660
681
  }
661
682
 
683
+ _onLocalParticipantJoined(localParticipant: LocalParticipant) {
684
+ this.localParticipant = localParticipant;
685
+
686
+ const localVideoStream = this._getLocalVideoStream();
687
+ if (localVideoStream) {
688
+ this._saveLocalVideoStream(localVideoStream);
689
+ }
690
+
691
+ this.connected = true;
692
+
693
+ localParticipant.broadcastStatus();
694
+
695
+ // we're in the room, now let's request everyone's status
696
+ if (this.localParticipant) {
697
+ this.sendSignal({
698
+ type: SIGNAL_TYPE_PARTICIPANT_REQUEST,
699
+ origin: this.localParticipant.getStatus(),
700
+ });
701
+ }
702
+ }
703
+
704
+ _isParticipantJoining(participant: Participant) {
705
+ this.__associateStreams(participant);
706
+ // @VALIDATE: no need to publicize ourselves, no?
707
+ if (participant instanceof RemoteParticipant) {
708
+ this.eventEmitter.emit(this.CONFERENCE_USER_PARTICIPANT_JOINED, participant);
709
+ }
710
+ }
711
+
662
712
  _saveLocalVideoStream(stream: MediaStream) {
663
713
  const { localParticipant } = this;
664
714
 
@@ -704,9 +754,11 @@ class Room extends Emitter {
704
754
  _onParticipantTrackUpdate(oldParticipant: Participant, update: string): Participant {
705
755
  const newParticipant = oldParticipant;
706
756
 
707
- const { trackId } = this._callIdStreamIdMap[newParticipant.callId] || {};
757
+ const { trackId, streamId } = this._callIdStreamIdMap[newParticipant.callId] || {};
708
758
  const pc = Wazo.Phone.phone.currentSipSession.sessionDescriptionHandler.peerConnection;
709
- const stream = pc.getRemoteStreams().find(someStream => someStream.getTracks().some(track => track.id === trackId));
759
+ // Can't use `getReceivers` here because on FF we make the mapping based on the streamId
760
+ const stream = pc.getRemoteStreams().find(someStream =>
761
+ someStream.id === streamId || someStream.getTracks().some(track => track.id === trackId));
710
762
 
711
763
  if (update === 'downgrade') {
712
764
  newParticipant.streams = [];
@@ -736,15 +788,41 @@ class Room extends Emitter {
736
788
  return;
737
789
  }
738
790
 
739
- if (this._unassociatedVideoStreams[trackId]) {
791
+ const streamId = this._getStreamIdFrTrackId(trackId);
792
+ const key = this._getUnassociatedMapIdFromTrackIdOrStreamId(trackId, streamId);
793
+ const stream = this._unassociatedVideoStreams[key];
794
+
795
+ if (stream) {
740
796
  // Try to associate stream
741
- this._associateStreamTo(this._unassociatedVideoStreams[trackId], participant);
797
+ this._associateStreamTo(stream, participant);
742
798
 
743
- delete this._unassociatedVideoStreams[trackId];
799
+ delete this._unassociatedVideoStreams[key];
744
800
  delete this._unassociatedParticipants[participant.callId];
745
801
  }
746
802
  }
747
803
 
804
+ _getUnassociatedMapIdFromTrackIdOrStreamId(trackId: string, streamId: ?string) {
805
+ // Find by trackId
806
+ if (trackId in this._unassociatedVideoStreams) {
807
+ return trackId;
808
+ }
809
+
810
+ // Find by streamId
811
+ if (streamId && streamId in this._unassociatedVideoStreams) {
812
+ return streamId;
813
+ }
814
+
815
+ // Find in all the streams by streamId (used on FF where we can't map by trackId)
816
+ const idx = Object.values(this._unassociatedVideoStreams).findIndex((stream: Object) => stream.id === streamId);
817
+ return idx === -1 ? null : Object.keys(this._unassociatedVideoStreams)[idx];
818
+ }
819
+
820
+ _getStreamIdFrTrackId(trackId: string) {
821
+ const mapping: Object = Object.values(this._callIdStreamIdMap).find((map: Object) => map.trackId === trackId);
822
+
823
+ return mapping ? mapping.streamId : null;
824
+ }
825
+
748
826
  _associateStreamTo(rawStream: any, participant: Participant) {
749
827
  const stream = new Wazo.Stream(rawStream, participant);
750
828
  participant.streams.push(stream);
@@ -0,0 +1,147 @@
1
+ // @flow
2
+
3
+ import type { Message } from 'sip.js/lib/api/message';
4
+ import IssueReporter from '../../service/IssueReporter';
5
+ import Wazo from '../index';
6
+ import Room from './Room';
7
+
8
+ const logger = IssueReporter.loggerFor('sdk-sip-room');
9
+
10
+ class SipRoom extends Room {
11
+
12
+ static async connect({ extension, constraints, audioOnly = false, extra, room }: Object) {
13
+ logger.info('connecting to sip room', { extension, audioOnly, room: !!room });
14
+
15
+ if (!room) {
16
+ await Wazo.Phone.connect();
17
+
18
+ const withCamera = constraints && !!constraints.video;
19
+
20
+ const callSession = await Wazo.Phone.call(extension, withCamera, null, audioOnly, true);
21
+ // eslint-disable-next-line no-param-reassign
22
+ room = new SipRoom(callSession, extension, null, null, extra);
23
+ }
24
+
25
+ if (room && room.callSession && room.callSession.call) {
26
+ room.setCallId(room.callSession.call.id);
27
+ }
28
+
29
+ logger.info('connected to room', {
30
+ extension: room.extension,
31
+ });
32
+
33
+ return room;
34
+ }
35
+
36
+ mute() {
37
+ logger.info('mute sip room');
38
+
39
+ Wazo.Phone.mute(this.callSession, false);
40
+
41
+ this.sendMuteStatus();
42
+ }
43
+
44
+ unmute() {
45
+ logger.info('unmute sip room');
46
+
47
+ Wazo.Phone.unmute(this.callSession, false);
48
+
49
+ this.sendUnMuteStatus();
50
+ }
51
+
52
+ getLocalGuestName() {
53
+ return Wazo.Phone.phone.client.userAgent.options.displayName;
54
+ }
55
+
56
+ // Overridden to not listen to websocket messages
57
+ _transferEvents() {
58
+ // Phone events
59
+ Wazo.Phone.on(this.ON_MESSAGE, this._boundOnMessage);
60
+ Wazo.Phone.on(this.ON_CHAT, this._boundOnChat);
61
+ Wazo.Phone.on(this.ON_SIGNAL, this._boundOnSignal);
62
+ Wazo.Phone.on(this.ON_VIDEO_INPUT_CHANGE, this._boundSaveLocalVideoStream);
63
+
64
+ [this.ON_AUDIO_STREAM, this.ON_VIDEO_STREAM, this.ON_REMOVE_STREAM].forEach(event =>
65
+ Wazo.Phone.on(event, (...args) => this.eventEmitter.emit.apply(this.eventEmitter, [event, ...args])));
66
+ }
67
+
68
+ _onMessage(message: Message) {
69
+ // eslint-disable-next-line no-underscore-dangle
70
+ const body = super._onMessage(message);
71
+ if (!body) {
72
+ return;
73
+ }
74
+
75
+ const getChannel = () => body.channels[0];
76
+
77
+ switch (body.type) {
78
+ case 'ConfbridgeWelcome':
79
+ body.channels.forEach(channel => {
80
+ this._onParticipantJoined(channel);
81
+ });
82
+
83
+ break;
84
+ case 'ConfbridgeJoin': {
85
+ const channel = getChannel();
86
+ this._onParticipantJoined(channel);
87
+ break;
88
+ }
89
+ case 'ConfbridgeLeave': {
90
+ const channel = getChannel();
91
+ this._onParticipantLeft({
92
+ data: {
93
+ call_id: channel.id,
94
+ },
95
+ });
96
+ break;
97
+ }
98
+ default:
99
+ break;
100
+ }
101
+ }
102
+
103
+ async _onParticipantJoined(channel: Object) {
104
+ const isLocal = channel.channelvars.WAZO_SIP_CALL_ID === this._getCurrentSipCallIs();
105
+ const callId = channel.id;
106
+ const ParticipantClass = isLocal ? Wazo.LocalParticipant : Wazo.RemoteParticipant;
107
+ const name = channel.caller ? channel.caller.name : null;
108
+ const extra = isLocal ? { guestName: this.getLocalGuestName() } : {};
109
+ const participant = new ParticipantClass(this, {
110
+ caller_id_name: name,
111
+ call_id: callId,
112
+ }, extra);
113
+
114
+ const participantIdx = this.participants.findIndex(other => other.callId === participant.callId);
115
+ if (participantIdx !== -1 && name) {
116
+ this.participants[participantIdx].name = name;
117
+ return;
118
+ }
119
+
120
+ if (isLocal) {
121
+ // Should be updated by the Wazo WS but we don't have it for now
122
+ if (this.callSession) {
123
+ this.callSession.ringing = false;
124
+ }
125
+
126
+ this._onLocalParticipantJoined(participant);
127
+
128
+ // Give some time for the stream to be updated
129
+ setTimeout(() => {
130
+ this.eventEmitter.emit(this.ON_JOINED, participant, this.participants);
131
+ }, 1000);
132
+ }
133
+
134
+ this.participants.push(participant);
135
+
136
+ this._isParticipantJoining(participant);
137
+
138
+ return participant;
139
+ }
140
+
141
+ _getCurrentSipCallIs() {
142
+ return Wazo.Phone.getSipSessionId(Wazo.Phone.phone.currentSipSession);
143
+ }
144
+
145
+ }
146
+
147
+ export default SipRoom;
@@ -52,9 +52,7 @@ class Stream {
52
52
  return false;
53
53
  }
54
54
 
55
- // With canvas track (used to fake upgrade in conference), we don't have a width
56
- return this.htmlStream.getTracks().find(track => track.kind === 'video'
57
- && track.readyState !== 'ended' && 'width' in track.getSettings());
55
+ return this.htmlStream.getTracks().some(track => track.kind === 'video' && track.readyState !== 'ended');
58
56
  }
59
57
 
60
58
  get id() {
@@ -1,9 +1,10 @@
1
- import RemoteParticipant from '../RemoteParticipant';
1
+ // Using directly Wazo to avoid issues with require cycle
2
+ import Wazo from '../../index';
2
3
 
3
4
  describe('RemoteParticipant', () => {
4
5
  it('should fall back on number when name is <unknown>', () => {
5
6
  const number = '1234';
6
- const participant = new RemoteParticipant(null, { caller_id_name: '<unknown>', caller_id_number: number });
7
+ const participant = new Wazo.RemoteParticipant(null, { caller_id_name: '<unknown>', caller_id_number: number });
7
8
  expect(participant.name).toEqual(number);
8
9
  });
9
10
  });
@@ -91,6 +91,17 @@ export type RoomParticipantUpdate = {
91
91
  muted: boolean,
92
92
  user_uuid: string,
93
93
  };
94
+ export type MeetingParticipantUpdate = {
95
+ admin: boolean,
96
+ call_id: string,
97
+ caller_id_name: string,
98
+ caller_id_number: string,
99
+ room_uuid: string,
100
+ id: string,
101
+ language: string,
102
+ muted: boolean,
103
+ user_uuid: string,
104
+ };
94
105
 
95
106
  export type FaxOutboundFailedEvent = {
96
107
  call_id: string,
@@ -145,6 +156,14 @@ type ParticipantLeftRoom = WebSocketBaseMessage & {
145
156
  data: RoomParticipantUpdate,
146
157
  name: 'conference_user_participant_left',
147
158
  };
159
+ type ParticipantJoinedMeeting = WebSocketBaseMessage & {
160
+ data: MeetingParticipantUpdate,
161
+ name: 'meeting_user_participant_joined',
162
+ };
163
+ type ParticipantLeftMeeting = WebSocketBaseMessage & {
164
+ data: MeetingParticipantUpdate,
165
+ name: 'meeting_user_participant_left',
166
+ };
148
167
  type SwitchboardQueuedCallsUpdated = WebSocketBaseMessage & {
149
168
  data: SwitchboardCallItems,
150
169
  name: 'switchboard_queued_calls_updated',
@@ -194,6 +213,8 @@ export type WebSocketMessage =
194
213
  | SwitchboardHeldCallAnswered
195
214
  | ParticipantJoinedRoom
196
215
  | ParticipantLeftRoom
216
+ | ParticipantJoinedMeeting
217
+ | ParticipantLeftMeeting
197
218
  | FaxOutboundFailed
198
219
  | FaxOutboundSucceeded
199
220
  | AgentStatusUpdate