@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
@@ -10,21 +10,26 @@ import InvalidState from '../domain/InvalidState';
10
10
 
11
11
  import Wazo from '..';
12
12
  import softphoneStateMachine, { type SoftphoneActorRef, Actions, type StateTypes, States, ActionTypes } from '../state-machine/softphone-state-machine';
13
- import { Actions as CallActions } from '../state-machine/call-state-machine';
14
13
  import { WebRtcConfig, SipCall } from '../domain/types';
15
14
  import WazoWebRTCClient from '../web-rtc-client';
16
15
  import IssueReporter from '../service/IssueReporter';
17
16
  import configureLogger from '../utils/sip-logger';
18
17
  import Call from './call';
19
18
  import { assertCan, can, getState, waitUntilState } from '../state-machine/utils';
19
+ import { getSipCallId } from '../utils/sdp';
20
20
 
21
21
  export const EVENT_INCOMING = 'incoming';
22
+ export const EVENT_OUTGOING = 'outgoing';
22
23
  export const EVENT_HEARTBEAT = 'heartbeat';
23
24
  export const EVENT_HEARTBEAT_TIMEOUT = 'heartbeatTimeout';
24
25
  export const EVENT_REGISTERED = 'registered';
25
26
  export const EVENT_UNREGISTERED = 'unregistered';
26
27
  export const EVENT_DISCONNECTED = 'disconnected';
27
28
  export const EVENT_VIDEO_INPUT_CHANGE = 'videoInputChange';
29
+ export const EVENT_TERMINATE_SOUND = 'terminateSound';
30
+ export const EVENT_PLAY_HANGUP_SOUND = 'playHangupSound';
31
+ export const EVENT_PLAY_RING_SOUND = 'playRingSound';
32
+ export const EVENT_PLAY_INBOUND_CALL_SOUND = 'playInboundCallSound';
28
33
  export const EVENT_ON_MESSAGE = 'message';
29
34
  export const EVENT_ON_CHAT = 'phone/ON_CHAT';
30
35
  export const EVENT_ON_SIGNAL = 'phone/ON_SIGNAL';
@@ -40,6 +45,7 @@ export type CallOptions = {
40
45
  // If audioOnly is set to true, all video stream will be deactivated, even remotes ones.
41
46
  audioOnly?: boolean,
42
47
  conference?: boolean,
48
+ extraHeaders?: Record<string, any>,
43
49
  };
44
50
 
45
51
  const logger = IssueReporter.loggerFor('softphone');
@@ -53,8 +59,6 @@ export class Softphone extends EventEmitter {
53
59
 
54
60
  audioRingVolume: number;
55
61
 
56
- eventEmitter: EventEmitter;
57
-
58
62
  calls: Call[];
59
63
 
60
64
  client: WazoWebRTCClient;
@@ -69,6 +73,8 @@ export class Softphone extends EventEmitter {
69
73
 
70
74
  shouldSendReinvite: boolean;
71
75
 
76
+ ringingEnabled = true;
77
+
72
78
  constructor() {
73
79
  super();
74
80
 
@@ -87,6 +93,8 @@ export class Softphone extends EventEmitter {
87
93
  const server = Wazo.Auth.getHost();
88
94
  const session = Wazo.Auth.getSession();
89
95
 
96
+ logger.info('softphone - connect', { server, session: !!session });
97
+
90
98
  if (!server || !session) {
91
99
  throw new Error('Please connect to the server using `Wazo.Auth.logIn` or `Wazo.Auth.authenticate` before using Room.connect().');
92
100
  }
@@ -103,14 +111,19 @@ export class Softphone extends EventEmitter {
103
111
  return this.connectWithCredentials(server, this.sipLine, session.displayName(), options);
104
112
  }
105
113
 
114
+ async hangupAllCalls() {
115
+ return Promise.all(this.calls.map(call => call.hangup()));
116
+ }
117
+
106
118
  async disconnect() {
107
119
  assertCan(this.softphoneActor, Actions.UNREGISTER);
108
120
  if (!this.client) {
121
+ logger.info('softphone - disconnect, not client to disconnect, bailing');
109
122
  return null;
110
123
  }
111
124
 
112
125
  // Terminate all calls
113
- await Promise.all(this.calls.map(call => call.hangup()));
126
+ await this.hangupAllCalls();
114
127
 
115
128
  return this.client.unregister();
116
129
  }
@@ -127,7 +140,9 @@ export class Softphone extends EventEmitter {
127
140
  ): Promise<void> {
128
141
  assertCan(this.softphoneActor, Actions.REGISTER);
129
142
 
130
- this.softphoneActor.send({ type: Actions.REGISTER });
143
+ logger.info('softphone - connect with credentials', { server, username: sipLine.username, displayName, options: rawOptions });
144
+
145
+ this._sendAction(Actions.REGISTER);
131
146
 
132
147
  const [host, port = 443] = server.split(':');
133
148
  let options = { ...rawOptions };
@@ -179,16 +194,16 @@ export class Softphone extends EventEmitter {
179
194
  return Promise.resolve(null);
180
195
  }
181
196
 
182
- if (this.getState() !== States.UNREGISTERED) {
183
- throw new InvalidState(`Invalid state ${States.UNREGISTERED}`, States.UNREGISTERED);
184
- }
185
-
186
- if (this.getState() !== States.REGISTERING) {
197
+ if (this.state === States.REGISTERING) {
187
198
  logger.info('softphone - calling when registering, waiting for Softphone to be registered ...');
188
199
 
189
200
  await waitUntilState(this.softphoneActor, States.REGISTERED);
190
201
  }
191
202
 
203
+ if (this.state !== States.REGISTERED) {
204
+ throw new InvalidState(`Invalid state ${States.UNREGISTERED}`, States.UNREGISTERED);
205
+ }
206
+
192
207
  // Hold other calls
193
208
  this.holdAllCalls();
194
209
 
@@ -197,6 +212,10 @@ export class Softphone extends EventEmitter {
197
212
  try {
198
213
  sipCall = this.client.call(options.params.To, options.withCamera, options.audioOnly, options.conference, options) as SipCall;
199
214
  const call = new Call(sipCall, this);
215
+ call.onMakeCall();
216
+
217
+ this.calls.push(call);
218
+ this.emit(EVENT_OUTGOING, call);
200
219
 
201
220
  return call;
202
221
  } catch (error: any) {
@@ -209,12 +228,77 @@ export class Softphone extends EventEmitter {
209
228
  this.calls = this.calls.filter((call: Call) => call.id !== callToRemove.id);
210
229
  }
211
230
 
231
+ onCallTerminated(call: Call) {
232
+ logger.info('softphone - on call terminated', { id: call.id });
233
+
234
+ const currentCall = this.getCurrentCall();
235
+ const isCurrentCall = currentCall?.id === call.id;
236
+ const isRinging = call.isRinging();
237
+ const isCurrentIncomingCall = isCurrentCall && isRinging;
238
+ const incomingCalls = this.getIncomingCalls();
239
+ const nbIncomingCalls = incomingCalls.length;
240
+
241
+ // Send an event to terminate ringing sound
242
+ if (isCurrentIncomingCall) {
243
+ setTimeout(() => {
244
+ // Avoid race condition when the other is calling and hanging up immediately
245
+ this.emit(EVENT_TERMINATE_SOUND, call, 'call terminated');
246
+ }, 5);
247
+ }
248
+
249
+ // If the terminated call was an incoming call, we have to re-trigger if it's was the first incoming call
250
+ // Otherwise, we have to re-trigger an incoming call event if another call is incoming
251
+ const shouldRetrigger = isCurrentCall ? nbIncomingCalls : isCurrentIncomingCall;
252
+
253
+ // Re-trigger incoming call event for remaining incoming calls
254
+ logger.info('Softphone - check to re-trigger incoming call', { shouldRetrigger, nbIncomingCalls });
255
+
256
+ if (shouldRetrigger && nbIncomingCalls > 1) {
257
+ const nextCall = incomingCalls.find(c => c.id !== call.id);
258
+ // Avoid race condition
259
+ setTimeout(() => {
260
+ if (this.ringingEnabled) {
261
+ if (currentCall) {
262
+ this.emit(EVENT_PLAY_RING_SOUND, this.audioRingDeviceId, this.audioRingVolume, nextCall);
263
+ } else {
264
+ this.emit(EVENT_PLAY_INBOUND_CALL_SOUND, this.audioOutputDeviceId, this.audioOutputVolume, nextCall);
265
+ }
266
+ }
267
+
268
+ this.emit(EVENT_INCOMING, nextCall);
269
+ }, 100);
270
+ }
271
+
272
+ // @ts-ignore: does not exist
273
+ if (!call.sipCall.isCanceled) {
274
+ setTimeout(() => {
275
+ this.emit(EVENT_PLAY_HANGUP_SOUND, this.audioOutputDeviceId, this.audioOutputVolume, call);
276
+ }, 10);
277
+ }
278
+
279
+ this.client.onCallEnded(call.sipCall);
280
+
281
+ this.removeCall(call);
282
+ }
283
+
284
+ enableRinging(): Promise<void> | void {
285
+ logger.info('softphone - enable ringing');
286
+ this.ringingEnabled = true;
287
+ }
288
+
289
+ disableRinging(): Promise<void> | void {
290
+ logger.info('softphone - disable ringing');
291
+ this.ringingEnabled = false;
292
+ }
293
+
212
294
  holdAllCalls(exceptCall: Call | null = null) {
213
- this.calls.filter(call => !call.isHeld() && call.id === exceptCall?.id).forEach(call => call.hold());
295
+ logger.info('softphone - hold all calls', { exceptId: exceptCall?.id, ids: this.calls.map(call => call.id) });
296
+
297
+ this.calls.filter(call => !call.isHeld() && call.id !== exceptCall?.id).forEach(call => call.hold());
214
298
  }
215
299
 
216
300
  startHeartbeat() {
217
- logger.info('softphone - start heartbeat', { client: !!this.client, hasHeartbeat: this.client.hasHeartbeat() });
301
+ logger.info('softphone - start heartbeat', { client: !!this.client, hasHeartbeat: this.client?.hasHeartbeat() });
218
302
  if (!this.client || this.client.hasHeartbeat()) {
219
303
  return;
220
304
  }
@@ -278,34 +362,64 @@ export class Softphone extends EventEmitter {
278
362
  this.audioRingVolume = volume;
279
363
  }
280
364
 
281
- getCalls(): Call[] {
282
- return this.calls;
365
+ getCurrentCall(): Call | undefined {
366
+ return this.getActiveCalls()[0];
283
367
  }
284
368
 
285
- getCurrentCall(): Call | undefined {
286
- return this.calls.find(call => call.isEstablished());
369
+ getActiveCalls(): Call[] {
370
+ return this.calls.filter(call => call.isEstablished());
371
+ }
372
+
373
+ getIncomingCalls(): Call[] {
374
+ return this.calls.filter(call => call.isRinging());
375
+ }
376
+
377
+ get state(): StateTypes {
378
+ return getState(this.softphoneActor) as StateTypes;
287
379
  }
288
380
 
289
- getState(): StateTypes {
290
- return this.softphoneActor.getSnapshot().value;
381
+ get user() {
382
+ return this.client?.userAgent?.contact?.uri?.user;
291
383
  }
292
384
 
293
385
  getUserAgent() {
294
386
  return this.client?.config?.userAgentString || 'softphone';
295
387
  }
296
388
 
389
+ isRegistering() {
390
+ return this.state === States.REGISTERING;
391
+ }
392
+
393
+ isRegistered() {
394
+ return this.state === States.REGISTERED;
395
+ }
396
+
397
+ isUnRegistered() {
398
+ return this.state === States.UNREGISTERED;
399
+ }
400
+
297
401
  _bindEvents(): void {
298
402
  this.client.on(this.client.INVITE, (sipSession: SipCall) => {
299
403
  const call = new Call(sipSession, this);
300
- call._sendAction(CallActions.INCOMING_CALL);
404
+ logger.info('softphone - on invite', { callId: call.id, number: call.number });
405
+
406
+ call.onCallIncoming();
301
407
  this.calls.push(call);
302
408
 
303
- this.eventEmitter.emit(EVENT_INCOMING, call);
409
+ this.emit(EVENT_INCOMING, call);
410
+
411
+ if (!this.getCurrentCall()) {
412
+ if (this.ringingEnabled) {
413
+ this.emit(EVENT_PLAY_RING_SOUND, this.audioRingDeviceId, this.audioRingVolume, call);
414
+ }
415
+ } else {
416
+ this.emit(EVENT_PLAY_INBOUND_CALL_SOUND, this.audioOutputDeviceId, this.audioOutputVolume, call);
417
+ }
304
418
  });
305
419
 
306
420
  this.client.on(this.client.ON_REINVITE, (sipCall: SipCall, request: IncomingRequestMessage, updatedCalleeName: string, updatedNumber: string, hadRemoteVideo: boolean) => {
307
421
  logger.info('softphone - on reinvite', {
308
- callId: sipCall.id,
422
+ callId: getSipCallId(sipCall),
309
423
  inviteId: request.callId,
310
424
  updatedCalleeName,
311
425
  updatedNumber,
@@ -316,6 +430,9 @@ export class Softphone extends EventEmitter {
316
430
  });
317
431
 
318
432
  this.client.on(this.client.ACCEPTED, async (sipCall: SipCall) => {
433
+ const call = this._getCall(sipCall);
434
+ this.emit(EVENT_TERMINATE_SOUND, call, 'call accepted');
435
+
319
436
  this._onCallEvent(sipCall, 'onAccepted');
320
437
 
321
438
  if (this.audioOutputDeviceId) {
@@ -354,29 +471,39 @@ export class Softphone extends EventEmitter {
354
471
 
355
472
  this.client.on(this.client.UNREGISTERED, () => {
356
473
  logger.info('softphone - unregistered');
357
- this.eventEmitter.emit(EVENT_UNREGISTERED);
474
+ if (!can(this.softphoneActor, Actions.UNREGISTER)) {
475
+ logger.warn('softphone - cannot unregister', { state: this.state });
476
+ return;
477
+ }
358
478
 
359
- this._sendAction(Actions.UNREGISTERED);
479
+ this._sendAction(Actions.UNREGISTER);
480
+
481
+ this.emit(EVENT_UNREGISTERED);
360
482
  });
361
483
 
362
484
  this.client.on(this.client.ON_DISCONNECTED, () => {
363
485
  logger.info('softphone - disconnected');
364
- this.eventEmitter.emit(EVENT_DISCONNECTED);
486
+ if (!can(this.softphoneActor, Actions.TRANSPORT_CLOSED)) {
487
+ logger.warn('softphone - cannot disconnected', { state: this.state });
488
+ return;
489
+ }
365
490
 
366
491
  this._sendAction(Actions.TRANSPORT_CLOSED);
492
+
493
+ this.emit(EVENT_DISCONNECTED);
367
494
  });
368
495
 
369
496
  this.client.on(this.client.REGISTERED, () => {
370
497
  logger.info('softphone - registered', { state: getState(this.softphoneActor) });
371
498
  if (!can(this.softphoneActor, Actions.REGISTER_DONE)) {
372
- logger.warn('softphone - registered but not in REGISTERING state', { state: getState(this.softphoneActor) });
499
+ logger.warn('softphone - registered but not in REGISTERING state', { state: this.state });
373
500
  return;
374
501
  }
375
502
 
376
503
  this.stopHeartbeat();
377
504
  this._sendAction(Actions.REGISTER_DONE);
378
505
 
379
- this.eventEmitter.emit(EVENT_REGISTERED);
506
+ this.emit(EVENT_REGISTERED);
380
507
 
381
508
  // If the phone registered with a current callSession (eg: when switching network):
382
509
  // send a reinvite to renegociate ICE with new IP
@@ -390,13 +517,19 @@ export class Softphone extends EventEmitter {
390
517
  });
391
518
 
392
519
  this.client.on(this.client.CONNECTED, () => {
393
- logger.info('softphone - connected');
520
+ logger.info('softphone - connected', { state: this.state });
394
521
  this.stopHeartbeat();
395
522
  });
396
523
 
524
+ this.client.on(this.client.REGISTERING, () => {
525
+ logger.info('softphone - registering', { state: this.state });
526
+
527
+ this._sendAction(Actions.REGISTER);
528
+ });
529
+
397
530
  this.client.on(this.client.DISCONNECTED, () => {
398
531
  logger.info('softphone - disconnected');
399
- this.eventEmitter.emit(EVENT_DISCONNECTED);
532
+ this.emit(EVENT_DISCONNECTED);
400
533
 
401
534
  // Do not trigger heartbeat if already running
402
535
  if (!this.client.hasHeartbeat()) {
@@ -408,38 +541,44 @@ export class Softphone extends EventEmitter {
408
541
  });
409
542
 
410
543
  this.client.on('onVideoInputChange', stream => {
411
- this.eventEmitter.emit(EVENT_VIDEO_INPUT_CHANGE, stream);
544
+ this.emit(EVENT_VIDEO_INPUT_CHANGE, stream);
412
545
  });
413
546
 
414
547
  this.client.on(this.client.MESSAGE, (message: Message) => {
415
548
  this._onMessage(message);
416
549
 
417
- this.eventEmitter.emit(EVENT_ON_MESSAGE, message);
550
+ this.emit(EVENT_ON_MESSAGE, message);
418
551
  });
419
552
 
420
553
  this.client.setOnHeartbeatTimeout(() => {
421
- this.eventEmitter.emit(EVENT_HEARTBEAT_TIMEOUT);
554
+ this.emit(EVENT_HEARTBEAT_TIMEOUT);
422
555
  });
423
556
 
424
557
  this.client.setOnHeartbeatCallback(() => {
425
- this.eventEmitter.emit(EVENT_HEARTBEAT);
558
+ this.emit(EVENT_HEARTBEAT);
426
559
  });
427
560
  }
428
561
 
429
- _sendAction(action: ActionTypes) {
562
+ private _sendAction(action: ActionTypes) {
563
+ const was = this.state;
430
564
  this.softphoneActor.send({ type: action });
565
+ logger.trace('softphone - send action', { action, was, state: this.state });
566
+ }
567
+
568
+ private _getCall(sipCall: SipCall): Call | undefined {
569
+ return this.calls.find(call => call.id === getSipCallId(sipCall));
431
570
  }
432
571
 
433
- _getCall(sipCall: SipCall): Call | undefined {
434
- return this.calls.find(call => call.id === sipCall.id);
572
+ private _getCallIds(): string[] {
573
+ return this.calls.map(call => call.id);
435
574
  }
436
575
 
437
- _onCallEvent(sipCall: SipCall, eventName: string, ...args: any[]) {
438
- logger.warn('softphone - received call event', { eventName, sipId: sipCall.id });
576
+ private _onCallEvent(sipCall: SipCall, eventName: string, ...args: any[]) {
577
+ logger.info('softphone - received call event', { eventName, sipId: getSipCallId(sipCall) });
439
578
 
440
579
  const call = this._getCall(sipCall);
441
580
  if (!call) {
442
- logger.warn('softphone - no call found to send the event', { eventName, sipId: sipCall.id });
581
+ logger.warn('softphone - no call found to send the event', { eventName, sipId: getSipCallId(sipCall), ids: this._getCallIds() });
443
582
  return;
444
583
  }
445
584
 
@@ -447,7 +586,7 @@ export class Softphone extends EventEmitter {
447
586
  call[eventName](...args);
448
587
  }
449
588
 
450
- _onMessage(message: Message & { method?: string, body?: string }): void {
589
+ private _onMessage(message: Message & { method?: string, body?: string }): void {
451
590
  if (!message || message.method !== 'MESSAGE') {
452
591
  return;
453
592
  }
@@ -467,12 +606,12 @@ export class Softphone extends EventEmitter {
467
606
 
468
607
  switch (type) {
469
608
  case MESSAGE_TYPE_CHAT:
470
- this.eventEmitter.emit(EVENT_ON_CHAT, content);
609
+ this.emit(EVENT_ON_CHAT, content);
471
610
  break;
472
611
 
473
612
  case MESSAGE_TYPE_SIGNAL:
474
613
  {
475
- this.eventEmitter.emit(EVENT_ON_SIGNAL, content);
614
+ this.emit(EVENT_ON_SIGNAL, content);
476
615
  break;
477
616
  }
478
617