@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
@@ -116,7 +116,9 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
116
116
  .then(() => {
117
117
  if (isConference && (options.constraints && !options.constraints.video) && !('hold' in options) && !audioOnly) {
118
118
  // Add a video an empty bundle to be able to replaceTrack when joining a conference without video
119
- this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
119
+ if (this.peerConnection.addTransceiver) {
120
+ this.peerConnection.addTransceiver('video', { streams: [this._localMediaStream], direction: 'sendrecv' });
121
+ }
120
122
  }
121
123
  })
122
124
  .then(() => this.createLocalOfferOrAnswer(options))
@@ -143,12 +145,6 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
143
145
  };
144
146
  })
145
147
  .then((sessionDescription) => this.applyModifiers(sessionDescription, modifiers))
146
- .then((sessionDescription) => {
147
- // Set new constraints to avoid the constraints check issue in `sdh.getLocalMediaStream` later.
148
- this.localMediaStreamConstraints = options.constraints;
149
-
150
- return sessionDescription;
151
- })
152
148
  .then((sessionDescription) => ({ body: sessionDescription.sdp, contentType: 'application/sdp' }))
153
149
  .catch((error) => {
154
150
  wazoLogger.error('error when creating media', error);
@@ -343,6 +339,9 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
343
339
 
344
340
  // set the transceiver direction to the answer direction
345
341
  this._peerConnection.getTransceivers().forEach((transceiver) => {
342
+ if (transceiver.stopped) {
343
+ return;
344
+ }
346
345
  if (transceiver.direction /* guarding, but should always be true */) {
347
346
  const { receiver } = transceiver;
348
347
  if (isConference && audioOnly && receiver.track && receiver.track.kind === 'video') {
@@ -387,10 +386,15 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
387
386
  if (kind !== 'audio' && kind !== 'video') {
388
387
  throw new Error(`Unknown new track kind ${kind}.`);
389
388
  }
390
- const sender = pc.getSenders().find((otherSender) => otherSender.track && otherSender.track.kind === kind);
389
+ const sender = pc.getSenders && pc.getSenders().find((otherSender) =>
390
+ otherSender.track && otherSender.track.kind === kind);
391
391
 
392
392
  // Do not reuse sender video tracks in SFU
393
393
  if (sender && (!sfu || newTrack.kind === 'audio')) {
394
+ if (sender.track) {
395
+ // eslint-disable-next-line no-param-reassign
396
+ newTrack.enabled = sender.track.enabled;
397
+ }
394
398
  trackUpdates.push(
395
399
  new Promise((resolve) => {
396
400
  this.logger.debug(`SessionDescriptionHandler.setLocalMediaStream - replacing sender ${kind} track`);
@@ -424,7 +428,11 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
424
428
  // Review: could make streamless tracks a configurable option?
425
429
  // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTrack#Usage_notes
426
430
  try {
427
- pc.addTrack(newTrack, localStream);
431
+ if (pc.addTrack) {
432
+ pc.addTrack(newTrack, localStream);
433
+ } else {
434
+ pc.addStream(localStream);
435
+ }
428
436
  } catch (error) {
429
437
  this.logger.error(`SessionDescriptionHandler.setLocalMediaStream - failed to add sender ${kind} track`);
430
438
  throw error;
@@ -171,13 +171,18 @@ class Phone extends Emitter {
171
171
  return adHocConference.start();
172
172
  }
173
173
 
174
- mute(callSession: CallSession) {
175
- this.muteViaAPI(callSession);
174
+ mute(callSession: CallSession, withApi: boolean = true) {
175
+ if (withApi) {
176
+ this.muteViaAPI(callSession);
177
+ }
178
+
176
179
  return this.phone && this.phone.mute(callSession);
177
180
  }
178
181
 
179
- unmute(callSession: CallSession) {
180
- this.unmuteViaAPI(callSession);
182
+ unmute(callSession: CallSession, withApi: boolean = true) {
183
+ if (withApi) {
184
+ this.unmuteViaAPI(callSession);
185
+ }
181
186
 
182
187
  return this.phone && this.phone.unmute(callSession);
183
188
  }
@@ -283,6 +288,10 @@ class Phone extends Emitter {
283
288
  return this.phone && this.phone.hasLocalVideo(callSession);
284
289
  }
285
290
 
291
+ hasALocalVideoTrack(callSession: CallSession) {
292
+ return this.phone && this.phone.hasALocalVideoTrack(callSession);
293
+ }
294
+
286
295
  // @Deprecated
287
296
  getLocalMediaStream(callSession: CallSession) {
288
297
  logger.warn('Phone.getLocalMediaStream is deprecated, use Phone.getLocalStream instead');
@@ -3,6 +3,7 @@ import Auth, { InvalidSubscription, InvalidAuthorization } from './Auth';
3
3
  import Phone from './Phone';
4
4
  import Websocket from './Websocket';
5
5
  import Room from './room/Room';
6
+ import SipRoom from './room/SipRoom';
6
7
  import RemoteParticipant from './room/RemoteParticipant';
7
8
  import LocalParticipant from './room/LocalParticipant';
8
9
  import Stream from './room/Stream';
@@ -35,6 +36,7 @@ import IndirectTransfer from '../domain/IndirectTransfer';
35
36
  import SwitchboardCall from '../domain/SwitchboardCall';
36
37
  import IssueReporter from '../service/IssueReporter';
37
38
  import Features from '../domain/Features';
39
+ import Meeting from '../domain/Meeting';
38
40
  import Checker from '../checker/Checker';
39
41
 
40
42
  const Wazo = {
@@ -42,6 +44,7 @@ const Wazo = {
42
44
  Phone,
43
45
  Websocket,
44
46
  Room,
47
+ SipRoom,
45
48
  RemoteParticipant,
46
49
  LocalParticipant,
47
50
  Stream,
@@ -78,6 +81,7 @@ const Wazo = {
78
81
  IndirectTransfer,
79
82
  SwitchboardCall,
80
83
  WebRTCPhone,
84
+ Meeting,
81
85
  },
82
86
 
83
87
  // Api
@@ -3,6 +3,7 @@ import Emitter from '../../utils/Emitter';
3
3
  import IssueReporter from '../../service/IssueReporter';
4
4
 
5
5
  import Room, { SIGNAL_TYPE_PARTICIPANT_UPDATE } from './Room';
6
+ import Contact from '../../domain/Contact';
6
7
 
7
8
  const logger = IssueReporter.loggerFor('room');
8
9
 
@@ -38,6 +39,7 @@ class Participant extends Emitter {
38
39
  ON_RESUME: string;
39
40
 
40
41
  /**
42
+ * @param room Room The room where the participant is
41
43
  * @param rawParticipant string Participant sent via the Wazo WS
42
44
  * @param extra Object extra status of the participant
43
45
  */
@@ -267,6 +269,11 @@ class Participant extends Emitter {
267
269
  // Poor man's object comparison
268
270
  if (typeof status.extra !== 'undefined' && JSON.stringify(this.extra) !== JSON.stringify(status.extra)) {
269
271
  this.extra = { ...this.extra, ...status.extra };
272
+
273
+ if (this.extra.contact && !(this.extra.contact instanceof Contact)) {
274
+ this.extra.contact = new Contact(this.extra.contact);
275
+ }
276
+
270
277
  this.triggerUpdate(this.ON_EXTRA_CHANGE, broadcast);
271
278
  }
272
279
  }
@@ -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,42 @@ 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) =>
817
+ stream && stream.id === streamId);
818
+ return idx === -1 ? null : Object.keys(this._unassociatedVideoStreams)[idx];
819
+ }
820
+
821
+ _getStreamIdFrTrackId(trackId: string) {
822
+ const mapping: Object = Object.values(this._callIdStreamIdMap).find((map: Object) => map.trackId === trackId);
823
+
824
+ return mapping ? mapping.streamId : null;
825
+ }
826
+
748
827
  _associateStreamTo(rawStream: any, participant: Participant) {
749
828
  const stream = new Wazo.Stream(rawStream, participant);
750
829
  participant.streams.push(stream);