@exotel-npm-dev/webrtc-client-sdk 2.0.5 → 3.0.2

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.
@@ -2,22 +2,21 @@ import { Call } from "../api/callAPI/Call";
2
2
  import { DoRegister as DoRegisterRL, UnRegister as UnRegisterRL } from '../api/registerAPI/RegisterListener';
3
3
  import { CallListener } from '../listeners/CallListener';
4
4
  import { ExotelVoiceClientListener } from '../listeners/ExotelVoiceClientListener';
5
- import { SessionListener as SessionListenerSL } from '../listeners/SessionListeners';
5
+ import { SessionListener } from '../listeners/SessionListeners';
6
6
  import { CallController } from "./CallCtrlerDummy";
7
7
 
8
8
  import { closeDiagnostics as closeDiagnosticsDL, initDiagnostics as initDiagnosticsDL, startMicDiagnosticsTest as startMicDiagnosticsTestDL, startNetworkDiagnostics as startNetworkDiagnosticsDL, startSpeakerDiagnosticsTest as startSpeakerDiagnosticsTestDL, stopMicDiagnosticsTest as stopMicDiagnosticsTestDL, stopNetworkDiagnostics as stopNetworkDiagnosticsDL, stopSpeakerDiagnosticsTest as stopSpeakerDiagnosticsTestDL } from '../api/omAPI/DiagnosticsListener';
9
9
 
10
- import { callbacks, registerCallback, sessionCallback } from '../listeners/Callback';
10
+ import { Callback, RegisterCallback, SessionCallback } from '../listeners/Callback';
11
11
  import { webrtcTroubleshooterEventBus } from "./Callback";
12
12
 
13
- import { webrtcSIPPhone } from '@exotel-npm-dev/webrtc-core-sdk';
13
+ import { WebrtcSIPPhone, getLogger } from "@exotel-npm-dev/webrtc-core-sdk";
14
14
  import { CallDetails } from "../api/callAPI/CallDetails";
15
15
  import LogManager from '../api/LogManager.js';
16
-
16
+ const phonePool = new Map();
17
17
  var intervalId;
18
18
  var intervalIDMap = new Map();
19
-
20
- var logger = webrtcSIPPhone.getLogger();
19
+ const logger = getLogger();
21
20
 
22
21
  function sleep(ms) {
23
22
  return new Promise(resolve => setTimeout(resolve, ms));
@@ -27,136 +26,122 @@ function sleep(ms) {
27
26
  * FQDN for fetching IP
28
27
  */
29
28
  function fetchPublicIP(sipAccountInfo) {
30
- var publicIp = "";
31
- const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
32
- pc.createDataChannel('');
33
- pc.createOffer().then(offer => pc.setLocalDescription(offer))
34
- pc.onicecandidate = (ice) => {
35
- if (!ice || !ice.candidate || !ice.candidate.candidate) {
36
- logger.log("all done.");
37
- pc.close();
38
- return "";
39
- }
40
- logger.log("iceCandidate =" + ice.candidate.candidate);
41
- let split = ice.candidate.candidate.split(" ");
42
- if (split[7] === "host") {
43
- logger.log(`fetchPublicIP:Local IP : ${split[4]}`);
44
- } else {
45
- logger.log(`fetchPublicIP:External IP : ${split[4]}`);
46
- publicIp = `${split[4]}`
47
- logger.log("fetchPublicIP:Public IP :" + publicIp);
48
- localStorage.setItem("contactHost", publicIp);
49
- pc.close();
50
- }
51
- };
52
- sleep(500).then(function () {
53
- logger.log("fetchPublicIP: public ip = ", publicIp)
54
- if (publicIp == "") {
55
- sipAccountInfo.contactHost = window.localStorage.getItem('contactHost');
56
- } else {
57
- sipAccountInfo.contactHost = publicIp;
58
- }
29
+ return new Promise((resolve) => {
30
+ var publicIp = "";
31
+ const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
32
+ pc.createDataChannel('');
33
+ pc.createOffer().then(offer => pc.setLocalDescription(offer))
34
+ pc.onicecandidate = (ice) => {
35
+ if (!ice || !ice.candidate || !ice.candidate.candidate) {
36
+ pc.close();
37
+ resolve();
38
+ return;
39
+ }
40
+ logger.log("iceCandidate =" + ice.candidate.candidate);
41
+ let split = ice.candidate.candidate.split(" ");
42
+ if (split[7] === "host") {
43
+ logger.log(`fetchPublicIP:Local IP : ${split[4]}`);
44
+ } else {
45
+ logger.log(`fetchPublicIP:External IP : ${split[4]}`);
46
+ publicIp = `${split[4]}`
47
+ logger.log("fetchPublicIP:Public IP :" + publicIp);
48
+ localStorage.setItem("contactHost", publicIp);
49
+ pc.close();
50
+ resolve();
51
+ }
52
+ };
53
+ setTimeout(() => {
54
+ logger.log("fetchPublicIP: public ip = ", publicIp)
55
+ if (publicIp == "") {
56
+ sipAccountInfo.contactHost = window.localStorage.getItem('contactHost');
57
+ } else {
58
+ sipAccountInfo.contactHost = publicIp;
59
+ }
60
+ resolve();
61
+ }, 1000);
59
62
  });
60
- return;
61
- };
62
-
63
+ }
63
64
 
64
- export function ExDelegationHandler(exClient_) {
65
- var exClient = exClient_;
66
- this.setTestingMode = function (mode) {
65
+ class ExDelegationHandler {
66
+ constructor(exClient) {
67
+ this.exClient = exClient;
68
+ this.sessionCallback = exClient.sessionCallback;
69
+ }
70
+ setTestingMode(mode) {
67
71
  logger.log("delegationHandler: setTestingMode\n");
68
72
  }
69
-
70
- this.onCallStatSipJsSessionEvent = function (ev) {
71
- logger.log("delegationHandler: onCallStatSipJsSessionEvent",ev);
73
+ onCallStatSipJsSessionEvent(ev) {
74
+ logger.log("delegationHandler: onCallStatSipJsSessionEvent", ev);
72
75
  }
73
-
74
- this.sendWebRTCEventsToFSM = function (eventType, sipMethod) {
76
+ sendWebRTCEventsToFSM(eventType, sipMethod) {
75
77
  logger.log("delegationHandler: sendWebRTCEventsToFSM\n");
76
78
  logger.log("delegationHandler: eventType\n", eventType);
77
79
  logger.log("delegationHandler: sipMethod\n", sipMethod);
80
+
78
81
  if (sipMethod == "CONNECTION") {
79
- exClient.registerEventCallback(eventType, exClient.userName)
82
+ this.exClient.registerEventCallback(eventType, this.exClient.userName);
80
83
  } else if (sipMethod == "CALL") {
81
- exClient.callEventCallback(eventType, exClient.callFromNumber, exClient.call)
84
+ this.exClient.callEventCallback(eventType, this.exClient.callFromNumber, this.exClient.call);
82
85
  }
83
86
  }
84
-
85
- this.playBeepTone = function () {
87
+ playBeepTone() {
86
88
  logger.log("delegationHandler: playBeepTone\n");
87
89
  }
88
-
89
- this.onStatPeerConnectionIceGatheringStateChange = function (iceGatheringState) {
90
+ onStatPeerConnectionIceGatheringStateChange(iceGatheringState) {
90
91
  logger.log("delegationHandler: onStatPeerConnectionIceGatheringStateChange\n");
91
- sessionCallback.initializeSession(`ice_gathering_state_${iceGatheringState}`, exClient.callFromNumber);
92
- sessionCallback.triggerSessionCallback();
92
+ this.sessionCallback.initializeSession(`ice_gathering_state_${iceGatheringState}`, this.exClient.callFromNumber);
93
+ this.sessionCallback.triggerSessionCallback();
93
94
  }
94
-
95
- this.onCallStatIceCandidate = function (ev, icestate) {
95
+ onCallStatIceCandidate(ev, icestate) {
96
96
  logger.log("delegationHandler: onCallStatIceCandidate\n");
97
97
  }
98
-
99
- this.onCallStatNegoNeeded = function (icestate) {
98
+ onCallStatNegoNeeded(icestate) {
100
99
  logger.log("delegationHandler: onCallStatNegoNeeded\n");
101
100
  }
102
-
103
- this.onCallStatSignalingStateChange = function (cstate) {
101
+ onCallStatSignalingStateChange(cstate) {
104
102
  logger.log("delegationHandler: onCallStatSignalingStateChange\n");
105
103
  }
106
-
107
- this.onStatPeerConnectionIceConnectionStateChange = function (iceConnectionState) {
104
+ onStatPeerConnectionIceConnectionStateChange(iceConnectionState) {
108
105
  logger.log("delegationHandler: onStatPeerConnectionIceConnectionStateChange\n");
109
- sessionCallback.initializeSession(`ice_connection_state_${iceConnectionState}`, exClient.callFromNumber);
110
- sessionCallback.triggerSessionCallback();
106
+ this.sessionCallback.initializeSession(`ice_connection_state_${iceConnectionState}`, this.exClient.callFromNumber);
107
+ this.sessionCallback.triggerSessionCallback();
111
108
  }
112
-
113
- this.onStatPeerConnectionConnectionStateChange = function () {
109
+ onStatPeerConnectionConnectionStateChange() {
114
110
  logger.log("delegationHandler: onStatPeerConnectionConnectionStateChange\n");
115
111
  }
116
-
117
- this.onGetUserMediaSuccessCallstatCallback = function () {
112
+ onGetUserMediaSuccessCallstatCallback() {
118
113
  logger.log("delegationHandler: onGetUserMediaSuccessCallstatCallback\n");
119
114
  }
120
-
121
- this.onGetUserMediaErrorCallstatCallback = function () {
115
+ onGetUserMediaErrorCallstatCallback() {
122
116
  logger.log("delegationHandler: onGetUserMediaErrorCallstatCallback\n");
123
- sessionCallback.initializeSession(`media_permission_denied`, exClient.callFromNumber);
124
- sessionCallback.triggerSessionCallback();
125
-
117
+ this.sessionCallback.initializeSession(`media_permission_denied`, this.exClient.callFromNumber);
118
+ this.sessionCallback.triggerSessionCallback();
126
119
  }
127
-
128
- this.onCallStatAddStream = function () {
120
+ onCallStatAddStream() {
129
121
  logger.log("delegationHandler: onCallStatAddStream\n");
130
122
  }
131
-
132
- this.onCallStatRemoveStream = function () {
123
+ onCallStatRemoveStream() {
133
124
  logger.log("delegationHandler: onCallStatRemoveStream\n");
134
125
  }
135
-
136
- this.setWebRTCFSMMapper = function (stack) {
126
+ setWebRTCFSMMapper(stack) {
137
127
  logger.log("delegationHandler: setWebRTCFSMMapper : Initialisation complete \n");
138
128
  }
139
-
140
- this.onCallStatSipJsTransportEvent = function () {
129
+ onCallStatSipJsTransportEvent() {
141
130
  logger.log("delegationHandler: onCallStatSipJsTransportEvent\n");
142
131
  }
143
-
144
- this.onCallStatSipSendCallback = function () {
132
+ onCallStatSipSendCallback() {
145
133
  logger.log("delegationHandler: onCallStatSipSendCallback\n");
146
134
  }
147
-
148
- this.onCallStatSipRecvCallback = function () {
135
+ onCallStatSipRecvCallback() {
149
136
  logger.log("delegationHandler: onCallStatSipRecvCallback\n");
150
137
  }
151
-
152
- this.stopCallStat = function () {
138
+ stopCallStat() {
153
139
  logger.log("delegationHandler: stopCallStat\n");
154
140
  }
155
-
156
- this.onRecieveInvite = function (incomingSession) {
141
+ onRecieveInvite(incomingSession) {
157
142
  logger.log("delegationHandler: onRecieveInvite\n");
158
143
  const obj = incomingSession.incomingInviteRequest.message.headers;
159
- exClient.callFromNumber = incomingSession.incomingInviteRequest.message.from.displayName;
144
+ this.exClient.callFromNumber = incomingSession.incomingInviteRequest.message.from.displayName;
160
145
  if (obj.hasOwnProperty("X-Exotel-Callsid")) {
161
146
  CallDetails.callSid = obj['X-Exotel-Callsid'][0].raw;
162
147
  }
@@ -178,45 +163,41 @@ export function ExDelegationHandler(exClient_) {
178
163
  }
179
164
  CallDetails.sipHeaders = result;
180
165
  }
181
-
182
- this.onPickCall = function () {
166
+ onPickCall() {
183
167
  logger.log("delegationHandler: onPickCall\n");
184
168
  }
185
-
186
- this.onRejectCall = function () {
169
+ onRejectCall() {
187
170
  logger.log("delegationHandler: onRejectCall\n");
188
171
  }
189
-
190
- this.onCreaterAnswer = function () {
172
+ onCreaterAnswer() {
191
173
  logger.log("delegationHandler: onCreaterAnswer\n");
192
174
  }
193
-
194
- this.onSettingLocalDesc = function () {
175
+ onSettingLocalDesc() {
195
176
  logger.log("delegationHandler: onSettingLocalDesc\n");
196
177
  }
197
-
198
- this.initGetStats = function (pc, callid, username) {
178
+ initGetStats(pc, callid, username) {
199
179
  logger.log("delegationHandler: initGetStats\n");
200
180
  }
201
-
202
- this.onRegisterWebRTCSIPEngine = function (engine) {
181
+ onRegisterWebRTCSIPEngine(engine) {
203
182
  logger.log("delegationHandler: onRegisterWebRTCSIPEngine, engine=\n", engine);
204
183
  }
205
184
  }
206
185
 
207
- export function ExSynchronousHandler() {
208
-
209
- this.onFailure = function () {
186
+ class ExSynchronousHandler {
187
+ onFailure() {
210
188
  logger.log("synchronousHandler: onFailure, phone is offline.\n");
211
189
  }
212
-
213
- this.onResponse = function () {
190
+ onResponse() {
214
191
  logger.log("synchronousHandler: onResponse, phone is connected.\n");
215
192
  }
216
193
  }
217
194
 
218
- export class ExotelWebClient {
195
+ export { ExDelegationHandler, ExSynchronousHandler };
219
196
 
197
+ export class ExotelWebClient {
198
+ /**
199
+ * @param {Object} sipAccntInfo
200
+ */
220
201
 
221
202
 
222
203
  ctrlr = null;
@@ -228,45 +209,74 @@ export class ExotelWebClient {
228
209
  unregisterInitiated = false;
229
210
  registrationInProgress = false;
230
211
  isReadyToRegister = true;
231
- /* OLD-Way to be revisited for multile phone support */
232
- //this.webRTCPhones = {};
212
+
233
213
 
234
214
  sipAccountInfo = null;
235
215
  clientSDKLoggerCallback = null;
236
-
216
+ callbacks = null;
217
+ registerCallback = null;
218
+ sessionCallback = null;
219
+ logger = getLogger();
220
+
237
221
  constructor() {
238
- /*
239
- Register the logger callback and emit the onLog event
240
- */
241
-
222
+ // Initialize properties
223
+ this.ctrlr = null;
224
+ this.call = null;
225
+ this.eventListener = null;
226
+ this.callListener = null;
227
+ this.callFromNumber = null;
228
+ this.shouldAutoRetry = false;
229
+ this.unregisterInitiated = false;
230
+ this.registrationInProgress = false;
231
+ this.currentSIPUserName = "";
232
+ this.isReadyToRegister = true;
233
+ this.sipAccountInfo = null;
234
+ this.clientSDKLoggerCallback = null;
235
+ this.callbacks = new Callback();
236
+ this.registerCallback = new RegisterCallback();
237
+ this.sessionCallback = new SessionCallback();
238
+ this.logger = getLogger();
239
+
240
+ // Register logger callback
242
241
  let exwebClientOb = this;
243
- logger.registerLoggerCallback(function (type, message, args) {
244
-
242
+ this.logger.registerLoggerCallback((type, message, args) => {
245
243
  LogManager.onLog(type, message, args);
246
- if (exwebClientOb.clientSDKLoggerCallback)
244
+ if (exwebClientOb.clientSDKLoggerCallback) {
247
245
  exwebClientOb.clientSDKLoggerCallback("log", message, args);
248
-
246
+ }
249
247
  });
250
- }
248
+ }
251
249
 
252
250
 
253
- initWebrtc = (sipAccountInfo_,
251
+ initWebrtc = async (sipAccountInfo_,
254
252
  RegisterEventCallBack, CallListenerCallback, SessionCallback, enableAutoAudioDeviceChangeHandling=false) => {
253
+ const userName = sipAccountInfo_?.userName;
254
+ if (!userName) return false;
255
+
256
+ // --- Duplicate registration guard ---
257
+ if (phonePool.has(userName)) {
258
+ if (this.currentSIPUserName == "" || this.currentSIPUserName !== userName) {
259
+ logger.warn(`ExWebClient: initWebrtc: [Dup‑Reg] ${userName} already in use – init rejected`);
260
+ return false;
261
+ }
262
+ }
263
+ this.currentSIPUserName = userName;
264
+ phonePool.set(userName, null);
255
265
 
256
266
  if (!this.eventListener) {
257
- this.eventListener = new ExotelVoiceClientListener();
267
+ this.eventListener = new ExotelVoiceClientListener(this.registerCallback);
258
268
  }
259
269
 
260
270
  if (!this.callListener) {
261
- this.callListener = new CallListener();
271
+ this.callListener = new CallListener(this.callbacks);
262
272
  }
263
273
 
264
- if (!this.ctrlr) {
265
- this.ctrlr = new CallController();
274
+ if (!this.sessionListener) {
275
+ this.sessionListener = new SessionListener(this.sessionCallback);
266
276
  }
267
277
 
268
- if (!this.call) {
269
- this.call = new Call();
278
+ if (!this.ctrlr) {
279
+ this.ctrlr = new CallController();
270
280
  }
271
281
 
272
282
  sipAccountInfo_.enableAutoAudioDeviceChangeHandling = enableAutoAudioDeviceChangeHandling;
@@ -276,12 +286,37 @@ export class ExotelWebClient {
276
286
  return false;
277
287
  }
278
288
  this.sipAccountInfo["sipUri"] = "wss://" + this.sipAccountInfo["userName"] + "@" + this.sipAccountInfo["sipdomain"] + ":" + this.sipAccountInfo["port"];
279
-
280
- callbacks.initializeCallback(CallListenerCallback);
281
- registerCallback.initializeRegisterCallback(RegisterEventCallBack);
289
+
290
+ // Register callbacks using the correct methods
291
+ this.callbacks.registerCallback('call', CallListenerCallback);
292
+ this.registerCallback.initializeRegisterCallback(RegisterEventCallBack);
282
293
  logger.log("ExWebClient: initWebrtc: Initializing session callback")
283
- sessionCallback.initializeSessionCallback(SessionCallback);
294
+ this.sessionCallback.initializeSessionCallback(SessionCallback);
284
295
  this.setEventListener(this.eventListener);
296
+
297
+ // Wait for public IP before registering
298
+ await fetchPublicIP(this.sipAccountInfo);
299
+
300
+ // Create phone instance if it wasn't created in constructor
301
+ if (!this.phone) {
302
+ this.userName = this.sipAccountInfo.userName;
303
+ let phone = phonePool.get(this.userName);
304
+ if (!phone) {
305
+ phone = new WebrtcSIPPhone(this.userName);
306
+ phonePool.set(this.userName, phone);
307
+ }
308
+ this.phone = phone;
309
+ this.webrtcSIPPhone = this.phone;
310
+ }
311
+
312
+ // Initialize the phone with SIP engine
313
+ this.webrtcSIPPhone.registerPhone("sipjs", new ExDelegationHandler(this), this.sipAccountInfo.enableAutoAudioDeviceChangeHandling);
314
+
315
+ // Create call instance after phone is initialized
316
+ if (!this.call) {
317
+ this.call = new Call(this.webrtcSIPPhone);
318
+ }
319
+
285
320
  return true;
286
321
  };
287
322
 
@@ -309,11 +344,11 @@ export class ExotelWebClient {
309
344
  };
310
345
 
311
346
  startSpeakerDiagnosticsTest = () => {
312
- startSpeakerDiagnosticsTestDL()
347
+ startSpeakerDiagnosticsTestDL(this.webrtcSIPPhone);
313
348
  };
314
349
 
315
350
  stopSpeakerDiagnosticsTest = (speakerTestResponse = 'none') => {
316
- stopSpeakerDiagnosticsTestDL(speakerTestResponse)
351
+ stopSpeakerDiagnosticsTestDL(speakerTestResponse, this.webrtcSIPPhone);
317
352
  };
318
353
 
319
354
  startMicDiagnosticsTest = () => {
@@ -333,13 +368,9 @@ export class ExotelWebClient {
333
368
  stopNetworkDiagnosticsDL()
334
369
  };
335
370
 
336
- SessionListener = () => {
337
- SessionListenerSL()
371
+ SessionListenerMethod = () => {
338
372
  };
339
373
 
340
- /**
341
- * function that returns the instance of the call controller object object
342
- */
343
374
 
344
375
  getCallController = () => {
345
376
  return this.ctrlr;
@@ -347,14 +378,12 @@ export class ExotelWebClient {
347
378
 
348
379
  getCall = () => {
349
380
  if (!this.call) {
350
- this.call = call = new Call();
381
+ this.call = new Call(this.webrtcSIPPhone);
351
382
  }
352
383
  return this.call;
353
384
  };
354
385
 
355
- /**
356
- * Dummy function to set the event listener object
357
- */
386
+
358
387
  setEventListener = (eventListener) => {
359
388
  this.eventListener = eventListener;
360
389
  };
@@ -368,38 +397,21 @@ export class ExotelWebClient {
368
397
  */
369
398
 
370
399
  registerEventCallback = (event, phone, param) => {
400
+ logger.log("ExWebClient: registerEventCallback: Received ---> " +
401
+ event, [phone, param]);
402
+
403
+ const lowerCaseEvent = event.toLowerCase();
371
404
 
372
- logger.log("ExWebClient: registerEventCallback: Received ---> " + event + 'phone....', phone + 'param....', param)
373
- if (event === "connected") {
374
- /**
375
- * When registration is successful then send the phone number of the same to UI
376
- */
377
- this.eventListener.onInitializationSuccess(phone);
405
+ if (lowerCaseEvent === "registered") {
378
406
  this.registrationInProgress = false;
379
- if (this.unregisterInitiated) {
380
- logger.log("ExWebClient: registerEventCallback: unregistering due to unregisterInitiated");
381
- this.unregisterInitiated = false;
382
- this.unregister();
383
- }
384
- } else if (event === "failed_to_start" || event === "transport_error") {
385
- /**
386
- * If registration fails
387
- */
388
- this.eventListener.onInitializationFailure(phone);
389
- if (this.unregisterInitiated) {
390
- this.shouldAutoRetry = false;
391
- this.unregisterInitiated = false;
392
- this.isReadyToRegister = true;
393
- }
394
- if (this.shouldAutoRetry) {
395
- logger.log("ExWebClient: registerEventCallback: Autoretrying");
396
- DoRegisterRL(this.sipAccountInfo, this, 5000);
397
- }
398
- } else if (event === "sent_request") {
399
- /**
400
- * If registration request waiting...
401
- */
402
- this.eventListener.onInitializationWaiting(phone);
407
+ this.unregisterInitiated = false;
408
+ this.isReadyToRegister = false;
409
+ this.eventListener.onRegistrationStateChanged("registered", phone);
410
+ } else if (lowerCaseEvent === "unregistered" || lowerCaseEvent === "terminated") {
411
+ this.registrationInProgress = false;
412
+ this.unregisterInitiated = false;
413
+ this.isReadyToRegister = true;
414
+ this.eventListener.onRegistrationStateChanged("unregistered", phone);
403
415
  }
404
416
  };
405
417
  /**
@@ -411,12 +423,17 @@ export class ExotelWebClient {
411
423
  callEventCallback = (event, phone, param) => {
412
424
  logger.log("ExWebClient: callEventCallback: Received ---> " + event + 'param sent....' + param + 'for phone....' + phone)
413
425
  if (event === "i_new_call") {
414
- this.callListener.onIncomingCall(param, phone)
426
+ if (!this.call) {
427
+ this.call = new Call(param); // param is the session
428
+ }
429
+ this.callListener.onIncomingCall(param, phone);
430
+ } else if (event === "ringing" || event === "accept_reject") {
431
+ this.callListener.onRinging(param, phone);
415
432
  } else if (event === "connected") {
416
433
  this.callListener.onCallEstablished(param, phone);
417
434
  } else if (event === "terminated") {
418
435
  this.callListener.onCallEnded(param, phone);
419
- }
436
+ }
420
437
  };
421
438
 
422
439
  /**
@@ -438,20 +455,22 @@ export class ExotelWebClient {
438
455
  this.shouldAutoRetry = false;
439
456
  this.unregisterInitiated = true;
440
457
  if (!this.registrationInProgress) {
441
- setTimeout(function () {
442
- webrtcSIPPhone.sipUnRegisterWebRTC();
443
- }, 500);
458
+ setTimeout(() => {
459
+ const phone = phonePool[this.userName] || this.webrtcSIPPhone;
460
+ if (phone) {
461
+ phone.sipUnRegisterWebRTC();
462
+ phone.disconnect?.();
463
+ }
464
+ }, 500);
444
465
  }
445
- };
446
-
466
+ };
467
+
447
468
 
448
469
  webRTCStatusCallbackHandler = (msg1, arg1) => {
449
470
  logger.log("ExWebClient: webRTCStatusCallbackHandler: " + msg1 + " " + arg1)
450
471
  };
451
472
 
452
- /**
453
- * initialize function called when user wants to register client
454
- */
473
+
455
474
  initialize = (uiContext, hostName, subscriberName,
456
475
  displayName, accountSid, subscriberToken,
457
476
  sipAccountInfo) => {
@@ -479,7 +498,6 @@ export class ExotelWebClient {
479
498
 
480
499
  fetchPublicIP(sipAccountInfo);
481
500
 
482
- /* Temporary till we figure out the arguments - Start */
483
501
  this.domain = hostName = sipAccountInfo.domain;
484
502
  this.sipdomain = sipAccountInfo.sipdomain;
485
503
  this.accountName = this.userName = sipAccountInfo.userName;
@@ -495,9 +513,7 @@ export class ExotelWebClient {
495
513
  this.sipWsPort = 5061;
496
514
  this.sipPort = 5061;
497
515
  this.sipSecurePort = 5062;
498
- /* Temporary till we figure out the arguments - End */
499
516
 
500
- /* This is permanent -Start */
501
517
  let webrtcPort = wssPort;
502
518
 
503
519
  if (this.security === 'ws') {
@@ -518,38 +534,28 @@ export class ExotelWebClient {
518
534
  this.sipAccntInfo['port'] = webrtcPort;
519
535
  this.sipAccntInfo['contactHost'] = this.contactHost;
520
536
  localStorage.setItem('contactHost', this.contactHost);
521
- /* This is permanent -End */
522
537
 
523
- /**
524
- * Call the webclient function inside this and pass register and call callbacks as arg
525
- */
526
- var synchronousHandler = new ExSynchronousHandler(this);
538
+
539
+ var synchronousHandler = new ExSynchronousHandler();
527
540
  var delegationHandler = new ExDelegationHandler(this);
528
541
 
529
542
  var userName = this.userName;
530
- /* OLD-Way to be revisited for multile phone support */
531
- //webRTCPhones[userName] = webRTC;
532
543
 
533
- /* New-Way */
534
- webrtcSIPPhone.registerPhone("sipjs", delegationHandler, sipAccountInfo.enableAutoAudioDeviceChangeHandling);
535
- webrtcSIPPhone.registerWebRTCClient(this.sipAccntInfo, synchronousHandler);
536
544
 
537
- /**
538
- * Store the intervalID against a map
539
- */
545
+ this.webrtcSIPPhone.registerPhone("sipjs", delegationHandler, this.sipAccountInfo.enableAutoAudioDeviceChangeHandling);
546
+ this.webrtcSIPPhone.registerWebRTCClient(this.sipAccntInfo, synchronousHandler);
547
+ phonePool[this.userName] = this.webrtcSIPPhone;
548
+
549
+
540
550
  intervalIDMap.set(userName, intervalId);
541
551
  };
542
552
 
543
553
  checkClientStatus = (callback) => {
544
- // using this function , first it will check mic permission is given or not
545
- // then it will check if transport is intialize or not
546
- // then it will check if user is registered or not
547
- // based on this we can evaludate SDK is ready for call
548
554
  var constraints = { audio: true, video: false };
549
555
  navigator.mediaDevices
550
556
  .getUserMedia(constraints)
551
557
  .then(function (mediaStream) {
552
- var transportState = webrtcSIPPhone.getTransportState();
558
+ var transportState = this.webrtcSIPPhone.getTransportState();
553
559
  transportState = transportState.toLowerCase();
554
560
  switch (transportState) {
555
561
  case "":
@@ -561,7 +567,7 @@ export class ExotelWebClient {
561
567
  break;
562
568
 
563
569
  default:
564
- var registerationState = webrtcSIPPhone.getRegistrationState();
570
+ var registerationState = this.webrtcSIPPhone.getRegistrationState();
565
571
  registerationState = registerationState.toLowerCase();
566
572
  switch (registerationState) {
567
573
  case "":
@@ -590,12 +596,12 @@ export class ExotelWebClient {
590
596
 
591
597
  changeAudioInputDevice(deviceId, onSuccess, onError, forceDeviceChange = false) {
592
598
  logger.log(`ExWebClient: changeAudioInputDevice: Entry`);
593
- webrtcSIPPhone.changeAudioInputDevice(deviceId, onSuccess, onError, forceDeviceChange);
599
+ this.webrtcSIPPhone.changeAudioInputDevice(deviceId, onSuccess, onError, forceDeviceChange);
594
600
  }
595
601
 
596
602
  changeAudioOutputDevice(deviceId, onSuccess, onError, forceDeviceChange = false) {
597
603
  logger.log(`ExWebClient: changeAudioOutputDevice: Entry`);
598
- webrtcSIPPhone.changeAudioOutputDevice(deviceId, onSuccess, onError, forceDeviceChange);
604
+ this.webrtcSIPPhone.changeAudioOutputDevice(deviceId, onSuccess, onError, forceDeviceChange);
599
605
  }
600
606
 
601
607
  downloadLogs() {
@@ -605,7 +611,11 @@ export class ExotelWebClient {
605
611
 
606
612
  setPreferredCodec(codecName) {
607
613
  logger.log("ExWebClient: setPreferredCodec: Entry");
608
- webrtcSIPPhone.setPreferredCodec(codecName);
614
+ if (!this.webrtcSIPPhone || !this.webrtcSIPPhone.phone) {
615
+ logger.warn("ExWebClient: setPreferredCodec: Phone not initialized");
616
+ return;
617
+ }
618
+ this.webrtcSIPPhone.setPreferredCodec(codecName);
609
619
  }
610
620
 
611
621
  registerLoggerCallback(callback) {
@@ -613,7 +623,12 @@ export class ExotelWebClient {
613
623
  }
614
624
 
615
625
  registerAudioDeviceChangeCallback(audioInputDeviceChangeCallback, audioOutputDeviceChangeCallback, onDeviceChangeCallback) {
616
- webrtcSIPPhone.registerAudioDeviceChangeCallback(audioInputDeviceChangeCallback, audioOutputDeviceChangeCallback, onDeviceChangeCallback);
626
+ logger.log("ExWebClient: registerAudioDeviceChangeCallback: Entry");
627
+ if (!this.webrtcSIPPhone) {
628
+ logger.warn("ExWebClient: registerAudioDeviceChangeCallback: webrtcSIPPhone not initialized");
629
+ return;
630
+ }
631
+ this.webrtcSIPPhone.registerAudioDeviceChangeCallback(audioInputDeviceChangeCallback, audioOutputDeviceChangeCallback, onDeviceChangeCallback);
617
632
  }
618
633
 
619
634
  setEnableConsoleLogging(enable) {
@@ -623,6 +638,7 @@ export class ExotelWebClient {
623
638
 
624
639
  logger.setEnableConsoleLogging(enable);
625
640
  }
641
+
626
642
  }
627
643
 
628
644
  export default ExotelWebClient;