@chat21/chat21-web-widget 5.1.26 → 5.1.30-rc1
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/.github/workflows/docker-community-push-latest.yml +23 -13
- package/.github/workflows/docker-image-tag-community-tag-push.yml +22 -12
- package/CHANGELOG.md +97 -13
- package/Dockerfile +4 -5
- package/angular.json +2 -1
- package/deploy_amazon_beta.sh +17 -7
- package/docs/changelog/badge_Bot_Umano.md +85 -0
- package/docs/changelog/this-branch.md +36 -0
- package/package.json +1 -1
- package/src/app/app.component.html +9 -2
- package/src/app/app.component.scss +59 -0
- package/src/app/app.component.ts +144 -36
- package/src/app/component/conversation-detail/conversation/conversation.component.html +13 -2
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +30 -2
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +85 -5
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +16 -3
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +12 -9
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +15 -1
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +1 -1
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +103 -80
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +40 -13
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +40 -1
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +3 -3
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.ts +1 -0
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.ts +0 -18
- package/src/app/component/home/home.component.html +3 -3
- package/src/app/component/last-message/last-message.component.ts +4 -1
- package/src/app/component/launcher-button/launcher-button.component.html +1 -1
- package/src/app/component/launcher-button/launcher-button.component.ts +3 -2
- package/src/app/providers/global-settings.service.ts +26 -0
- package/src/app/providers/translator.service.ts +2 -0
- package/src/app/sass/_variables.scss +1 -0
- package/src/app/utils/conversation-sender-classifier.ts +116 -0
- package/src/app/utils/globals.ts +33 -5
- package/src/assets/i18n/en.json +2 -0
- package/src/assets/i18n/es.json +2 -0
- package/src/assets/i18n/fr.json +2 -0
- package/src/assets/i18n/it.json +2 -0
- package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
- package/src/chat21-core/utils/utils.ts +5 -2
- package/src/iframe-style.css +5 -5
- package/deploy_amazon_prod.sh +0 -41
package/src/app/app.component.ts
CHANGED
|
@@ -106,6 +106,11 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
106
106
|
|
|
107
107
|
forceDisconnect: boolean = false;
|
|
108
108
|
|
|
109
|
+
//network status
|
|
110
|
+
isOnline: boolean = true;
|
|
111
|
+
loading: boolean = false;
|
|
112
|
+
private calloutScheduleTimeout: any = null;
|
|
113
|
+
|
|
109
114
|
// alert error message
|
|
110
115
|
isShowErrorMessage: boolean = false;
|
|
111
116
|
errorMessage: string = '';
|
|
@@ -149,7 +154,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
149
154
|
this.logger.info('[APP-CONF]---------------- ngAfterViewInit: APP.COMPONENT ---------------- ')
|
|
150
155
|
|
|
151
156
|
// Initialize translation map and enable buttons
|
|
152
|
-
const keys = ['MAXIMIZE', 'MINIMIZE', 'CENTER', 'BUTTON_CLOSE_TO_ICON'];
|
|
157
|
+
const keys = ['MAXIMIZE', 'MINIMIZE', 'CENTER', 'BUTTON_CLOSE_TO_ICON', 'LABEL_LOADING'];
|
|
153
158
|
this.translationMap = this.translateService.translateLanguage(keys);
|
|
154
159
|
this.isButtonsDisabled = false;
|
|
155
160
|
|
|
@@ -164,13 +169,13 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
164
169
|
if (conversation.attributes && conversation.attributes['subtype'] === 'info') {
|
|
165
170
|
return;
|
|
166
171
|
}
|
|
167
|
-
if (conversation.is_new &&
|
|
172
|
+
if (conversation.is_new && that.isInitialized) {
|
|
168
173
|
that.manageTabNotification(false, 'conv-added')
|
|
169
174
|
// this.soundMessage();
|
|
170
175
|
}
|
|
171
|
-
if(this.g.isOpen === false){
|
|
172
|
-
that.lastConversation = conversation;
|
|
176
|
+
if(this.g.isOpen === false && conversation.sender !== this.g.senderId && !isInfo(conversation)){
|
|
173
177
|
that.g.isOpenNewMessage = true;
|
|
178
|
+
that.lastConversation = conversation;
|
|
174
179
|
}
|
|
175
180
|
} else {
|
|
176
181
|
//widget closed
|
|
@@ -218,6 +223,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
218
223
|
that.lastConversation = conversation;
|
|
219
224
|
that.g.isOpenNewMessage = true;
|
|
220
225
|
that.logger.debug('[APP-COMP] lastconversationnn', that.lastConversation)
|
|
226
|
+
that.logger.debug('[APP-COMP] lastconversationnn message' + JSON.stringify(that.lastConversation?.attributes?.commands))
|
|
221
227
|
}
|
|
222
228
|
let badgeNewConverstionNumber = that.conversationsHandlerService.countIsNew()
|
|
223
229
|
that.g.setParameter('conversationsBadge', badgeNewConverstionNumber);
|
|
@@ -316,8 +322,19 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
316
322
|
this.g.setIsOpen(isOpen)
|
|
317
323
|
this.appStorageService.setItem('isOpen', isOpen)
|
|
318
324
|
}
|
|
319
|
-
|
|
320
|
-
|
|
325
|
+
|
|
326
|
+
if(this.g.onPageChangeVisibilityDesktop === 'last'){
|
|
327
|
+
this.logger.debug('[APP-COMP2] ------this.g.isOpen: ', this.g.isOpen)
|
|
328
|
+
if(this.g.isOpen){
|
|
329
|
+
this.g.autoStart = true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// STEP-2: schedule callout after settings are loaded,
|
|
334
|
+
// independently from auth/sign-in.
|
|
335
|
+
this.scheduleCalloutFromSettings();
|
|
336
|
+
|
|
337
|
+
|
|
321
338
|
/**CHECK IF JWT IS IN URL PARAMETERS */
|
|
322
339
|
this.logger.debug('[APP-COMP] check if token is passed throw url: ', this.g.jwt);
|
|
323
340
|
if (this.g.jwt) {
|
|
@@ -354,10 +371,24 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
354
371
|
this.subscriptions.push(obsSettingsService);
|
|
355
372
|
this.globalSettingsService.initWidgetParamiters(this.g, this.el);
|
|
356
373
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this.
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private scheduleCalloutFromSettings() {
|
|
377
|
+
if (this.calloutScheduleTimeout) {
|
|
378
|
+
clearTimeout(this.calloutScheduleTimeout);
|
|
379
|
+
this.calloutScheduleTimeout = null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const calloutTimer = Number(this.g.calloutTimer);
|
|
383
|
+
if (isNaN(calloutTimer) || calloutTimer < 0) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const delayMs = calloutTimer * 1000;
|
|
388
|
+
this.calloutScheduleTimeout = setTimeout(() => {
|
|
389
|
+
this.calloutScheduleTimeout = null;
|
|
390
|
+
this.showCallout();
|
|
391
|
+
}, delayMs);
|
|
361
392
|
}
|
|
362
393
|
|
|
363
394
|
private initAll() {
|
|
@@ -422,6 +453,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
422
453
|
}
|
|
423
454
|
|
|
424
455
|
const autoStart = this.g.autoStart;
|
|
456
|
+
const startHidden = this.g.startHidden;
|
|
425
457
|
that.stateLoggedUser = state;
|
|
426
458
|
if (state && state === AUTH_STATE_ONLINE) {
|
|
427
459
|
/** sono loggato */
|
|
@@ -448,7 +480,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
448
480
|
that.triggerOnAuthStateChanged(that.stateLoggedUser);
|
|
449
481
|
that.logger.debug('[APP-COMP] 1 - IMPOSTO STATO CONNESSO UTENTE ', autoStart);
|
|
450
482
|
|
|
451
|
-
|
|
483
|
+
this.initAudioNotification()
|
|
452
484
|
|
|
453
485
|
new Promise(async (resolve, reject)=> {
|
|
454
486
|
that.typingService.initialize(this.g.tenant);
|
|
@@ -460,7 +492,8 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
460
492
|
|
|
461
493
|
// this.initConversationsHandler(this.g.tenant, that.g.senderId);
|
|
462
494
|
/* If singleConversation mode is active wait to showWidget: do it later in initConversationsHandler */
|
|
463
|
-
|
|
495
|
+
const hasBotsRules = Array.isArray(this.g.botsRules) && this.g.botsRules.length > 0;
|
|
496
|
+
if ((autoStart || hasBotsRules) && !that.g.singleConversation) {
|
|
464
497
|
that.showWidget();
|
|
465
498
|
}
|
|
466
499
|
|
|
@@ -474,26 +507,39 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
474
507
|
that.listenToWidgetClick()
|
|
475
508
|
}
|
|
476
509
|
|
|
510
|
+
/** DDP IF AUTOSTART IS FALSE, SHOW WIDGET */
|
|
511
|
+
if(!autoStart){
|
|
512
|
+
that.logger.info('[APP-COMP] AUTOSTART IS FALSE AND LOGGED SUCCESSFULLY ');
|
|
513
|
+
this.g.setParameter('isShown', true, true);
|
|
514
|
+
}
|
|
477
515
|
|
|
478
516
|
|
|
479
517
|
} else if (state && state === AUTH_STATE_OFFLINE && !this.forceDisconnect) {
|
|
480
518
|
/** non sono loggato */
|
|
481
519
|
that.logger.info('[APP-COMP] OFFLINE - NO CURRENT USER AUTENTICATE: ');
|
|
482
520
|
that.g.setParameter('isLogged', false);
|
|
483
|
-
that.hideWidget();
|
|
521
|
+
// that.hideWidget();
|
|
484
522
|
// that.g.setParameter('isShown', false, true);
|
|
485
523
|
that.triggerOnAuthStateChanged(that.stateLoggedUser);
|
|
486
|
-
|
|
524
|
+
const shouldAutoAuthenticate = autoStart ||
|
|
525
|
+
this.g.onPageChangeVisibilityDesktop === 'open' ||
|
|
526
|
+
this.g.onPageChangeVisibilityMobile === 'open' ||
|
|
527
|
+
(Array.isArray(this.g.botsRules) && this.g.botsRules.length > 0)
|
|
528
|
+
// || this.g.hasCalloutInWidgetConfig;
|
|
529
|
+
console.log('[APP-COMP] shouldAutoAuthenticate', shouldAutoAuthenticate, startHidden)
|
|
530
|
+
if (shouldAutoAuthenticate) {
|
|
487
531
|
that.authenticate();
|
|
532
|
+
if(startHidden){ that.hideWidget(); }
|
|
533
|
+
} else {
|
|
534
|
+
that.logger.debug('[APP-COMP] Skip auto-auth: startup conditions not met, show launcher only');
|
|
488
535
|
}
|
|
489
|
-
}else if(state && state === AUTH_STATE_CLOSE ){
|
|
536
|
+
} else if(state && state === AUTH_STATE_CLOSE ){
|
|
490
537
|
that.logger.info('[APP-COMP] CLOSE - CHANNEL CLOSED: ', this.chatManager);
|
|
491
538
|
if(this.g.recipientId){
|
|
492
539
|
this.chatManager.removeConversationHandler(this.g.recipientId)
|
|
493
540
|
this.g.recipientId = null;
|
|
494
541
|
}
|
|
495
|
-
}
|
|
496
|
-
|
|
542
|
+
}
|
|
497
543
|
|
|
498
544
|
});
|
|
499
545
|
this.subscriptions.push(subAuthStateChanged);
|
|
@@ -738,6 +784,8 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
738
784
|
// divWidgetContainer.style.display = 'block';
|
|
739
785
|
// }
|
|
740
786
|
// }, 500);
|
|
787
|
+
|
|
788
|
+
this.loading = false;
|
|
741
789
|
}
|
|
742
790
|
// ========= end:: START UI ============//
|
|
743
791
|
|
|
@@ -846,7 +894,13 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
846
894
|
this.appStorageService.setItem('attributes', JSON.stringify(attributes));
|
|
847
895
|
return attributes;
|
|
848
896
|
}
|
|
849
|
-
|
|
897
|
+
|
|
898
|
+
// SET AUDIO
|
|
899
|
+
private initAudioNotification(){
|
|
900
|
+
this.audio = new Audio();
|
|
901
|
+
this.audio.src = this.g.baseLocation + URL_SOUND_LIST_CONVERSATION;
|
|
902
|
+
this.audio.load();
|
|
903
|
+
}
|
|
850
904
|
|
|
851
905
|
private async initConversationsHandler(tenant: string, senderId: string) {
|
|
852
906
|
this.logger.debug('[APP-COMP] initialize: ListConversationsComponent');
|
|
@@ -1170,14 +1224,32 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
1170
1224
|
return this.signOut();
|
|
1171
1225
|
}
|
|
1172
1226
|
|
|
1227
|
+
private canShowCalloutNow(): boolean {
|
|
1228
|
+
if (this.g.isOpen !== false) {
|
|
1229
|
+
return false;
|
|
1230
|
+
}
|
|
1231
|
+
if (this.g.isOpenNewMessage) {
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
if (!this.g.calloutStaus) {
|
|
1235
|
+
return false;
|
|
1236
|
+
}
|
|
1237
|
+
if (!this.g.hasCalloutInWidgetConfig) {
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1173
1243
|
/** show callout */
|
|
1174
1244
|
private showCallout() {
|
|
1175
|
-
if (this.
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1245
|
+
if (!this.canShowCalloutNow()) {
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
if (!this.eyeeyeCatcherCardComponent) {
|
|
1249
|
+
return;
|
|
1180
1250
|
}
|
|
1251
|
+
// Delegate visibility logic to the eye-catcher component.
|
|
1252
|
+
this.eyeeyeCatcherCardComponent.openEyeCatcher();
|
|
1181
1253
|
}
|
|
1182
1254
|
|
|
1183
1255
|
/** open popup conversation */
|
|
@@ -1185,6 +1257,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
1185
1257
|
const senderId = this.g.senderId;
|
|
1186
1258
|
this.logger.debug('[APP-COMP] f21_open senderId: ', senderId);
|
|
1187
1259
|
if (senderId) {
|
|
1260
|
+
this.enforceMobileFullscreenOnOpen();
|
|
1188
1261
|
// chiudo callout
|
|
1189
1262
|
this.g.setParameter('displayEyeCatcherCard', 'none');
|
|
1190
1263
|
// this.g.isOpen = true; // !this.isOpen;
|
|
@@ -1605,23 +1678,47 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
1605
1678
|
this.f21_close();
|
|
1606
1679
|
}
|
|
1607
1680
|
|
|
1681
|
+
|
|
1682
|
+
/** DDP reload widget */
|
|
1683
|
+
async reloadWidget() {
|
|
1684
|
+
this.openCloseWidget();
|
|
1685
|
+
this.logger.debug('[APP-COMP-1] AAA - hideWidget');
|
|
1686
|
+
await Promise.all([
|
|
1687
|
+
this.authenticate(),
|
|
1688
|
+
// this.initAll()
|
|
1689
|
+
]);
|
|
1690
|
+
this.logger.debug('[APP-COMP-1] CCC - showWidget');
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
|
|
1608
1694
|
/**
|
|
1609
1695
|
* LAUNCHER BUTTON:
|
|
1610
1696
|
* onClick button open/close widget
|
|
1611
1697
|
*/
|
|
1612
1698
|
onOpenCloseWidget($event) {
|
|
1699
|
+
this.logger.debug('[APP-COMP] onOpenCloseWidget', $event, this.g.isLogged);
|
|
1700
|
+
if(!this.g.isLogged){
|
|
1701
|
+
this.reloadWidget();
|
|
1702
|
+
} else {
|
|
1703
|
+
this.openCloseWidget();
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/** DDP show widget */
|
|
1708
|
+
openCloseWidget() {
|
|
1613
1709
|
this.g.setParameter('displayEyeCatcherCard', 'none');
|
|
1614
1710
|
// const conversationActive: ConversationModel = JSON.parse(this.appStorageService.getItem('activeConversation'));
|
|
1615
1711
|
const recipientId : string = this.appStorageService.getItem('recipientId')
|
|
1616
1712
|
this.g.setParameter('recipientId', recipientId);
|
|
1617
1713
|
this.logger.debug('[APP-COMP] openCloseWidget', recipientId, this.g.isOpen, this.g.startFromHome);
|
|
1714
|
+
|
|
1618
1715
|
if (this.g.isOpen === false) {
|
|
1716
|
+
this.enforceMobileFullscreenOnOpen();
|
|
1619
1717
|
if(this.forceDisconnect){
|
|
1620
1718
|
this.logger.log('[FORCE] onOpenCloseWidget --> reconnect', this.forceDisconnect)
|
|
1621
1719
|
this.messagingAuthService.createCustomToken(this.g.tiledeskToken)
|
|
1622
1720
|
this.forceDisconnect = false;
|
|
1623
1721
|
}
|
|
1624
|
-
|
|
1625
1722
|
if (!recipientId) {
|
|
1626
1723
|
if(this.g.singleConversation){
|
|
1627
1724
|
this.isOpenHome = false;
|
|
@@ -1641,29 +1738,22 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
1641
1738
|
this.isOpenHome = false;
|
|
1642
1739
|
this.isOpenConversation = true;
|
|
1643
1740
|
this.startUI()
|
|
1644
|
-
// this.isOpenSelectionDepartment = false;
|
|
1645
1741
|
}
|
|
1646
|
-
// if (!conversationActive && !this.g.startFromHome) {
|
|
1647
|
-
// this.isOpenHome = false;
|
|
1648
|
-
// this.isOpenConversation = true;
|
|
1649
|
-
// this.startNwConversation();
|
|
1650
|
-
// } else if (conversationActive) {
|
|
1651
|
-
// this.isOpenHome = false;
|
|
1652
|
-
// this.isOpenConversation = true;
|
|
1653
|
-
// }
|
|
1654
|
-
// this.g.startFromHome = true;
|
|
1655
1742
|
this.triggerOnOpenEvent();
|
|
1656
|
-
|
|
1657
1743
|
} else {
|
|
1658
1744
|
this.triggerOnCloseEvent();
|
|
1659
1745
|
}
|
|
1660
1746
|
//change status to the widget
|
|
1661
1747
|
this.g.setIsOpen(!this.g.isOpen);
|
|
1662
1748
|
this.appStorageService.setItem('isOpen', this.g.isOpen);
|
|
1663
|
-
|
|
1749
|
+
//show loading if widget is open and user is not logged
|
|
1750
|
+
if(this.g.isOpen === true && !this.g.isLogged){
|
|
1751
|
+
this.loading = true;
|
|
1752
|
+
}
|
|
1664
1753
|
// this.saveBadgeNewConverstionNumber();
|
|
1665
1754
|
}
|
|
1666
1755
|
|
|
1756
|
+
|
|
1667
1757
|
/**
|
|
1668
1758
|
* MODAL SELECTION DEPARTMENT:
|
|
1669
1759
|
* selected department
|
|
@@ -2039,6 +2129,12 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
2039
2129
|
}
|
|
2040
2130
|
|
|
2041
2131
|
onWidgetSizeChange(mode: any) {
|
|
2132
|
+
if (this.g?.isMobile) {
|
|
2133
|
+
this.g.fullscreenMode = true;
|
|
2134
|
+
this.g.size = 'max';
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2042
2138
|
const normalize = (val: any): 'min' | 'max' | 'top' => {
|
|
2043
2139
|
const v = (typeof val === 'string') ? val.toLowerCase().trim() : '';
|
|
2044
2140
|
return (v === 'min' || v === 'max' || v === 'top') ? (v as any) : 'min';
|
|
@@ -2084,6 +2180,13 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
2084
2180
|
}
|
|
2085
2181
|
}
|
|
2086
2182
|
|
|
2183
|
+
private enforceMobileFullscreenOnOpen() {
|
|
2184
|
+
if (this.g?.isMobile) {
|
|
2185
|
+
this.g.fullscreenMode = true;
|
|
2186
|
+
this.g.size = 'max';
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2087
2190
|
/**
|
|
2088
2191
|
* MODAL RATING WIDGET:
|
|
2089
2192
|
* close modal page
|
|
@@ -2217,6 +2320,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
2217
2320
|
this.el.nativeElement.style.setProperty('--chat-header-height', this.g.hideHeaderConversation? '0px': null)
|
|
2218
2321
|
this.el.nativeElement.style.setProperty('--font-size-bubble-message', this.g.fontSize)
|
|
2219
2322
|
this.el.nativeElement.style.setProperty('--font-family-bubble-message', this.g.fontFamily)
|
|
2323
|
+
this.el.nativeElement.style.setProperty('--chat-footer-close-button-height', this.g.closeChatInConversation? '30px': '0px')
|
|
2220
2324
|
|
|
2221
2325
|
}
|
|
2222
2326
|
|
|
@@ -2266,6 +2370,10 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
2266
2370
|
/** elimino tutte le sottoscrizioni */
|
|
2267
2371
|
ngOnDestroy() {
|
|
2268
2372
|
this.logger.debug('[APP-COMP] this.subscriptions', this.subscriptions);
|
|
2373
|
+
if (this.calloutScheduleTimeout) {
|
|
2374
|
+
clearTimeout(this.calloutScheduleTimeout);
|
|
2375
|
+
this.calloutScheduleTimeout = null;
|
|
2376
|
+
}
|
|
2269
2377
|
const windowContext = this.g.windowContext;
|
|
2270
2378
|
if (windowContext && windowContext['tiledesk']) {
|
|
2271
2379
|
windowContext['tiledesk']['angularcomponent'] = null;
|
|
@@ -30,12 +30,21 @@
|
|
|
30
30
|
[isTypings]="isTypings"
|
|
31
31
|
[nameUserTypingNow]="nameUserTypingNow"
|
|
32
32
|
[typingLocation]="g?.typingLocation"
|
|
33
|
+
[isMobile]="g?.isMobile"
|
|
33
34
|
(onBack)="onBackHomeFN()"
|
|
34
35
|
(onCloseWidget)="onCloseWidgetFN()"
|
|
35
36
|
(onMenuOptionClick)="onMenuOptionClick($event)"
|
|
36
37
|
(onMenuOptionShow)="onMenuOption($event)">
|
|
37
38
|
</chat-conversation-header>
|
|
38
39
|
|
|
40
|
+
<!-- Badge: natura dell'ultimo messaggio ricevuto dal server -->
|
|
41
|
+
<!-- <div
|
|
42
|
+
*ngIf="showLastServerSenderBadge"
|
|
43
|
+
id="chat21-last-server-sender-badge"
|
|
44
|
+
[ngClass]="lastServerSenderKind">
|
|
45
|
+
{{ lastServerSenderBadgeText }}
|
|
46
|
+
</div> -->
|
|
47
|
+
|
|
39
48
|
<div id="dropZone_container" *ngIf="isHovering"
|
|
40
49
|
[class.hideTextReply]="hideFooterTextReply && g?.poweredBy">
|
|
41
50
|
<div class="drop">
|
|
@@ -54,6 +63,7 @@
|
|
|
54
63
|
[idUserTypingNow]="idUserTypingNow"
|
|
55
64
|
[nameUserTypingNow]="nameUserTypingNow"
|
|
56
65
|
[typingLocation]="g?.typingLocation"
|
|
66
|
+
[showThinkingMessage]="showThinkingMessage"
|
|
57
67
|
[fullscreenMode]="g?.fullscreenMode"
|
|
58
68
|
[translationMap]="translationMapContent"
|
|
59
69
|
[stylesMap]="stylesMap"
|
|
@@ -68,7 +78,6 @@
|
|
|
68
78
|
(dragleave)="drag($event)" >
|
|
69
79
|
</chat-conversation-content>
|
|
70
80
|
|
|
71
|
-
|
|
72
81
|
|
|
73
82
|
<!-- INTERNAL FRAME FOR SELF ACTION LINK BUTTONS-->
|
|
74
83
|
<chat-internal-frame *ngIf="isButtonUrl"
|
|
@@ -128,6 +137,7 @@
|
|
|
128
137
|
[isMobile]="g?.isMobile"
|
|
129
138
|
[isEmojiiPickerShow]="isEmojiiPickerShow"
|
|
130
139
|
[footerMessagePlaceholder]="footerMessagePlaceholder"
|
|
140
|
+
[closeChatInConversation]="g?.closeChatInConversation"
|
|
131
141
|
[fileUploadAccept]="g?.fileUploadAccept"
|
|
132
142
|
[dropEvent]="dropEvent"
|
|
133
143
|
[poweredBy]="g?.poweredBy"
|
|
@@ -138,7 +148,8 @@
|
|
|
138
148
|
(onAfterSendMessage)="onAfterSendMessageFN($event)"
|
|
139
149
|
(onChangeTextArea)="onChangeTextArea($event)"
|
|
140
150
|
(onAttachmentFileButtonClicked)="onAttachmentFileButtonClicked($event)"
|
|
141
|
-
(onNewConversationButtonClicked)="onNewConversationButtonClickedFN($event)"
|
|
151
|
+
(onNewConversationButtonClicked)="onNewConversationButtonClickedFN($event)"
|
|
152
|
+
(onCloseChatButtonClicked)="onCloseChatButtonClickedFN($event)">
|
|
142
153
|
</chat-conversation-footer>
|
|
143
154
|
|
|
144
155
|
</div>
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
#dropZone_container{
|
|
138
138
|
position: absolute;
|
|
139
139
|
top: 52px;
|
|
140
|
-
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
|
|
140
|
+
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height));
|
|
141
141
|
left: 0;
|
|
142
142
|
right: 0;
|
|
143
143
|
background-color: rgba(240,248,255,0.6);
|
|
@@ -153,6 +153,34 @@
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
#chat21-last-server-sender-badge{
|
|
157
|
+
position: absolute;
|
|
158
|
+
top: 58px;
|
|
159
|
+
right: 12px;
|
|
160
|
+
z-index: 25;
|
|
161
|
+
padding: 6px 10px;
|
|
162
|
+
border-radius: 999px;
|
|
163
|
+
font-size: 0.95em;
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
line-height: 1.1;
|
|
166
|
+
border: 1px solid rgba(0,0,0,0.08);
|
|
167
|
+
color: rgba(0,0,0,0.65);
|
|
168
|
+
background: rgba(0,0,0,0.03);
|
|
169
|
+
user-select: none;
|
|
170
|
+
|
|
171
|
+
&.bot{
|
|
172
|
+
border-color: rgba(0, 150, 136, 0.45);
|
|
173
|
+
color: rgba(0, 150, 136, 1);
|
|
174
|
+
background: rgba(0, 150, 136, 0.12);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
&.human{
|
|
178
|
+
border-color: rgba(63, 81, 181, 0.45);
|
|
179
|
+
color: rgba(63, 81, 181, 1);
|
|
180
|
+
background: rgba(63, 81, 181, 0.12);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
156
184
|
dialog:-internal-dialog-in-top-layer{
|
|
157
185
|
border: 0px;
|
|
158
186
|
border-radius: 16px;
|
|
@@ -212,7 +240,7 @@ dialog:-internal-dialog-in-top-layer{
|
|
|
212
240
|
|
|
213
241
|
|
|
214
242
|
::ng-deep .chat21-sheet-content{
|
|
215
|
-
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + 34px)!important;
|
|
243
|
+
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height) + 34px)!important;
|
|
216
244
|
}
|
|
217
245
|
|
|
218
246
|
}
|
|
@@ -40,6 +40,7 @@ import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance'
|
|
|
40
40
|
import { TiledeskRequestsService } from 'src/chat21-core/providers/tiledesk/tiledesk-requests.service';
|
|
41
41
|
import { ConversationContentComponent } from '../conversation-content/conversation-content.component';
|
|
42
42
|
import { checkAcceptedFile } from 'src/app/utils/utils';
|
|
43
|
+
import { computeConversationBadgeState } from 'src/app/utils/conversation-sender-classifier';
|
|
43
44
|
// import { TranslateService } from '@ngx-translate/core';
|
|
44
45
|
|
|
45
46
|
@Component({
|
|
@@ -113,6 +114,16 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
113
114
|
// availableAgentsStatus = false; // indica quando è impostato lo stato degli agenti nel subscribe
|
|
114
115
|
messages: Array<MessageModel> = [];
|
|
115
116
|
|
|
117
|
+
// Temporary "thinking" state after a client message is sent.
|
|
118
|
+
public showThinkingMessage: boolean = false;
|
|
119
|
+
|
|
120
|
+
// Badge "ultimo messaggio ricevuto dal server" (bot/umano)
|
|
121
|
+
public showLastServerSenderBadge: boolean = false;
|
|
122
|
+
public lastServerSenderKind: 'bot' | 'human' | null = null;
|
|
123
|
+
public lastServerSenderBadgeText: string = '';
|
|
124
|
+
// Diagnostics/internal state: kind of the latest *server* message (including system).
|
|
125
|
+
public latestServerMessageKind: 'bot' | 'human' | 'system' | 'unknown' = 'unknown';
|
|
126
|
+
|
|
116
127
|
|
|
117
128
|
CLIENT_BROWSER: string = navigator.userAgent;
|
|
118
129
|
|
|
@@ -235,7 +246,8 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
235
246
|
'CONTINUE',
|
|
236
247
|
'EMOJI_NOT_ELLOWED',
|
|
237
248
|
'ATTACHMENT',
|
|
238
|
-
'EMOJI'
|
|
249
|
+
'EMOJI',
|
|
250
|
+
'CLOSE_CHAT'
|
|
239
251
|
];
|
|
240
252
|
|
|
241
253
|
const keysContent = [
|
|
@@ -252,6 +264,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
252
264
|
'LABEL_TODAY',
|
|
253
265
|
'LABEL_TOMORROW',
|
|
254
266
|
'LABEL_LOADING',
|
|
267
|
+
'LABEL_THINKING',
|
|
255
268
|
'LABEL_TO',
|
|
256
269
|
'ARRAY_DAYS',
|
|
257
270
|
];
|
|
@@ -356,6 +369,21 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
356
369
|
|
|
357
370
|
}
|
|
358
371
|
|
|
372
|
+
/**
|
|
373
|
+
* Backward-compat wrappers: keep component API stable while delegating
|
|
374
|
+
* the sender classification logic to a pure utility module.
|
|
375
|
+
*/
|
|
376
|
+
private refreshLastServerSenderBadge() {
|
|
377
|
+
const state = computeConversationBadgeState(this.messages || [], this.senderId);
|
|
378
|
+
this.latestServerMessageKind = state.latestServerMessageKind;
|
|
379
|
+
this.lastServerSenderKind = state.latestNonSystemResponderKind;
|
|
380
|
+
this.showLastServerSenderBadge = state.showBadge;
|
|
381
|
+
this.lastServerSenderBadgeText = state.badgeText;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// (Implementation moved to src/app/utils/conversation-sender-classifier.ts)
|
|
385
|
+
|
|
386
|
+
|
|
359
387
|
/**
|
|
360
388
|
* do per scontato che this.userId esiste!!!
|
|
361
389
|
*/
|
|
@@ -368,6 +396,10 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
368
396
|
// this.connectConversation();
|
|
369
397
|
await this.initConversationHandler();
|
|
370
398
|
|
|
399
|
+
// After loading/connecting, compute "ultimo messaggio ricevuto dal server"
|
|
400
|
+
// (excluding messages sent by the client).
|
|
401
|
+
this.refreshLastServerSenderBadge();
|
|
402
|
+
|
|
371
403
|
this.logger.debug('[CONV-COMP] ------ 4: initializeChatManager ------ ');
|
|
372
404
|
//this.initializeChatManager();
|
|
373
405
|
|
|
@@ -470,7 +502,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
470
502
|
return this.isConversationArchived;
|
|
471
503
|
}
|
|
472
504
|
|
|
473
|
-
//FALLBACK TO TILEDESK
|
|
505
|
+
// //FALLBACK TO TILEDESK
|
|
474
506
|
const requests_list = await this.tiledeskRequestService.getMyRequests().catch(err => {
|
|
475
507
|
this.logger.error('[CONV-COMP] getConversationDetail: error getting request from Tiledesk', err);
|
|
476
508
|
this.isConversationArchived=true
|
|
@@ -488,9 +520,9 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
488
520
|
return this.isConversationArchived
|
|
489
521
|
}
|
|
490
522
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
523
|
+
this.isConversationArchived = false;
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
494
526
|
|
|
495
527
|
/**
|
|
496
528
|
* this.g.recipientId:
|
|
@@ -787,8 +819,14 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
787
819
|
subscribtion = this.conversationHandlerService.messageAdded.pipe(takeUntil(this.unsubscribe$)).subscribe((msg: MessageModel) => {
|
|
788
820
|
this.logger.debug('[CONV-COMP] ***** DETAIL messageAdded *****', msg);
|
|
789
821
|
if (msg) {
|
|
822
|
+
if (msg.sender !== this.senderId) {
|
|
823
|
+
this.showThinkingMessage = false;
|
|
824
|
+
}
|
|
790
825
|
|
|
791
826
|
that.newMessageAdded(msg);
|
|
827
|
+
// Update badge based on the latest message received from the server.
|
|
828
|
+
// We rely on `messages` being kept in-sync by the conversation handler.
|
|
829
|
+
that.refreshLastServerSenderBadge();
|
|
792
830
|
this.checkMessagesLegntForTranscriptDownloadMenuOption();
|
|
793
831
|
this.resetTimeout();
|
|
794
832
|
|
|
@@ -840,6 +878,20 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
840
878
|
this.subscriptions.push(subscribe);
|
|
841
879
|
}
|
|
842
880
|
|
|
881
|
+
subscribtionKey = 'conversationsAdded';
|
|
882
|
+
subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
|
|
883
|
+
if(!subscribtion){
|
|
884
|
+
|
|
885
|
+
subscribtion = this.chatManager.conversationsHandlerService.conversationChanged.pipe(takeUntil(this.unsubscribe$)).subscribe((conversation) => {
|
|
886
|
+
this.logger.debug('[CONV-COMP] ***** DATAIL conversationsChanged *****', conversation, this.conversationWith, this.isConversationArchived);
|
|
887
|
+
if(conversation && conversation.recipient === this.conversationId){
|
|
888
|
+
this.isConversationArchived = false
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
const subscribe = {key: subscribtionKey, value: subscribtion };
|
|
892
|
+
this.subscriptions.push(subscribe);
|
|
893
|
+
}
|
|
894
|
+
|
|
843
895
|
subscribtionKey = 'messageWait';
|
|
844
896
|
subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
|
|
845
897
|
if (!subscribtion) {
|
|
@@ -1088,6 +1140,13 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1088
1140
|
}
|
|
1089
1141
|
/** CALLED BY: conv-header component */
|
|
1090
1142
|
onWidgetSizeChange(mode: any){
|
|
1143
|
+
if (this.g?.isMobile) {
|
|
1144
|
+
this.g.fullscreenMode = true;
|
|
1145
|
+
this.g.size = 'max';
|
|
1146
|
+
this.isMenuShow = false;
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1091
1150
|
const normalize = (val: any): 'min' | 'max' | 'top' => {
|
|
1092
1151
|
const v = (typeof val === 'string') ? val.toLowerCase().trim() : '';
|
|
1093
1152
|
return (v === 'min' || v === 'max' || v === 'top') ? (v as any) : 'min';
|
|
@@ -1294,6 +1353,20 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1294
1353
|
}
|
|
1295
1354
|
/** CALLED BY: conv-footer component */
|
|
1296
1355
|
onAfterSendMessageFN(message: MessageModel){
|
|
1356
|
+
// Manage thinking state only for messages sent by the current client.
|
|
1357
|
+
// Do not force-hide here for other message types/events.
|
|
1358
|
+
this.logger.debug('[CONV-COMP] onAfterSendMessageFN::::')
|
|
1359
|
+
if (message && message.sender === this.senderId) {
|
|
1360
|
+
this.logger.debug('[CONV-COMP] onAfterSendMessageFN:::: message', message)
|
|
1361
|
+
// if (this.shouldShowThinkingForBot()) {
|
|
1362
|
+
// this.logger.debug('[CONV-COMP] shouldShowThinkingForBot::::', true)
|
|
1363
|
+
// this.startThinkingMessage();
|
|
1364
|
+
// } else {
|
|
1365
|
+
// this.logger.debug('[CONV-COMP] shouldShowThinkingForBot::::', false)
|
|
1366
|
+
// this.showThinkingMessage = false;
|
|
1367
|
+
// }
|
|
1368
|
+
this.showThinkingMessage = true;
|
|
1369
|
+
}
|
|
1297
1370
|
this.onAfterSendMessage.emit(message)
|
|
1298
1371
|
}
|
|
1299
1372
|
/** CALLED BY: conv-footer component */
|
|
@@ -1325,6 +1398,12 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1325
1398
|
this.logger.debug('[CONV-COMP] floating onNewConversationButtonClicked')
|
|
1326
1399
|
this.onNewConversationButtonClicked.emit()
|
|
1327
1400
|
}
|
|
1401
|
+
|
|
1402
|
+
/** CALLED BY: conv-footer component */
|
|
1403
|
+
onCloseChatButtonClickedFN(event){
|
|
1404
|
+
this.logger.debug('[CONV-COMP] onCloseChatButtonClicked::::', event)
|
|
1405
|
+
this.onCloseChat()
|
|
1406
|
+
}
|
|
1328
1407
|
// =========== END: event emitter function ====== //
|
|
1329
1408
|
|
|
1330
1409
|
|
|
@@ -1345,6 +1424,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1345
1424
|
//this.storageService.removeItem('activeConversation');
|
|
1346
1425
|
this.isConversationArchived = false;
|
|
1347
1426
|
this.hideTextAreaContent = false;
|
|
1427
|
+
this.showThinkingMessage = false;
|
|
1348
1428
|
this.conversationFooter.textInputTextArea='';
|
|
1349
1429
|
this.hideFooterTextReply = false;
|
|
1350
1430
|
this.footerMessagePlaceholder = '';
|