@wazo/sdk 1.0.0-alpha.0 → 1.0.0-alpha.10

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 (162) hide show
  1. package/README.md +16 -7
  2. package/dist/wazo-sdk.js +6 -6
  3. package/esm/__tests__/__voice__/call.test.js +57 -2
  4. package/esm/__tests__/api-client.test.js +20 -1
  5. package/esm/api/auth.js +16 -2
  6. package/esm/checker/checks/webrtc.js +2 -2
  7. package/esm/domain/CallLog.js +2 -3
  8. package/esm/domain/Phone/CTIPhone.js +2 -2
  9. package/esm/domain/Phone/WebRTCPhone.js +64 -63
  10. package/esm/index.js +5 -1
  11. package/esm/service/CallApi.js +4 -4
  12. package/esm/simple/Auth.js +19 -15
  13. package/esm/simple/Phone.js +6 -5
  14. package/esm/simple/__tests__/Auth.test.js +8 -64
  15. package/esm/simple/index.js +2 -1
  16. package/esm/simple/room/Room.js +2 -2
  17. package/esm/simple/room/SipRoom.js +2 -2
  18. package/esm/state-machine/call-state-machine.js +2 -2
  19. package/esm/state-machine/softphone-state-machine.js +1 -2
  20. package/esm/state-machine/utils.js +8 -3
  21. package/esm/types/api/auth.d.ts +3 -0
  22. package/esm/types/api/auth.d.ts.map +1 -1
  23. package/esm/types/checker/checks/index.d.ts +1 -1
  24. package/esm/types/checker/checks/webrtc-transport.d.ts +2 -2
  25. package/esm/types/checker/checks/webrtc-transport.d.ts.map +1 -1
  26. package/esm/types/domain/CallLog.d.ts.map +1 -1
  27. package/esm/types/domain/CallSession.d.ts +3 -3
  28. package/esm/types/domain/CallSession.d.ts.map +1 -1
  29. package/esm/types/domain/Phone/CTIPhone.d.ts.map +1 -1
  30. package/esm/types/domain/Phone/WebRTCPhone.d.ts +22 -22
  31. package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  32. package/esm/types/domain/types.d.ts +0 -4
  33. package/esm/types/domain/types.d.ts.map +1 -1
  34. package/esm/types/index.d.ts +5 -1
  35. package/esm/types/index.d.ts.map +1 -1
  36. package/esm/types/lib/WazoSessionDescriptionHandler.d.ts +3 -3
  37. package/esm/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
  38. package/esm/types/service/CallApi.d.ts +5 -5
  39. package/esm/types/service/CallApi.d.ts.map +1 -1
  40. package/esm/types/simple/Auth.d.ts +6 -1
  41. package/esm/types/simple/Auth.d.ts.map +1 -1
  42. package/esm/types/simple/Phone.d.ts +8 -8
  43. package/esm/types/simple/Phone.d.ts.map +1 -1
  44. package/esm/types/simple/index.d.ts +3 -1
  45. package/esm/types/simple/index.d.ts.map +1 -1
  46. package/esm/types/state-machine/softphone-state-machine.d.ts +1 -2
  47. package/esm/types/state-machine/softphone-state-machine.d.ts.map +1 -1
  48. package/esm/types/state-machine/utils.d.ts +2 -2
  49. package/esm/types/state-machine/utils.d.ts.map +1 -1
  50. package/esm/types/utils/api-requester.d.ts.map +1 -1
  51. package/esm/types/utils/sdp.d.ts +3 -2
  52. package/esm/types/utils/sdp.d.ts.map +1 -1
  53. package/esm/types/utils/webrtc.d.ts +1 -0
  54. package/esm/types/utils/webrtc.d.ts.map +1 -1
  55. package/esm/types/voice/call.d.ts +30 -12
  56. package/esm/types/voice/call.d.ts.map +1 -1
  57. package/esm/types/voice/softphone.d.ts +25 -13
  58. package/esm/types/voice/softphone.d.ts.map +1 -1
  59. package/esm/types/web-rtc-client.d.ts +39 -38
  60. package/esm/types/web-rtc-client.d.ts.map +1 -1
  61. package/esm/utils/__tests__/api-requester.test.js +17 -0
  62. package/esm/utils/api-requester.js +9 -1
  63. package/esm/utils/sdp.js +22 -9
  64. package/esm/utils/webrtc.js +24 -4
  65. package/esm/voice/call.js +130 -38
  66. package/esm/voice/softphone.js +145 -35
  67. package/esm/web-rtc-client.js +61 -58
  68. package/lib/__tests__/__voice__/call.test.js +56 -1
  69. package/lib/__tests__/api-client.test.js +20 -1
  70. package/lib/api/auth.js +16 -2
  71. package/lib/checker/checks/webrtc.js +1 -1
  72. package/lib/domain/CallLog.js +2 -3
  73. package/lib/domain/Phone/CTIPhone.js +2 -2
  74. package/lib/domain/Phone/WebRTCPhone.js +63 -62
  75. package/lib/index.js +11 -3
  76. package/lib/service/CallApi.js +2 -2
  77. package/lib/simple/Auth.js +21 -15
  78. package/lib/simple/Phone.js +3 -2
  79. package/lib/simple/__tests__/Auth.test.js +11 -64
  80. package/lib/simple/index.js +2 -1
  81. package/lib/simple/room/Room.js +1 -1
  82. package/lib/simple/room/SipRoom.js +1 -1
  83. package/lib/state-machine/call-state-machine.js +2 -2
  84. package/lib/state-machine/softphone-state-machine.js +1 -2
  85. package/lib/state-machine/utils.js +8 -3
  86. package/lib/types/api/auth.d.ts +3 -0
  87. package/lib/types/api/auth.d.ts.map +1 -1
  88. package/lib/types/checker/checks/index.d.ts +1 -1
  89. package/lib/types/checker/checks/webrtc-transport.d.ts +2 -2
  90. package/lib/types/checker/checks/webrtc-transport.d.ts.map +1 -1
  91. package/lib/types/domain/CallLog.d.ts.map +1 -1
  92. package/lib/types/domain/CallSession.d.ts +3 -3
  93. package/lib/types/domain/CallSession.d.ts.map +1 -1
  94. package/lib/types/domain/Phone/CTIPhone.d.ts.map +1 -1
  95. package/lib/types/domain/Phone/WebRTCPhone.d.ts +22 -22
  96. package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  97. package/lib/types/domain/types.d.ts +0 -4
  98. package/lib/types/domain/types.d.ts.map +1 -1
  99. package/lib/types/index.d.ts +5 -1
  100. package/lib/types/index.d.ts.map +1 -1
  101. package/lib/types/lib/WazoSessionDescriptionHandler.d.ts +3 -3
  102. package/lib/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
  103. package/lib/types/service/CallApi.d.ts +5 -5
  104. package/lib/types/service/CallApi.d.ts.map +1 -1
  105. package/lib/types/simple/Auth.d.ts +6 -1
  106. package/lib/types/simple/Auth.d.ts.map +1 -1
  107. package/lib/types/simple/Phone.d.ts +8 -8
  108. package/lib/types/simple/Phone.d.ts.map +1 -1
  109. package/lib/types/simple/index.d.ts +3 -1
  110. package/lib/types/simple/index.d.ts.map +1 -1
  111. package/lib/types/state-machine/softphone-state-machine.d.ts +1 -2
  112. package/lib/types/state-machine/softphone-state-machine.d.ts.map +1 -1
  113. package/lib/types/state-machine/utils.d.ts +2 -2
  114. package/lib/types/state-machine/utils.d.ts.map +1 -1
  115. package/lib/types/utils/api-requester.d.ts.map +1 -1
  116. package/lib/types/utils/sdp.d.ts +3 -2
  117. package/lib/types/utils/sdp.d.ts.map +1 -1
  118. package/lib/types/utils/webrtc.d.ts +1 -0
  119. package/lib/types/utils/webrtc.d.ts.map +1 -1
  120. package/lib/types/voice/call.d.ts +30 -12
  121. package/lib/types/voice/call.d.ts.map +1 -1
  122. package/lib/types/voice/softphone.d.ts +25 -13
  123. package/lib/types/voice/softphone.d.ts.map +1 -1
  124. package/lib/types/web-rtc-client.d.ts +39 -38
  125. package/lib/types/web-rtc-client.d.ts.map +1 -1
  126. package/lib/utils/__tests__/api-requester.test.js +17 -0
  127. package/lib/utils/api-requester.js +9 -1
  128. package/lib/utils/sdp.js +25 -10
  129. package/lib/utils/webrtc.js +25 -4
  130. package/lib/voice/call.js +131 -36
  131. package/lib/voice/softphone.js +150 -35
  132. package/lib/web-rtc-client.js +59 -57
  133. package/package.json +1 -1
  134. package/src/__tests__/__voice__/call.test.ts +63 -2
  135. package/src/__tests__/api-client.test.ts +22 -1
  136. package/src/api/auth.ts +30 -3
  137. package/src/checker/checks/webrtc-transport.ts +2 -2
  138. package/src/checker/checks/webrtc.ts +2 -2
  139. package/src/domain/CallLog.ts +2 -3
  140. package/src/domain/CallSession.ts +3 -3
  141. package/src/domain/Phone/CTIPhone.ts +3 -2
  142. package/src/domain/Phone/WebRTCPhone.ts +95 -93
  143. package/src/domain/types.ts +0 -5
  144. package/src/index.ts +6 -1
  145. package/src/lib/WazoSessionDescriptionHandler.ts +3 -3
  146. package/src/service/CallApi.ts +7 -7
  147. package/src/simple/Auth.ts +21 -16
  148. package/src/simple/Phone.ts +12 -11
  149. package/src/simple/__tests__/Auth.test.ts +9 -87
  150. package/src/simple/index.ts +2 -1
  151. package/src/simple/room/Room.ts +3 -3
  152. package/src/simple/room/SipRoom.ts +3 -3
  153. package/src/state-machine/call-state-machine.ts +2 -2
  154. package/src/state-machine/softphone-state-machine.ts +1 -2
  155. package/src/state-machine/utils.ts +12 -5
  156. package/src/utils/__tests__/api-requester.test.ts +20 -0
  157. package/src/utils/api-requester.ts +9 -1
  158. package/src/utils/sdp.ts +26 -11
  159. package/src/utils/webrtc.ts +27 -5
  160. package/src/voice/call.ts +169 -50
  161. package/src/voice/softphone.ts +180 -41
  162. package/src/web-rtc-client.ts +106 -99
package/src/voice/call.ts CHANGED
@@ -8,13 +8,16 @@ import { SessionDescriptionHandler } from 'sip.js/lib/platform/web';
8
8
  import { SessionDescriptionHandlerOptions } from 'sip.js/lib/api';
9
9
  import type { IncomingRequestMessage } from 'sip.js/lib/core/messages/incoming-request-message';
10
10
 
11
- import callStateMachine, { type ActionTypes, Actions, type EstablishedActionTypes, EstablishedActions, States, EstablishedStates, type CallActorRef } from '../state-machine/call-state-machine';
11
+ import callStateMachine, { type ActionTypes, Actions, type EstablishedActionTypes, EstablishedActions, States, type StateTypes, EstablishedStates, type EstablishedStateTypes, type CallActorRef } from '../state-machine/call-state-machine';
12
12
  import { SipCall, PeerConnection } from '../domain/types';
13
+ import ApiCall from '../domain/Call';
14
+ import newFrom from '../utils/new-from';
15
+ import updateFrom from '../utils/update-from';
13
16
  import IssueReporter from '../service/IssueReporter';
14
- import { Softphone } from './softphone';
15
- import { getSipSessionId } from '../utils/sdp';
17
+ import softphone, { Softphone } from './softphone';
18
+ import { getCallDisplayName, getCallNumber, getSipCallId } from '../utils/sdp';
16
19
  import { assertCan, assertState, getState } from '../state-machine/utils';
17
- import { downgradeToAudio, getLocalStream, getLocalVideoStream, getPeerConnection, getRemoteStream, getRemoteVideoStream, getStreamFromConstraints, getWebRtcStats, hasALocalVideoTrack, hasLocalVideo, hasRemoteVideo, isVideoRemotelyHeld, setLocalMediaStream, toggleVideo, upgradeToVideo } from '../utils/webrtc';
20
+ import { downgradeToAudio, getLocalStream, getLocalVideoStream, getPeerConnection, getRemoteStream, getRemoteVideoStream, getStreamFromConstraints, getWebRtcStats, hasALocalVideoTrack, hasLocalVideo, hasRemoteVideo, isVideoMuted, isVideoRemotelyHeld, setLocalMediaStream, toggleVideo, upgradeToVideo } from '../utils/webrtc';
18
21
 
19
22
  const logger = IssueReporter.loggerFor('call');
20
23
 
@@ -30,7 +33,8 @@ export type ReinviteOptions = {
30
33
  };
31
34
 
32
35
  // Events
33
- export const EVENT_CALL_ACCEPTED = 'accepted';
36
+ export const EVENT_CALL_REMOTLY_ACCEPTED = 'remotelyAccepted';
37
+ export const EVENT_CALL_LOCALLY_ACCEPTED = 'locallyAccepted';
34
38
  export const EVENT_CALL_CANCELED = 'canceled';
35
39
  export const EVENT_PROGRESS = 'progress';
36
40
  export const EVENT_EARLY_MEDIA = 'earlyMedia';
@@ -53,15 +57,12 @@ export const EVENT_SHARE_SCREEN_ENDED = 'onScreenShareEnded';
53
57
  export const EVENT_WEB_RTC_STATS = 'webRtcStats';
54
58
  export const EVENT_CAMERA_RESUMED = 'onCameraResumed';
55
59
  export const EVENT_CAMERA_DISABLED = 'onCameraDisabled';
56
- export const EVENT_TERMINATE_SOUND = 'terminateSound';
57
60
 
58
61
  export const MESSAGE_TYPE_CHAT = 'message/TYPE_CHAT';
59
62
  export const MESSAGE_TYPE_SIGNAL = 'message/TYPE_SIGNAL';
60
63
  export const MESSAGE_TRACK_UPDATED = 'onTrackUpdated';
61
64
 
62
65
  class Call extends EventEmitter {
63
- eventEmitter: EventEmitter;
64
-
65
66
  callActor: CallActorRef;
66
67
 
67
68
  // Wazo call id, used in Wazo Websocket and API
@@ -81,8 +82,68 @@ class Call extends EventEmitter {
81
82
 
82
83
  shouldSendReinvite = false;
83
84
 
85
+ recording = false;
86
+
87
+ displayName: string | null;
88
+
89
+ number: string | null;
90
+
84
91
  currentScreenShare: Record<string, any> | null;
85
92
 
93
+ static newFrom(call: Call) {
94
+ return newFrom(call, Call);
95
+ }
96
+
97
+ static parseCall(apiCall: ApiCall): Call {
98
+ const sipCall = {
99
+ id: apiCall.sipCallId,
100
+ stateChange: {
101
+ addListener: () => {},
102
+ },
103
+ remoteIdentity: {
104
+ uri: {
105
+ _normal: {
106
+ user: apiCall.calleeNumber,
107
+ },
108
+ },
109
+ },
110
+ // Mock sdk so `hasLocalVideo` will return true.
111
+ sessionDescriptionHandler: {
112
+ localMediaStream: {
113
+ getTracks: () => (apiCall.isVideo ? [{ kind: 'video', readyState: 'live' }] : []),
114
+ },
115
+ },
116
+ } as any;
117
+
118
+ const call = new Call(sipCall, softphone);
119
+ call.apiId = apiCall.id;
120
+ call.answerTime = new Date(+apiCall.startingTime);
121
+
122
+ // Update call state
123
+ const snapshot = call.callActor.getSnapshot();
124
+
125
+ if (apiCall.isUp()) {
126
+ snapshot.value = { [States.ESTABLISHED]: EstablishedStates.ONGOING };
127
+ }
128
+
129
+ if (apiCall.muted) {
130
+ snapshot.value = { [States.ESTABLISHED]: EstablishedStates.MUTED };
131
+ }
132
+
133
+ if (apiCall.isRinging()) {
134
+ snapshot.value = States.RINGING;
135
+ }
136
+
137
+ if (apiCall.isRecording()) {
138
+ call.recording = true;
139
+ }
140
+
141
+ // @ts-ignore
142
+ call.callActor.update(snapshot);
143
+
144
+ return call;
145
+ }
146
+
86
147
  constructor(sipCall: SipCall, phone: Softphone) {
87
148
  super();
88
149
 
@@ -94,7 +155,8 @@ class Call extends EventEmitter {
94
155
  this.phone = phone;
95
156
  this.sipCall = sipCall;
96
157
 
97
- this._bindEvents();
158
+ this.number = getCallNumber(sipCall);
159
+ this.displayName = getCallDisplayName(sipCall);
98
160
  }
99
161
 
100
162
  async accept(options: AcceptOptions = {}): Promise<string | null> {
@@ -111,6 +173,8 @@ class Call extends EventEmitter {
111
173
  return this.phone.client.answer(this.sipCall as Invitation, options.withCamera).then(() => {
112
174
  logger.info('call - accepted', { id: this.id, ...options });
113
175
 
176
+ this.emit(EVENT_CALL_LOCALLY_ACCEPTED, this);
177
+
114
178
  return this.id;
115
179
  }).catch(e => {
116
180
  logger.error(e);
@@ -123,21 +187,23 @@ class Call extends EventEmitter {
123
187
  assertCan(this.callActor, Actions.REJECT);
124
188
  logger.info('call - reject', { id: this.id });
125
189
 
126
- this.eventEmitter.emit(EVENT_TERMINATE_SOUND, 'call rejected locally');
127
-
128
- this.shouldSendReinvite = false;
129
-
130
- return this.hangup();
190
+ return this._terminate();
131
191
  }
132
192
 
133
193
  async hangup(): Promise<boolean> {
134
194
  assertCan(this.callActor, Actions.HANGUP);
135
195
  logger.info('call - hangup', { id: this.id });
136
196
 
197
+ return this._terminate();
198
+ }
199
+
200
+ async _terminate(): Promise<boolean> {
137
201
  await this.phone.client.hangup(this.sipCall);
138
202
 
203
+ this.shouldSendReinvite = false;
204
+
139
205
  // Remove this call from softphone's calls
140
- this.phone.removeCall(this);
206
+ this.phone.onCallTerminated(this);
141
207
 
142
208
  return true;
143
209
  }
@@ -151,7 +217,7 @@ class Call extends EventEmitter {
151
217
  }
152
218
  }
153
219
 
154
- startConference(withCalls: Call[]) {
220
+ startConference(/* withCalls: Call[] */) {
155
221
  // @TODO
156
222
  // const participants = [this, ...withCalls].reduce((acc: Record<string, any>, call: Call) => {
157
223
  // acc[call.talkingToIds[0]] = call;
@@ -174,7 +240,7 @@ class Call extends EventEmitter {
174
240
 
175
241
  this.phone.client.mute(this.sipCall);
176
242
 
177
- this.eventEmitter.emit(EVENT_CALL_MUTED);
243
+ this.emit(EVENT_CALL_MUTED);
178
244
  }
179
245
 
180
246
  unMute() {
@@ -185,7 +251,7 @@ class Call extends EventEmitter {
185
251
 
186
252
  this.phone.client.unmute(this.sipCall);
187
253
 
188
- this.eventEmitter.emit(EVENT_CALL_UN_MUTED);
254
+ this.emit(EVENT_CALL_UN_MUTED);
189
255
  }
190
256
 
191
257
  async hold() {
@@ -208,7 +274,7 @@ class Call extends EventEmitter {
208
274
 
209
275
  const promise = this.phone.client.hold(this.sipCall, this.isConference(), hasVideo);
210
276
 
211
- this.eventEmitter.emit(EVENT_CALL_HELD);
277
+ this.emit(EVENT_CALL_HELD);
212
278
 
213
279
  return promise;
214
280
  }
@@ -244,7 +310,7 @@ class Call extends EventEmitter {
244
310
  }
245
311
  };
246
312
 
247
- this.eventEmitter.emit(EVENT_CALL_RESUMED);
313
+ this.emit(EVENT_CALL_RESUMED);
248
314
 
249
315
  return promise.then(() => {
250
316
  const stream = this.getLocalVideoStream();
@@ -261,7 +327,7 @@ class Call extends EventEmitter {
261
327
  logger.info('call - turn camera on', { id: this.id });
262
328
  toggleVideo(this.sipCall, true);
263
329
 
264
- this.eventEmitter.emit(EVENT_CAMERA_RESUMED);
330
+ this.emit(EVENT_CAMERA_RESUMED);
265
331
  }
266
332
 
267
333
  turnCameraOff(): void {
@@ -269,15 +335,39 @@ class Call extends EventEmitter {
269
335
  logger.info('call - turn camera off', { id: this.id });
270
336
  toggleVideo(this.sipCall, false);
271
337
 
272
- this.eventEmitter.emit(EVENT_CAMERA_DISABLED);
338
+ this.emit(EVENT_CAMERA_DISABLED);
339
+ }
340
+
341
+ isMuted() {
342
+ return this._hasEstablishedState(EstablishedStates.MUTED);
343
+ }
344
+
345
+ isCameraMuted() {
346
+ return isVideoMuted(this.sipCall);
273
347
  }
274
348
 
275
349
  isHeld() {
276
- return getState(this.callActor) === EstablishedStates.HELD;
350
+ return this._hasEstablishedState(EstablishedStates.HELD);
351
+ }
352
+
353
+ isEstablishing() {
354
+ return this.sipCall.state === SessionState.Establishing;
277
355
  }
278
356
 
279
357
  isEstablished() {
280
- return getState(this.callActor) === States.ESTABLISHED;
358
+ return typeof this.state === 'object' && States.ESTABLISHED in (this.state as Record<StateTypes, EstablishedStateTypes>);
359
+ }
360
+
361
+ isRinging() {
362
+ return this.state === States.RINGING;
363
+ }
364
+
365
+ isOutgoing() {
366
+ return this.sipCall instanceof Invitation;
367
+ }
368
+
369
+ isIncoming() {
370
+ return !this.isOutgoing();
281
371
  }
282
372
 
283
373
  isConference(): boolean {
@@ -288,7 +378,7 @@ class Call extends EventEmitter {
288
378
  assertState(this.callActor, States.ESTABLISHED);
289
379
  logger.info('call - transfer', { id: this.id, target });
290
380
 
291
- this.phone.client.transfer(this.sipCall, target);
381
+ return this.phone.client.transfer(this.sipCall, target);
292
382
  }
293
383
 
294
384
  atxfer(): Record<string, any> | null | undefined {
@@ -446,7 +536,7 @@ class Call extends EventEmitter {
446
536
  console.warn(e);
447
537
  }
448
538
 
449
- this.eventEmitter.emit(EVENT_SHARE_SCREEN_ENDED);
539
+ this.emit(EVENT_SHARE_SCREEN_ENDED);
450
540
  this.currentScreenShare = null;
451
541
 
452
542
  return reinvited;
@@ -459,9 +549,15 @@ class Call extends EventEmitter {
459
549
  this._onScreenSharing(localStream as MediaStream, false, desktop);
460
550
  }
461
551
 
552
+ onMakeCall() {
553
+ this._bindEvents();
554
+
555
+ this._sendAction(Actions.MAKE_CALL);
556
+ }
557
+
462
558
  onAccepted() {
463
559
  this._sendAction(Actions.REMOTLY_ACCEPTED);
464
- this.emit(EVENT_CALL_ACCEPTED);
560
+ this.emit(EVENT_CALL_REMOTLY_ACCEPTED);
465
561
  }
466
562
 
467
563
  onProgress() {
@@ -480,12 +576,18 @@ class Call extends EventEmitter {
480
576
  this.emit(EVENT_CALL_ERROR, error);
481
577
  }
482
578
 
579
+ onCallIncoming() {
580
+ this._bindEvents();
581
+
582
+ this._sendAction(Actions.INCOMING_CALL);
583
+ }
584
+
483
585
  onRejected() {
484
586
  this.endTime = new Date();
485
587
 
486
588
  this.emit(EVENT_CALL_REJECTED);
487
589
 
488
- this.phone.removeCall(this);
590
+ this.phone.onCallTerminated(this);
489
591
  }
490
592
 
491
593
  onNetworkStats(stats: Record<string, any>, previousStats: Record<string, any>) {
@@ -528,17 +630,32 @@ class Call extends EventEmitter {
528
630
  this.emit(EVENT_REINVITE, request, updatedCalleeName, updatedNumber, hadRemoteVideo);
529
631
  }
530
632
 
633
+ updateFrom(call: Call) {
634
+ updateFrom(this, call);
635
+ }
636
+
637
+ is(otherCall: Call) {
638
+ return otherCall && this.id && this.id === otherCall.id;
639
+ }
640
+
641
+ shouldAutoAnswer() {
642
+ return !!this.sipCall.request?.getHeader('alert-info');
643
+ }
644
+
531
645
  get id() {
532
- return getSipSessionId(this.sipCall);
646
+ return getSipCallId(this.sipCall);
533
647
  }
534
648
 
535
- get number() {
536
- const identity = this.sipCall.remoteIdentity || this.sipCall.assertedIdentity;
537
- // @ts-ignore: private
538
- return identity ? identity.uri._normal.user : null;
649
+ get state(): StateTypes | Record<StateTypes, EstablishedStateTypes> {
650
+ return getState(this.callActor) as StateTypes | Record<StateTypes, EstablishedStateTypes>;
539
651
  }
540
652
 
541
- _bindEvents() {
653
+ private _hasEstablishedState(establishedState: EstablishedStateTypes) {
654
+ const state = this.state as Record<StateTypes, EstablishedStateTypes>;
655
+ return typeof state === 'object' && States.ESTABLISHED in state && state[States.ESTABLISHED] === establishedState;
656
+ }
657
+
658
+ private _bindEvents() {
542
659
  if (this.sipCall instanceof Invitation) {
543
660
  // Monkey patch to know when canceled with the CANCEL message
544
661
  const onCancel = (this.sipCall as Invitation)._onCancel.bind(this.sipCall);
@@ -548,15 +665,15 @@ class Call extends EventEmitter {
548
665
  onCancel(message);
549
666
  const elsewhere = message.data.indexOf('cause=26') !== -1 && message.data.indexOf('completed elsewhere') !== -1;
550
667
 
551
- this.eventEmitter.emit(EVENT_CALL_CANCELED, elsewhere);
668
+ this.emit(EVENT_CALL_CANCELED, elsewhere);
552
669
  };
553
670
  }
554
671
 
555
- this.sipCall.stateChange.addListener((newState: SessionState) => {
672
+ this.sipCall?.stateChange?.addListener((newState: SessionState) => {
556
673
  switch (newState) {
557
674
  case SessionState.Terminating:
558
675
  logger.info('call - terminating', { id: this.id });
559
- this.eventEmitter.emit(EVENT_CALL_TERMINATING);
676
+ this.emit(EVENT_CALL_TERMINATING);
560
677
  break;
561
678
 
562
679
  case SessionState.Terminated:
@@ -566,7 +683,9 @@ class Call extends EventEmitter {
566
683
  // Should be called before `_onCallTerminated` or the callCount will not decrement...
567
684
  this.endTime = new Date();
568
685
 
569
- return this.eventEmitter.emit(EVENT_CALL_ENDED);
686
+ this.emit(EVENT_CALL_ENDED);
687
+
688
+ return this.phone.onCallTerminated(this);
570
689
  }
571
690
 
572
691
  default:
@@ -599,10 +718,10 @@ class Call extends EventEmitter {
599
718
  });
600
719
 
601
720
  if (kind === 'audio') {
602
- return this.eventEmitter.emit(EVENT_AUDIO_STREAM, stream);
721
+ return this.emit(EVENT_AUDIO_STREAM, stream);
603
722
  }
604
723
 
605
- return this.eventEmitter.emit(EVENT_VIDEO_STREAM, stream, event.track.id, event);
724
+ return this.emit(EVENT_VIDEO_STREAM, stream, event.track.id, event);
606
725
  };
607
726
 
608
727
  peerConnection.onremovestream = (event: any) => {
@@ -610,13 +729,11 @@ class Call extends EventEmitter {
610
729
  id: event.stream.id,
611
730
  tracks: event.stream.getTracks(),
612
731
  });
613
- this.eventEmitter.emit(EVENT_REMOVE_STREAM, event.stream);
732
+ this.emit(EVENT_REMOVE_STREAM, event.stream);
614
733
  };
615
-
616
- // Client events
617
734
  }
618
735
 
619
- _downgradeToAudio(withMessage = true) {
736
+ private _downgradeToAudio(withMessage = true) {
620
737
  assertState(this.callActor, States.ESTABLISHED);
621
738
  logger.info('call - downgrade to audio', { id: this.id, withMessage });
622
739
 
@@ -630,7 +747,7 @@ class Call extends EventEmitter {
630
747
  }
631
748
 
632
749
  // Returns true if we need to send a re-INVITE request
633
- async _upgradeToVideo(options: ReinviteOptions = {}): Promise<boolean> {
750
+ private async _upgradeToVideo(options: ReinviteOptions = {}): Promise<boolean> {
634
751
  assertState(this.callActor, States.ESTABLISHED);
635
752
  logger.info('call - upgrade to video', { id: this.id, ...options });
636
753
 
@@ -660,7 +777,7 @@ class Call extends EventEmitter {
660
777
  return false;
661
778
  }
662
779
 
663
- _sendReinviteMessage(isUpgrade: boolean) {
780
+ private _sendReinviteMessage(isUpgrade: boolean) {
664
781
  assertState(this.callActor, States.ESTABLISHED);
665
782
  logger.info('call - sending reinvite message', { id: this.id, isUpgrade });
666
783
 
@@ -679,7 +796,7 @@ class Call extends EventEmitter {
679
796
  }, 2500);
680
797
  }
681
798
 
682
- _onScreenSharing(screenStream: MediaStream, hadVideo: boolean, desktop?: boolean | null | undefined) {
799
+ private _onScreenSharing(screenStream: MediaStream, hadVideo: boolean, desktop?: boolean | null | undefined) {
683
800
  const screenTrack = screenStream.getVideoTracks()[0];
684
801
  const pc = this.phone.client.getPeerConnection(this.id);
685
802
  const sender = pc && pc.getSenders && pc.getSenders().find((s) => s && s.track && s.track.kind === 'video');
@@ -690,7 +807,7 @@ class Call extends EventEmitter {
690
807
  screenTrack.onended = () => {
691
808
  logger.info('call - on screenshare ended', { hadVideo, id: this.id, screenTrack });
692
809
 
693
- this.eventEmitter.emit(EVENT_SHARE_SCREEN_ENDING);
810
+ this.emit(EVENT_SHARE_SCREEN_ENDING);
694
811
  };
695
812
  }
696
813
 
@@ -702,11 +819,13 @@ class Call extends EventEmitter {
702
819
  };
703
820
 
704
821
  setLocalMediaStream(this.sipCall, screenStream);
705
- this.eventEmitter.emit(EVENT_SHARE_SCREEN_STARTED, screenStream);
822
+ this.emit(EVENT_SHARE_SCREEN_STARTED, screenStream);
706
823
  }
707
824
 
708
- _sendAction(action: ActionTypes | EstablishedActionTypes) {
825
+ private _sendAction(action: ActionTypes | EstablishedActionTypes) {
826
+ const was = this.state;
709
827
  this.callActor.send({ type: action });
828
+ logger.trace('call - send action', { action, was, state: this.state });
710
829
  }
711
830
  }
712
831