@wazo/sdk 0.37.30 → 0.37.34

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 (80) hide show
  1. package/dist/wazo-sdk.js +4 -4
  2. package/esm/__tests__/api-client.test.js +30 -0
  3. package/esm/__tests__/web-rtc-client.test.js +57 -6
  4. package/esm/api/calld.js +30 -0
  5. package/esm/api-client.js +30 -0
  6. package/esm/checker/Checker.js +57 -6
  7. package/esm/checker/checks/aor.js +30 -0
  8. package/esm/checker/checks/api.js +30 -0
  9. package/esm/checker/checks/index.js +57 -6
  10. package/esm/checker/checks/webrtc-transport.js +57 -6
  11. package/esm/checker/checks/webrtc.js +57 -6
  12. package/esm/domain/AdHocAPIConference.js +30 -0
  13. package/esm/domain/CallSession.js +9 -1
  14. package/esm/domain/Features.js +30 -0
  15. package/esm/domain/MeetingStatus.js +29 -0
  16. package/esm/domain/Phone/CTIPhone.js +39 -1
  17. package/esm/domain/Phone/WebRTCPhone.js +43 -8
  18. package/esm/domain/__tests__/CallSession.test.js +41 -5
  19. package/esm/domain/__tests__/IndirectTransfer.test.js +9 -1
  20. package/esm/domain/__tests__/Room.test.js +9 -1
  21. package/esm/index.js +100 -14
  22. package/esm/service/CallApi.js +30 -0
  23. package/esm/service/getApiClient.js +30 -0
  24. package/esm/simple/Auth.js +100 -14
  25. package/esm/simple/Configuration.js +100 -14
  26. package/esm/simple/Directory.js +30 -0
  27. package/esm/simple/Phone.js +100 -14
  28. package/esm/simple/index.js +100 -14
  29. package/esm/simple/room/LocalParticipant.js +100 -14
  30. package/esm/simple/room/Participant.js +100 -14
  31. package/esm/simple/room/RemoteParticipant.js +100 -14
  32. package/esm/simple/room/Room.js +100 -14
  33. package/esm/simple/room/SipRoom.js +100 -14
  34. package/esm/simple/room/Stream.js +100 -14
  35. package/esm/simple/room/__tests__/RemoteParticipant.test.js +100 -14
  36. package/esm/simple/utils.js +100 -14
  37. package/esm/web-rtc-client.js +57 -6
  38. package/lib/__tests__/api-client.test.js +30 -0
  39. package/lib/__tests__/web-rtc-client.test.js +57 -6
  40. package/lib/api/calld.js +30 -0
  41. package/lib/api-client.js +30 -0
  42. package/lib/checker/Checker.js +57 -6
  43. package/lib/checker/checks/aor.js +30 -0
  44. package/lib/checker/checks/api.js +30 -0
  45. package/lib/checker/checks/index.js +57 -6
  46. package/lib/checker/checks/webrtc-transport.js +57 -6
  47. package/lib/checker/checks/webrtc.js +57 -6
  48. package/lib/domain/AdHocAPIConference.js +30 -0
  49. package/lib/domain/CallSession.js +9 -1
  50. package/lib/domain/Features.js +30 -0
  51. package/lib/domain/MeetingStatus.js +31 -0
  52. package/lib/domain/Phone/CTIPhone.js +39 -1
  53. package/lib/domain/Phone/WebRTCPhone.js +43 -8
  54. package/lib/domain/__tests__/CallSession.test.js +41 -5
  55. package/lib/domain/__tests__/IndirectTransfer.test.js +9 -1
  56. package/lib/domain/__tests__/Room.test.js +9 -1
  57. package/lib/index.js +100 -14
  58. package/lib/service/CallApi.js +30 -0
  59. package/lib/service/getApiClient.js +30 -0
  60. package/lib/simple/Auth.js +100 -14
  61. package/lib/simple/Configuration.js +100 -14
  62. package/lib/simple/Directory.js +30 -0
  63. package/lib/simple/Phone.js +100 -14
  64. package/lib/simple/index.js +100 -14
  65. package/lib/simple/room/LocalParticipant.js +100 -14
  66. package/lib/simple/room/Participant.js +100 -14
  67. package/lib/simple/room/RemoteParticipant.js +100 -14
  68. package/lib/simple/room/Room.js +100 -14
  69. package/lib/simple/room/SipRoom.js +100 -14
  70. package/lib/simple/room/Stream.js +100 -14
  71. package/lib/simple/room/__tests__/RemoteParticipant.test.js +100 -14
  72. package/lib/simple/utils.js +100 -14
  73. package/lib/web-rtc-client.js +57 -6
  74. package/package.json +1 -1
  75. package/src/api/calld.js +3 -0
  76. package/src/domain/CallSession.js +9 -1
  77. package/src/domain/MeetingStatus.js +27 -0
  78. package/src/domain/Phone/WebRTCPhone.js +34 -7
  79. package/src/domain/__tests__/CallSession.test.js +32 -4
  80. package/src/web-rtc-client.js +27 -6
@@ -3610,13 +3610,21 @@ class CallSession {
3610
3610
 
3611
3611
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
3612
3612
  set answered(value ) {
3613
- this.answerTime = value ? new Date() : null;
3613
+ this.answerTime = value ? this.answerTime || new Date() : null;
3614
3614
  }
3615
3615
 
3616
3616
  get answered() {
3617
3617
  return !!this.answerTime;
3618
3618
  }
3619
3619
 
3620
+ toJSON() {
3621
+ const jsonObj = { ...this };
3622
+ // $FlowFixMe
3623
+ jsonObj.answered = this.answered;
3624
+
3625
+ return jsonObj;
3626
+ }
3627
+
3620
3628
  static parseCall(call ) {
3621
3629
  return new CallSession({
3622
3630
  callId: call.id,
@@ -4842,6 +4850,34 @@ var chatdMethods = (client , baseUrl ) => ({
4842
4850
  client.get(`${baseUrl}/users/me/rooms/messages`, options),
4843
4851
  });
4844
4852
 
4853
+ //
4854
+
4855
+
4856
+
4857
+
4858
+
4859
+ class MeetingStatus {
4860
+
4861
+
4862
+
4863
+
4864
+ constructor({
4865
+ full,
4866
+ } = {}) {
4867
+ this.full = full;
4868
+
4869
+ // Useful to compare instead of instanceof with minified code
4870
+ this.type = 'MeetingStatus';
4871
+ }
4872
+
4873
+ static parse(plain ) {
4874
+ return new MeetingStatus({
4875
+ full: plain.full,
4876
+ });
4877
+ }
4878
+
4879
+ }
4880
+
4845
4881
  /* */
4846
4882
 
4847
4883
 
@@ -5036,6 +5072,8 @@ var calldMethods = (client , baseUrl ) => ({
5036
5072
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
5037
5073
  ApiRequester.successResponseParser),
5038
5074
 
5075
+ guestGetMeetingStatus: (meetingUuid ) =>
5076
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
5039
5077
  });
5040
5078
 
5041
5079
  //
@@ -6625,7 +6663,7 @@ const events$1 = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
6625
6663
  const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
6626
6664
  class CanceledCallError extends Error {}
6627
6665
 
6628
- const MAX_REGISTER_TRIES = 10;
6666
+ const MAX_REGISTER_TRIES = 5;
6629
6667
 
6630
6668
 
6631
6669
 
@@ -6679,6 +6717,7 @@ class WebRTCClient extends Emitter {
6679
6717
 
6680
6718
 
6681
6719
 
6720
+
6682
6721
 
6683
6722
  // sugar
6684
6723
 
@@ -6741,6 +6780,7 @@ class WebRTCClient extends Emitter {
6741
6780
  this.conferences = {};
6742
6781
  this.networkMonitoringInterval = {};
6743
6782
  this.sessionNetworkStats = {};
6783
+ this.forceClosed = false;
6744
6784
 
6745
6785
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
6746
6786
  this.heartbeat = new Heartbeat(config.heartbeatDelay, config.heartbeatTimeout, config.maxHeartbeats);
@@ -6862,6 +6902,8 @@ class WebRTCClient extends Emitter {
6862
6902
  skipRegister: this.skipRegister,
6863
6903
  };
6864
6904
 
6905
+ this.forceClosed = false;
6906
+
6865
6907
  if (this.skipRegister) {
6866
6908
  logger$8.info('sdk webrtc skip register...', logInfo);
6867
6909
  return Promise.resolve();
@@ -6886,7 +6928,15 @@ class WebRTCClient extends Emitter {
6886
6928
  const registerOptions = this._isWeb() ? {} : { extraContactHeaderParams: ['mobility=mobile'] };
6887
6929
 
6888
6930
  const onRegisterFailed = () => {
6889
- logger$8.info('sdk webrtc registering failed', { tries, registerer: !!this.registerer });
6931
+ logger$8.info('sdk webrtc registering failed', {
6932
+ tries,
6933
+ registerer: !!this.registerer,
6934
+ forceClosed: this.forceClosed,
6935
+ });
6936
+ if (this.forceClosed) {
6937
+ return;
6938
+ }
6939
+
6890
6940
  this.connectionPromise = null;
6891
6941
  if (this.registerer && this.registerer.waiting) {
6892
6942
  this.registerer.waitingToggle(false);
@@ -7164,8 +7214,9 @@ class WebRTCClient extends Emitter {
7164
7214
  }
7165
7215
  }
7166
7216
 
7167
- async close() {
7168
- logger$8.info('sdk webrtc closing client', { userAgent: !!this.userAgent });
7217
+ async close(force = false) {
7218
+ logger$8.info('sdk webrtc closing client', { userAgent: !!this.userAgent, force });
7219
+ this.forceClosed = force;
7169
7220
  this._cleanupMedia();
7170
7221
  this.connectionPromise = null;
7171
7222
 
@@ -7184,7 +7235,7 @@ class WebRTCClient extends Emitter {
7184
7235
  this.userAgent.delegate = null;
7185
7236
  this.userAgent.stateChange.removeAllListeners();
7186
7237
 
7187
- await this._disconnectTransport();
7238
+ await this._disconnectTransport(force);
7188
7239
 
7189
7240
  this._cleanupRegister();
7190
7241
 
@@ -8620,7 +8671,15 @@ class WebRTCClient extends Emitter {
8620
8671
  return UserAgent.makeURI(`sip:${target}@${this.config.host}`);
8621
8672
  }
8622
8673
 
8623
- async _disconnectTransport() {
8674
+ async _disconnectTransport(force = false) {
8675
+ if (force) {
8676
+ // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
8677
+ this.userAgent.transport.disconnectResolve = () => {};
8678
+ // eslint-disable-next-line
8679
+ this.userAgent.transport._ws.close(1000);
8680
+ return;
8681
+ }
8682
+
8624
8683
  // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors.
8625
8684
  if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) {
8626
8685
  try {
@@ -9357,6 +9416,7 @@ class WebRTCPhone extends Emitter {
9357
9416
  }
9358
9417
 
9359
9418
  callSession.answerTime = new Date();
9419
+ this._updateCallSession(callSession);
9360
9420
  this.eventEmitter.emit(ON_CALL_ACCEPTED, callSession, cameraEnabled);
9361
9421
 
9362
9422
  return callSession;
@@ -9566,6 +9626,12 @@ class WebRTCPhone extends Emitter {
9566
9626
  return Promise.resolve(null);
9567
9627
  }
9568
9628
 
9629
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
9630
+ if (sipSessionId && !(sipSessionId in this.callSessions)) {
9631
+ logger$7.warn('Call session already ended or not found, can\'t accept it.', { sipSessionId });
9632
+ return Promise.resolve(null);
9633
+ }
9634
+
9569
9635
  if (callSession.getId() in this.acceptedSessions) {
9570
9636
  logger$7.warn('CallSession already accepted.', { id: callSession ? callSession.getId() : 'n/a' });
9571
9637
  return Promise.resolve(callSession.sipCallId);
@@ -9573,6 +9639,7 @@ class WebRTCPhone extends Emitter {
9573
9639
 
9574
9640
  this.shouldSendReinvite = false;
9575
9641
  callSession.answerTime = new Date();
9642
+ this._updateCallSession(callSession);
9576
9643
  this.acceptedSessions[callSession.getId()] = true;
9577
9644
 
9578
9645
  this.eventEmitter.emit(ON_CALL_ANSWERED, callSession);
@@ -9672,7 +9739,7 @@ class WebRTCPhone extends Emitter {
9672
9739
  // Hold current session if different from the current one (we don't want 2 sessions active at the same time).
9673
9740
  if (this.currentSipSession && this.currentSipSession.id !== sipSession.id) {
9674
9741
  logger$7.info('WebRTC hold call after resume', { id: this.currentSipSession.id });
9675
- this.holdSipSession(this.currentSipSession, callSession);
9742
+ this.holdSipSession(this.currentSipSession, this.currentCallSession);
9676
9743
  }
9677
9744
 
9678
9745
  const promise = this.unholdSipSession(sipSession, callSession, withEvent, isConference);
@@ -9883,6 +9950,7 @@ class WebRTCPhone extends Emitter {
9883
9950
  }
9884
9951
  const callSession = this._createOutgoingCallSession(sipSession, cameraEnabled || false);
9885
9952
  callSession.creationTime = new Date();
9953
+ this._updateCallSession(callSession);
9886
9954
 
9887
9955
  this.eventEmitter.emit(ON_PLAY_PROGRESS_SOUND, this.audioOutputDeviceId, this.audioOutputVolume);
9888
9956
 
@@ -10024,7 +10092,7 @@ class WebRTCPhone extends Emitter {
10024
10092
  return this.client && this.client.isConference(callSession.sipCallId);
10025
10093
  }
10026
10094
 
10027
- async close() {
10095
+ async close(force = false) {
10028
10096
  logger$7.info('WebRTC close');
10029
10097
 
10030
10098
  try {
@@ -10036,7 +10104,7 @@ class WebRTCPhone extends Emitter {
10036
10104
  logger$7.error('WebRTC close, unregister error', e);
10037
10105
  }
10038
10106
 
10039
- this.client.close();
10107
+ this.client.close(force);
10040
10108
  this.unbind();
10041
10109
 
10042
10110
  this.incomingSessions = [];
@@ -10162,6 +10230,8 @@ class WebRTCPhone extends Emitter {
10162
10230
  }
10163
10231
 
10164
10232
  callSession.creationTime = new Date();
10233
+ this._updateCallSession(callSession);
10234
+
10165
10235
  this.eventEmitter.emit(ON_CALL_INCOMING, callSession, wantsToDoVideo);
10166
10236
  });
10167
10237
 
@@ -10186,9 +10256,17 @@ class WebRTCPhone extends Emitter {
10186
10256
  });
10187
10257
 
10188
10258
  this.client.on(this.client.ACCEPTED, (sipSession ) => {
10189
- logger$7.info('WebRTC call accepted', { sipId: sipSession.id });
10259
+ const sessionId = this.getSipSessionId(sipSession);
10260
+ const hasSession = sessionId in this.callSessions;
10190
10261
 
10191
- this._onCallAccepted(sipSession, this.client.hasVideo(this.getSipSessionId(sipSession)));
10262
+ logger$7.info('WebRTC call accepted', { sipId: sessionId, hasSession });
10263
+
10264
+ if (!hasSession) {
10265
+ logger$7.warn('Call accepted ignored, session is no longer present in the WebRtcPhone', { sessionId });
10266
+ return;
10267
+ }
10268
+
10269
+ this._onCallAccepted(sipSession, this.client.hasVideo(sessionId));
10192
10270
 
10193
10271
  if (this.audioOutputDeviceId) {
10194
10272
  this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
@@ -10205,6 +10283,8 @@ class WebRTCPhone extends Emitter {
10205
10283
  logger$7.info('WebRTC call rejected', session.id);
10206
10284
  const callSession = this._createCallSession(session);
10207
10285
  callSession.endTime = new Date();
10286
+ this._updateCallSession(callSession);
10287
+
10208
10288
  this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
10209
10289
  });
10210
10290
 
@@ -10322,9 +10402,14 @@ class WebRTCPhone extends Emitter {
10322
10402
  return this.client.getSipSessionId(sipSession);
10323
10403
  }
10324
10404
 
10325
- _updateCallSession(callSession ) {
10405
+ getSipSessionIdFromCallSession(callSession ) {
10326
10406
  const sipSession = this.findSipSession(callSession);
10327
- const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
10407
+
10408
+ return sipSession ? this.getSipSessionId(sipSession) : null;
10409
+ }
10410
+
10411
+ _updateCallSession(callSession ) {
10412
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
10328
10413
 
10329
10414
  if (sipSessionId) {
10330
10415
  this.callSessions[sipSessionId] = callSession;
@@ -10429,6 +10514,7 @@ class WebRTCPhone extends Emitter {
10429
10514
  displayName: identity ? identity.displayName || number : number,
10430
10515
  startTime: fromSession ? fromSession.startTime : new Date(),
10431
10516
  creationTime: fromSession ? fromSession.creationTime : null,
10517
+ answerTime: fromSession ? fromSession.answerTime : null,
10432
10518
  endTime: fromSession ? fromSession.endTime : null,
10433
10519
  answered: state === SessionState.Established,
10434
10520
  paused: this.client.isCallHeld(sipSession),
@@ -3584,13 +3584,21 @@ class CallSession {
3584
3584
 
3585
3585
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
3586
3586
  set answered(value ) {
3587
- this.answerTime = value ? new Date() : null;
3587
+ this.answerTime = value ? this.answerTime || new Date() : null;
3588
3588
  }
3589
3589
 
3590
3590
  get answered() {
3591
3591
  return !!this.answerTime;
3592
3592
  }
3593
3593
 
3594
+ toJSON() {
3595
+ const jsonObj = { ...this };
3596
+ // $FlowFixMe
3597
+ jsonObj.answered = this.answered;
3598
+
3599
+ return jsonObj;
3600
+ }
3601
+
3594
3602
  static parseCall(call ) {
3595
3603
  return new CallSession({
3596
3604
  callId: call.id,
@@ -4842,6 +4850,34 @@ var chatdMethods = (client , baseUrl ) => ({
4842
4850
  client.get(`${baseUrl}/users/me/rooms/messages`, options),
4843
4851
  });
4844
4852
 
4853
+ //
4854
+
4855
+
4856
+
4857
+
4858
+
4859
+ class MeetingStatus {
4860
+
4861
+
4862
+
4863
+
4864
+ constructor({
4865
+ full,
4866
+ } = {}) {
4867
+ this.full = full;
4868
+
4869
+ // Useful to compare instead of instanceof with minified code
4870
+ this.type = 'MeetingStatus';
4871
+ }
4872
+
4873
+ static parse(plain ) {
4874
+ return new MeetingStatus({
4875
+ full: plain.full,
4876
+ });
4877
+ }
4878
+
4879
+ }
4880
+
4845
4881
  /* */
4846
4882
 
4847
4883
 
@@ -5036,6 +5072,8 @@ var calldMethods = (client , baseUrl ) => ({
5036
5072
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
5037
5073
  ApiRequester.successResponseParser),
5038
5074
 
5075
+ guestGetMeetingStatus: (meetingUuid ) =>
5076
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
5039
5077
  });
5040
5078
 
5041
5079
  //
@@ -6625,7 +6663,7 @@ const events$1 = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
6625
6663
  const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
6626
6664
  class CanceledCallError extends Error {}
6627
6665
 
6628
- const MAX_REGISTER_TRIES = 10;
6666
+ const MAX_REGISTER_TRIES = 5;
6629
6667
 
6630
6668
 
6631
6669
 
@@ -6679,6 +6717,7 @@ class WebRTCClient extends Emitter {
6679
6717
 
6680
6718
 
6681
6719
 
6720
+
6682
6721
 
6683
6722
  // sugar
6684
6723
 
@@ -6741,6 +6780,7 @@ class WebRTCClient extends Emitter {
6741
6780
  this.conferences = {};
6742
6781
  this.networkMonitoringInterval = {};
6743
6782
  this.sessionNetworkStats = {};
6783
+ this.forceClosed = false;
6744
6784
 
6745
6785
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
6746
6786
  this.heartbeat = new Heartbeat(config.heartbeatDelay, config.heartbeatTimeout, config.maxHeartbeats);
@@ -6862,6 +6902,8 @@ class WebRTCClient extends Emitter {
6862
6902
  skipRegister: this.skipRegister,
6863
6903
  };
6864
6904
 
6905
+ this.forceClosed = false;
6906
+
6865
6907
  if (this.skipRegister) {
6866
6908
  logger$8.info('sdk webrtc skip register...', logInfo);
6867
6909
  return Promise.resolve();
@@ -6886,7 +6928,15 @@ class WebRTCClient extends Emitter {
6886
6928
  const registerOptions = this._isWeb() ? {} : { extraContactHeaderParams: ['mobility=mobile'] };
6887
6929
 
6888
6930
  const onRegisterFailed = () => {
6889
- logger$8.info('sdk webrtc registering failed', { tries, registerer: !!this.registerer });
6931
+ logger$8.info('sdk webrtc registering failed', {
6932
+ tries,
6933
+ registerer: !!this.registerer,
6934
+ forceClosed: this.forceClosed,
6935
+ });
6936
+ if (this.forceClosed) {
6937
+ return;
6938
+ }
6939
+
6890
6940
  this.connectionPromise = null;
6891
6941
  if (this.registerer && this.registerer.waiting) {
6892
6942
  this.registerer.waitingToggle(false);
@@ -7164,8 +7214,9 @@ class WebRTCClient extends Emitter {
7164
7214
  }
7165
7215
  }
7166
7216
 
7167
- async close() {
7168
- logger$8.info('sdk webrtc closing client', { userAgent: !!this.userAgent });
7217
+ async close(force = false) {
7218
+ logger$8.info('sdk webrtc closing client', { userAgent: !!this.userAgent, force });
7219
+ this.forceClosed = force;
7169
7220
  this._cleanupMedia();
7170
7221
  this.connectionPromise = null;
7171
7222
 
@@ -7184,7 +7235,7 @@ class WebRTCClient extends Emitter {
7184
7235
  this.userAgent.delegate = null;
7185
7236
  this.userAgent.stateChange.removeAllListeners();
7186
7237
 
7187
- await this._disconnectTransport();
7238
+ await this._disconnectTransport(force);
7188
7239
 
7189
7240
  this._cleanupRegister();
7190
7241
 
@@ -8620,7 +8671,15 @@ class WebRTCClient extends Emitter {
8620
8671
  return UserAgent.makeURI(`sip:${target}@${this.config.host}`);
8621
8672
  }
8622
8673
 
8623
- async _disconnectTransport() {
8674
+ async _disconnectTransport(force = false) {
8675
+ if (force) {
8676
+ // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
8677
+ this.userAgent.transport.disconnectResolve = () => {};
8678
+ // eslint-disable-next-line
8679
+ this.userAgent.transport._ws.close(1000);
8680
+ return;
8681
+ }
8682
+
8624
8683
  // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors.
8625
8684
  if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) {
8626
8685
  try {
@@ -9357,6 +9416,7 @@ class WebRTCPhone extends Emitter {
9357
9416
  }
9358
9417
 
9359
9418
  callSession.answerTime = new Date();
9419
+ this._updateCallSession(callSession);
9360
9420
  this.eventEmitter.emit(ON_CALL_ACCEPTED, callSession, cameraEnabled);
9361
9421
 
9362
9422
  return callSession;
@@ -9566,6 +9626,12 @@ class WebRTCPhone extends Emitter {
9566
9626
  return Promise.resolve(null);
9567
9627
  }
9568
9628
 
9629
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
9630
+ if (sipSessionId && !(sipSessionId in this.callSessions)) {
9631
+ logger$7.warn('Call session already ended or not found, can\'t accept it.', { sipSessionId });
9632
+ return Promise.resolve(null);
9633
+ }
9634
+
9569
9635
  if (callSession.getId() in this.acceptedSessions) {
9570
9636
  logger$7.warn('CallSession already accepted.', { id: callSession ? callSession.getId() : 'n/a' });
9571
9637
  return Promise.resolve(callSession.sipCallId);
@@ -9573,6 +9639,7 @@ class WebRTCPhone extends Emitter {
9573
9639
 
9574
9640
  this.shouldSendReinvite = false;
9575
9641
  callSession.answerTime = new Date();
9642
+ this._updateCallSession(callSession);
9576
9643
  this.acceptedSessions[callSession.getId()] = true;
9577
9644
 
9578
9645
  this.eventEmitter.emit(ON_CALL_ANSWERED, callSession);
@@ -9672,7 +9739,7 @@ class WebRTCPhone extends Emitter {
9672
9739
  // Hold current session if different from the current one (we don't want 2 sessions active at the same time).
9673
9740
  if (this.currentSipSession && this.currentSipSession.id !== sipSession.id) {
9674
9741
  logger$7.info('WebRTC hold call after resume', { id: this.currentSipSession.id });
9675
- this.holdSipSession(this.currentSipSession, callSession);
9742
+ this.holdSipSession(this.currentSipSession, this.currentCallSession);
9676
9743
  }
9677
9744
 
9678
9745
  const promise = this.unholdSipSession(sipSession, callSession, withEvent, isConference);
@@ -9883,6 +9950,7 @@ class WebRTCPhone extends Emitter {
9883
9950
  }
9884
9951
  const callSession = this._createOutgoingCallSession(sipSession, cameraEnabled || false);
9885
9952
  callSession.creationTime = new Date();
9953
+ this._updateCallSession(callSession);
9886
9954
 
9887
9955
  this.eventEmitter.emit(ON_PLAY_PROGRESS_SOUND, this.audioOutputDeviceId, this.audioOutputVolume);
9888
9956
 
@@ -10024,7 +10092,7 @@ class WebRTCPhone extends Emitter {
10024
10092
  return this.client && this.client.isConference(callSession.sipCallId);
10025
10093
  }
10026
10094
 
10027
- async close() {
10095
+ async close(force = false) {
10028
10096
  logger$7.info('WebRTC close');
10029
10097
 
10030
10098
  try {
@@ -10036,7 +10104,7 @@ class WebRTCPhone extends Emitter {
10036
10104
  logger$7.error('WebRTC close, unregister error', e);
10037
10105
  }
10038
10106
 
10039
- this.client.close();
10107
+ this.client.close(force);
10040
10108
  this.unbind();
10041
10109
 
10042
10110
  this.incomingSessions = [];
@@ -10162,6 +10230,8 @@ class WebRTCPhone extends Emitter {
10162
10230
  }
10163
10231
 
10164
10232
  callSession.creationTime = new Date();
10233
+ this._updateCallSession(callSession);
10234
+
10165
10235
  this.eventEmitter.emit(ON_CALL_INCOMING, callSession, wantsToDoVideo);
10166
10236
  });
10167
10237
 
@@ -10186,9 +10256,17 @@ class WebRTCPhone extends Emitter {
10186
10256
  });
10187
10257
 
10188
10258
  this.client.on(this.client.ACCEPTED, (sipSession ) => {
10189
- logger$7.info('WebRTC call accepted', { sipId: sipSession.id });
10259
+ const sessionId = this.getSipSessionId(sipSession);
10260
+ const hasSession = sessionId in this.callSessions;
10190
10261
 
10191
- this._onCallAccepted(sipSession, this.client.hasVideo(this.getSipSessionId(sipSession)));
10262
+ logger$7.info('WebRTC call accepted', { sipId: sessionId, hasSession });
10263
+
10264
+ if (!hasSession) {
10265
+ logger$7.warn('Call accepted ignored, session is no longer present in the WebRtcPhone', { sessionId });
10266
+ return;
10267
+ }
10268
+
10269
+ this._onCallAccepted(sipSession, this.client.hasVideo(sessionId));
10192
10270
 
10193
10271
  if (this.audioOutputDeviceId) {
10194
10272
  this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
@@ -10205,6 +10283,8 @@ class WebRTCPhone extends Emitter {
10205
10283
  logger$7.info('WebRTC call rejected', session.id);
10206
10284
  const callSession = this._createCallSession(session);
10207
10285
  callSession.endTime = new Date();
10286
+ this._updateCallSession(callSession);
10287
+
10208
10288
  this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
10209
10289
  });
10210
10290
 
@@ -10322,9 +10402,14 @@ class WebRTCPhone extends Emitter {
10322
10402
  return this.client.getSipSessionId(sipSession);
10323
10403
  }
10324
10404
 
10325
- _updateCallSession(callSession ) {
10405
+ getSipSessionIdFromCallSession(callSession ) {
10326
10406
  const sipSession = this.findSipSession(callSession);
10327
- const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
10407
+
10408
+ return sipSession ? this.getSipSessionId(sipSession) : null;
10409
+ }
10410
+
10411
+ _updateCallSession(callSession ) {
10412
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
10328
10413
 
10329
10414
  if (sipSessionId) {
10330
10415
  this.callSessions[sipSessionId] = callSession;
@@ -10429,6 +10514,7 @@ class WebRTCPhone extends Emitter {
10429
10514
  displayName: identity ? identity.displayName || number : number,
10430
10515
  startTime: fromSession ? fromSession.startTime : new Date(),
10431
10516
  creationTime: fromSession ? fromSession.creationTime : null,
10517
+ answerTime: fromSession ? fromSession.answerTime : null,
10432
10518
  endTime: fromSession ? fromSession.endTime : null,
10433
10519
  answered: state === SessionState.Established,
10434
10520
  paused: this.client.isCallHeld(sipSession),