@vgroup/dialbox 0.7.56 → 0.7.57
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.
- package/esm2020/lib/components/call-progress/call-progress.component.mjs +8 -2
- package/esm2020/lib/dialbox.component.mjs +7 -27
- package/esm2020/lib/service/twilio.service.mjs +86 -1
- package/fesm2015/vgroup-dialbox.mjs +101 -32
- package/fesm2015/vgroup-dialbox.mjs.map +1 -1
- package/fesm2020/vgroup-dialbox.mjs +98 -27
- package/fesm2020/vgroup-dialbox.mjs.map +1 -1
- package/lib/dialbox.component.d.ts +1 -1
- package/lib/service/twilio.service.d.ts +6 -0
- package/package.json +1 -1
- package/vgroup-dialbox-0.7.50.tgz +0 -0
|
@@ -1487,11 +1487,23 @@ class TwilioService {
|
|
|
1487
1487
|
this.pendingIncomingDevices = new Map();
|
|
1488
1488
|
this.extraDevices = new Map();
|
|
1489
1489
|
this.incomingCallsMap = new Map();
|
|
1490
|
+
this.tokenRefreshInProgress = false;
|
|
1491
|
+
this.recoveryAttempts = 0;
|
|
1492
|
+
this.recoveryTimer = null;
|
|
1490
1493
|
// User-controlled switch (in the call-progress UI). When ON, a new incoming
|
|
1491
1494
|
// call's built-in Twilio ringtone is silenced so it does not disturb the
|
|
1492
1495
|
// ongoing call. Source of truth so newly created (spare) devices inherit it.
|
|
1493
1496
|
this.isIncomingMuted = false;
|
|
1494
1497
|
this.initializeToken();
|
|
1498
|
+
// Safety net for long idle/sleep periods: the signaling socket can die silently
|
|
1499
|
+
// (OS suspends timers, tab is backgrounded) without ever firing 'error'/'unregistered'.
|
|
1500
|
+
// Re-check device health whenever the app becomes active/online again.
|
|
1501
|
+
document.addEventListener('visibilitychange', () => {
|
|
1502
|
+
if (document.visibilityState === 'visible') {
|
|
1503
|
+
this.ensureDeviceHealthy();
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
window.addEventListener('online', () => this.ensureDeviceHealthy());
|
|
1495
1507
|
}
|
|
1496
1508
|
initializeToken() {
|
|
1497
1509
|
if (this.tokenInitialized) {
|
|
@@ -1534,6 +1546,14 @@ class TwilioService {
|
|
|
1534
1546
|
this.device = new Device(this.incomingCallToken, { allowIncomingWhileBusy: true });
|
|
1535
1547
|
this.device.on('registered', () => {
|
|
1536
1548
|
console.log('Twilio Device registered successfully');
|
|
1549
|
+
this.recoveryAttempts = 0;
|
|
1550
|
+
});
|
|
1551
|
+
// Twilio fires this ~3 minutes before the access token expires. Swap in a
|
|
1552
|
+
// fresh one via updateToken() so the signaling connection never has to drop
|
|
1553
|
+
// in the first place (this is the main cause of "idle for a while, BE still
|
|
1554
|
+
// notifies but Twilio incoming event never fires").
|
|
1555
|
+
this.device.on('tokenWillExpire', () => {
|
|
1556
|
+
this.refreshDeviceToken();
|
|
1537
1557
|
});
|
|
1538
1558
|
this.device.on('incoming', (call) => {
|
|
1539
1559
|
if (this.acceptedCallList.length > 0 || this.primaryHasCall) {
|
|
@@ -1574,16 +1594,81 @@ class TwilioService {
|
|
|
1574
1594
|
console.error('Twilio Device Error:', error);
|
|
1575
1595
|
this.isShowIncomingCall = false;
|
|
1576
1596
|
this.tokenInitialized = false;
|
|
1597
|
+
this.scheduleDeviceRecovery();
|
|
1577
1598
|
});
|
|
1578
1599
|
this.device.on('unregistered', () => {
|
|
1579
1600
|
console.log('Twilio Device unregistered');
|
|
1580
1601
|
this.isShowIncomingCall = false;
|
|
1581
1602
|
this.tokenInitialized = false;
|
|
1603
|
+
this.scheduleDeviceRecovery();
|
|
1582
1604
|
});
|
|
1583
1605
|
// Register AFTER attaching all listeners
|
|
1584
1606
|
this.device.register();
|
|
1585
1607
|
console.log('device created', this.device);
|
|
1586
1608
|
}
|
|
1609
|
+
// Proactively swap in a fresh access token ahead of expiry (see 'tokenWillExpire'
|
|
1610
|
+
// above) so the Device's signaling connection survives long idle periods.
|
|
1611
|
+
refreshDeviceToken() {
|
|
1612
|
+
if (this.tokenRefreshInProgress) {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
this.tokenRefreshInProgress = true;
|
|
1616
|
+
this.extensionService.getIncomingCallToken(this.deviceId).subscribe({
|
|
1617
|
+
next: (data) => {
|
|
1618
|
+
this.tokenRefreshInProgress = false;
|
|
1619
|
+
this.incomingCallToken = data.token;
|
|
1620
|
+
localStorage.setItem('in-token', data.token);
|
|
1621
|
+
this.tokenInitialized = true;
|
|
1622
|
+
try {
|
|
1623
|
+
this.device?.updateToken(this.incomingCallToken);
|
|
1624
|
+
}
|
|
1625
|
+
catch (err) {
|
|
1626
|
+
console.error('Failed to apply refreshed Twilio token:', err);
|
|
1627
|
+
}
|
|
1628
|
+
},
|
|
1629
|
+
error: (err) => {
|
|
1630
|
+
this.tokenRefreshInProgress = false;
|
|
1631
|
+
console.error('Failed to refresh Twilio token ahead of expiry:', err);
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
// Self-heal after the Device dies (expired token, dropped signaling socket while
|
|
1636
|
+
// idle/asleep, etc). Never runs while a call is actually active, and backs off so a
|
|
1637
|
+
// genuinely logged-out/offline user doesn't retry forever.
|
|
1638
|
+
scheduleDeviceRecovery() {
|
|
1639
|
+
if (this.acceptedCallList.length > 0 || this.recoveryTimer) {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
const delay = Math.min(30000, 2000 * Math.pow(2, this.recoveryAttempts));
|
|
1643
|
+
this.recoveryAttempts++;
|
|
1644
|
+
this.recoveryTimer = setTimeout(() => {
|
|
1645
|
+
this.recoveryTimer = null;
|
|
1646
|
+
const authToken = localStorage.getItem('ext_token');
|
|
1647
|
+
if (!authToken || authToken === 'logout') {
|
|
1648
|
+
this.recoveryAttempts = 0;
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
this.tokenInitialized = false;
|
|
1652
|
+
this.initializeToken().subscribe({
|
|
1653
|
+
error: (err) => console.error('Twilio device recovery attempt failed:', err)
|
|
1654
|
+
});
|
|
1655
|
+
}, delay);
|
|
1656
|
+
}
|
|
1657
|
+
// Safety net for the case where the underlying socket dies silently during a long
|
|
1658
|
+
// sleep/idle period without ever firing 'error'/'unregistered' (the OS can suspend
|
|
1659
|
+
// timers/sockets outright) — re-check device health whenever the app wakes up.
|
|
1660
|
+
ensureDeviceHealthy() {
|
|
1661
|
+
if (this.acceptedCallList.length > 0) {
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
if (!this.device || this.device.state !== 'registered') {
|
|
1665
|
+
console.log('Twilio device unhealthy after wake/visibility change, re-initializing');
|
|
1666
|
+
this.tokenInitialized = false;
|
|
1667
|
+
this.initializeToken().subscribe({
|
|
1668
|
+
error: (err) => console.error('Twilio device recovery failed:', err)
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1587
1672
|
saveContact(payload) {
|
|
1588
1673
|
const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Auth-Key': "Bearer " + localStorage.getItem('ext_token') }) };
|
|
1589
1674
|
return this.http.post(environment.apiUrl + '/utilities/phonebook/add/contacts/manually', payload, httpOptions);
|
|
@@ -2769,6 +2854,9 @@ class CallProgressComponent {
|
|
|
2769
2854
|
deviceId: this.deviceId,
|
|
2770
2855
|
clientSid: this.call?.parameters['CallSid']
|
|
2771
2856
|
});
|
|
2857
|
+
if (addClientParticipantRes?.status != 200) {
|
|
2858
|
+
swal("Error", response?.message.join("<br/>"), "error");
|
|
2859
|
+
}
|
|
2772
2860
|
this.addRes = await this.addParticipantToCall({
|
|
2773
2861
|
from: callData?.from,
|
|
2774
2862
|
route: "OUTGOING",
|
|
@@ -3209,10 +3297,13 @@ class CallProgressComponent {
|
|
|
3209
3297
|
mute: ourNumberInfo?.mute || false
|
|
3210
3298
|
};
|
|
3211
3299
|
this.extensionService.holdParticipant(payload).subscribe((res) => {
|
|
3300
|
+
console.log('holdParticipant res', res);
|
|
3212
3301
|
if (res?.status != 200) {
|
|
3213
3302
|
swal("Error", res?.message?.join?.("<br/>") || 'Internal Server Error', "error");
|
|
3214
3303
|
}
|
|
3215
|
-
|
|
3304
|
+
else {
|
|
3305
|
+
this.add(incomingCallInfo, ourNumberInfo);
|
|
3306
|
+
}
|
|
3216
3307
|
});
|
|
3217
3308
|
this.isConferenceCallHold = true;
|
|
3218
3309
|
}
|
|
@@ -4371,11 +4462,9 @@ class DialboxComponent {
|
|
|
4371
4462
|
// intercepted with a custom modal. Triggers the native confirmation so the
|
|
4372
4463
|
// user does not lose the call accidentally. (Browser controls the wording.)
|
|
4373
4464
|
if (this.isCallInProgress || this.incomingCallsList?.length) {
|
|
4374
|
-
// $event.preventDefault();
|
|
4375
4465
|
const confirmationMessage = 'Reloading this page will disconnect your active call. Are you sure you want to continue?';
|
|
4376
4466
|
$event.returnValue = confirmationMessage;
|
|
4377
4467
|
$event.preventDefault();
|
|
4378
|
-
// this.confirmReloadDuringCall();
|
|
4379
4468
|
return confirmationMessage;
|
|
4380
4469
|
}
|
|
4381
4470
|
return undefined;
|
|
@@ -4385,27 +4474,7 @@ class DialboxComponent {
|
|
|
4385
4474
|
if (this.isReloadConfirmed) {
|
|
4386
4475
|
return;
|
|
4387
4476
|
}
|
|
4388
|
-
// Fires only after the user confirmed the native reload/navigation. Remove
|
|
4389
|
-
// the conference participants using a keepalive request so it survives the
|
|
4390
|
-
// page being torn down (a normal HttpClient call would be cancelled).
|
|
4391
|
-
if (this.isCallInProgress || this.incomingCallsList?.length) {
|
|
4392
|
-
// this.removeParticipantsOnUnload();
|
|
4393
|
-
}
|
|
4394
4477
|
}
|
|
4395
|
-
// private removeParticipantsOnUnload() {
|
|
4396
|
-
// const conferenceId = this.incomingCallsList?.[0]?.conferenceId;
|
|
4397
|
-
// if (!conferenceId) {
|
|
4398
|
-
// return;
|
|
4399
|
-
// }
|
|
4400
|
-
// try {
|
|
4401
|
-
// fetch(
|
|
4402
|
-
// `${environment.apiUrl}/utilities/ext/ur/remove/participant/all/${conferenceId}`,
|
|
4403
|
-
// { method: 'GET', keepalive: true }
|
|
4404
|
-
// );
|
|
4405
|
-
// } catch {
|
|
4406
|
-
// // Best-effort cleanup during unload; nothing else we can do here.
|
|
4407
|
-
// }
|
|
4408
|
-
// }
|
|
4409
4478
|
constructor(twilioService, extService, ipService, extensionService, cdk, router, incomeingCallSocketService) {
|
|
4410
4479
|
this.twilioService = twilioService;
|
|
4411
4480
|
this.extService = extService;
|
|
@@ -4476,6 +4545,7 @@ class DialboxComponent {
|
|
|
4476
4545
|
// Set once the user confirms an intentional reload via our custom modal,
|
|
4477
4546
|
// so the native beforeunload prompt is not shown on top of it.
|
|
4478
4547
|
this.isReloadConfirmed = false;
|
|
4548
|
+
this.nullParticipantCallList = [];
|
|
4479
4549
|
this.contactNameForLoader = '';
|
|
4480
4550
|
this.contactImgForLoader = 'assets/images/user.jpg';
|
|
4481
4551
|
this.incomingFromImage = '';
|
|
@@ -4487,9 +4557,6 @@ class DialboxComponent {
|
|
|
4487
4557
|
}
|
|
4488
4558
|
this.incomeingCallSocketService.createAudioBase();
|
|
4489
4559
|
}
|
|
4490
|
-
async getRemoveParticipants(participantId, conferenceId) {
|
|
4491
|
-
return await this.extensionService.getRemoveParticipants(participantId, conferenceId).toPromise();
|
|
4492
|
-
}
|
|
4493
4560
|
initializeTwilio() {
|
|
4494
4561
|
if (!this.isInitialized) {
|
|
4495
4562
|
this.token = localStorage.getItem('ext_token') || '';
|
|
@@ -4525,7 +4592,11 @@ class DialboxComponent {
|
|
|
4525
4592
|
const socketConferenceIds = incomingCallData.map((c) => c.conferenceId);
|
|
4526
4593
|
incomingCallData.forEach((callInfo) => {
|
|
4527
4594
|
// Check if WE are an active participant in this conference
|
|
4528
|
-
|
|
4595
|
+
if (!callInfo?.participants?.length && !this.nullParticipantCallList.includes(callInfo?.conferenceId)) {
|
|
4596
|
+
this.nullParticipantCallList.push(callInfo?.conferenceId);
|
|
4597
|
+
swal("Error", 'Call not connected facing some issue', "error");
|
|
4598
|
+
}
|
|
4599
|
+
const ourNumber = callInfo?.participants.find((resData) => ((this.deviceNumberList.includes(resData?.from) && resData?.direction == 'outgoing-call' && resData?.client) ||
|
|
4529
4600
|
(this.deviceNumberList.includes(resData?.to) && resData?.direction == 'incoming-call' && resData?.client &&
|
|
4530
4601
|
resData?.callStatus !== 'completed' &&
|
|
4531
4602
|
(resData?.callStatus == 'participant-join' || resData?.callStatus == 'participant-mute' ||
|