@chat21/chat21-web-widget 5.1.26 → 5.1.27-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.
Files changed (39) hide show
  1. package/.github/workflows/docker-community-push-latest.yml +23 -13
  2. package/.github/workflows/docker-image-tag-community-tag-push.yml +22 -12
  3. package/CHANGELOG.md +88 -13
  4. package/Dockerfile +4 -5
  5. package/angular.json +2 -1
  6. package/deploy_amazon_beta.sh +17 -7
  7. package/docs/changelog/this-branch.md +36 -0
  8. package/package.json +1 -1
  9. package/src/app/app.component.html +9 -2
  10. package/src/app/app.component.scss +59 -0
  11. package/src/app/app.component.ts +128 -33
  12. package/src/app/component/conversation-detail/conversation/conversation.component.html +13 -2
  13. package/src/app/component/conversation-detail/conversation/conversation.component.scss +30 -2
  14. package/src/app/component/conversation-detail/conversation/conversation.component.ts +190 -5
  15. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +16 -3
  16. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +12 -9
  17. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +15 -1
  18. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +1 -1
  19. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +103 -80
  20. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +40 -13
  21. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +40 -1
  22. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +4 -4
  23. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.ts +1 -0
  24. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.ts +0 -18
  25. package/src/app/component/home/home.component.html +3 -3
  26. package/src/app/component/launcher-button/launcher-button.component.html +1 -1
  27. package/src/app/component/launcher-button/launcher-button.component.ts +3 -2
  28. package/src/app/providers/global-settings.service.ts +38 -0
  29. package/src/app/providers/translator.service.ts +2 -0
  30. package/src/app/sass/_variables.scss +1 -0
  31. package/src/app/utils/globals.ts +8 -2
  32. package/src/assets/i18n/en.json +2 -0
  33. package/src/assets/i18n/es.json +2 -0
  34. package/src/assets/i18n/fr.json +2 -0
  35. package/src/assets/i18n/it.json +2 -0
  36. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
  37. package/src/chat21-core/utils/utils.ts +5 -2
  38. package/src/iframe-style.css +5 -5
  39. package/deploy_amazon_prod.sh +0 -41
@@ -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
 
@@ -316,8 +321,19 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
316
321
  this.g.setIsOpen(isOpen)
317
322
  this.appStorageService.setItem('isOpen', isOpen)
318
323
  }
319
-
320
-
324
+
325
+ if(this.g.onPageChangeVisibilityDesktop === 'last'){
326
+ this.logger.debug('[APP-COMP2] ------this.g.isOpen: ', this.g.isOpen)
327
+ if(this.g.isOpen){
328
+ this.g.autoStart = true;
329
+ }
330
+ }
331
+
332
+ // STEP-2: schedule callout after settings are loaded,
333
+ // independently from auth/sign-in.
334
+ this.scheduleCalloutFromSettings();
335
+
336
+
321
337
  /**CHECK IF JWT IS IN URL PARAMETERS */
322
338
  this.logger.debug('[APP-COMP] check if token is passed throw url: ', this.g.jwt);
323
339
  if (this.g.jwt) {
@@ -354,10 +370,24 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
354
370
  this.subscriptions.push(obsSettingsService);
355
371
  this.globalSettingsService.initWidgetParamiters(this.g, this.el);
356
372
 
357
- // SET AUDIO
358
- this.audio = new Audio();
359
- this.audio.src = this.g.baseLocation + URL_SOUND_LIST_CONVERSATION;
360
- this.audio.load();
373
+ }
374
+
375
+ private scheduleCalloutFromSettings() {
376
+ if (this.calloutScheduleTimeout) {
377
+ clearTimeout(this.calloutScheduleTimeout);
378
+ this.calloutScheduleTimeout = null;
379
+ }
380
+
381
+ const calloutTimer = Number(this.g.calloutTimer);
382
+ if (isNaN(calloutTimer) || calloutTimer < 0) {
383
+ return;
384
+ }
385
+
386
+ const delayMs = calloutTimer * 1000;
387
+ this.calloutScheduleTimeout = setTimeout(() => {
388
+ this.calloutScheduleTimeout = null;
389
+ this.showCallout();
390
+ }, delayMs);
361
391
  }
362
392
 
363
393
  private initAll() {
@@ -448,7 +478,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
448
478
  that.triggerOnAuthStateChanged(that.stateLoggedUser);
449
479
  that.logger.debug('[APP-COMP] 1 - IMPOSTO STATO CONNESSO UTENTE ', autoStart);
450
480
 
451
-
481
+ this.initAudioNotification()
452
482
 
453
483
  new Promise(async (resolve, reject)=> {
454
484
  that.typingService.initialize(this.g.tenant);
@@ -460,7 +490,8 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
460
490
 
461
491
  // this.initConversationsHandler(this.g.tenant, that.g.senderId);
462
492
  /* If singleConversation mode is active wait to showWidget: do it later in initConversationsHandler */
463
- if (autoStart && !that.g.singleConversation) {
493
+ const hasBotsRules = Array.isArray(this.g.botsRules) && this.g.botsRules.length > 0;
494
+ if ((autoStart || hasBotsRules) && !that.g.singleConversation) {
464
495
  that.showWidget();
465
496
  }
466
497
 
@@ -474,26 +505,37 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
474
505
  that.listenToWidgetClick()
475
506
  }
476
507
 
508
+ /** DDP IF AUTOSTART IS FALSE, SHOW WIDGET */
509
+ if(!autoStart){
510
+ that.logger.info('[APP-COMP] AUTOSTART IS FALSE AND LOGGED SUCCESSFULLY ');
511
+ this.g.setParameter('isShown', true, true);
512
+ }
477
513
 
478
514
 
479
515
  } else if (state && state === AUTH_STATE_OFFLINE && !this.forceDisconnect) {
480
516
  /** non sono loggato */
481
517
  that.logger.info('[APP-COMP] OFFLINE - NO CURRENT USER AUTENTICATE: ');
482
518
  that.g.setParameter('isLogged', false);
483
- that.hideWidget();
519
+ // that.hideWidget();
484
520
  // that.g.setParameter('isShown', false, true);
485
521
  that.triggerOnAuthStateChanged(that.stateLoggedUser);
486
- if (autoStart) {
522
+ const shouldAutoAuthenticate = autoStart ||
523
+ this.g.onPageChangeVisibilityDesktop === 'open' ||
524
+ this.g.onPageChangeVisibilityMobile === 'open' ||
525
+ (Array.isArray(this.g.botsRules) && this.g.botsRules.length > 0)
526
+ // || this.g.hasCalloutInWidgetConfig;
527
+ if (shouldAutoAuthenticate) {
487
528
  that.authenticate();
529
+ } else {
530
+ that.logger.debug('[APP-COMP] Skip auto-auth: startup conditions not met, show launcher only');
488
531
  }
489
- }else if(state && state === AUTH_STATE_CLOSE ){
532
+ } else if(state && state === AUTH_STATE_CLOSE ){
490
533
  that.logger.info('[APP-COMP] CLOSE - CHANNEL CLOSED: ', this.chatManager);
491
534
  if(this.g.recipientId){
492
535
  this.chatManager.removeConversationHandler(this.g.recipientId)
493
536
  this.g.recipientId = null;
494
537
  }
495
- }
496
-
538
+ }
497
539
 
498
540
  });
499
541
  this.subscriptions.push(subAuthStateChanged);
@@ -738,6 +780,8 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
738
780
  // divWidgetContainer.style.display = 'block';
739
781
  // }
740
782
  // }, 500);
783
+
784
+ this.loading = false;
741
785
  }
742
786
  // ========= end:: START UI ============//
743
787
 
@@ -846,7 +890,13 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
846
890
  this.appStorageService.setItem('attributes', JSON.stringify(attributes));
847
891
  return attributes;
848
892
  }
849
-
893
+
894
+ // SET AUDIO
895
+ private initAudioNotification(){
896
+ this.audio = new Audio();
897
+ this.audio.src = this.g.baseLocation + URL_SOUND_LIST_CONVERSATION;
898
+ this.audio.load();
899
+ }
850
900
 
851
901
  private async initConversationsHandler(tenant: string, senderId: string) {
852
902
  this.logger.debug('[APP-COMP] initialize: ListConversationsComponent');
@@ -1170,14 +1220,32 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
1170
1220
  return this.signOut();
1171
1221
  }
1172
1222
 
1223
+ private canShowCalloutNow(): boolean {
1224
+ if (this.g.isOpen !== false) {
1225
+ return false;
1226
+ }
1227
+ if (this.g.isOpenNewMessage) {
1228
+ return false;
1229
+ }
1230
+ if (!this.g.calloutStaus) {
1231
+ return false;
1232
+ }
1233
+ if (!this.g.hasCalloutInWidgetConfig) {
1234
+ return false;
1235
+ }
1236
+ return true;
1237
+ }
1238
+
1173
1239
  /** show callout */
1174
1240
  private showCallout() {
1175
- if (this.g.isOpen === false) {
1176
- // this.g.setParameter('calloutTimer', 1)
1177
- this.eyeeyeCatcherCardComponent.openEyeCatcher();
1178
- this.g.setParameter('displayEyeCatcherCard', 'block');
1179
- this.triggerOnOpenEyeCatcherEvent();
1241
+ if (!this.canShowCalloutNow()) {
1242
+ return;
1180
1243
  }
1244
+ if (!this.eyeeyeCatcherCardComponent) {
1245
+ return;
1246
+ }
1247
+ // Delegate visibility logic to the eye-catcher component.
1248
+ this.eyeeyeCatcherCardComponent.openEyeCatcher();
1181
1249
  }
1182
1250
 
1183
1251
  /** open popup conversation */
@@ -1605,23 +1673,46 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
1605
1673
  this.f21_close();
1606
1674
  }
1607
1675
 
1676
+
1677
+ /** DDP reload widget */
1678
+ async reloadWidget() {
1679
+ this.openCloseWidget();
1680
+ this.logger.debug('[APP-COMP-1] AAA - hideWidget');
1681
+ await Promise.all([
1682
+ this.authenticate(),
1683
+ // this.initAll()
1684
+ ]);
1685
+ this.logger.debug('[APP-COMP-1] CCC - showWidget');
1686
+ }
1687
+
1688
+
1608
1689
  /**
1609
1690
  * LAUNCHER BUTTON:
1610
1691
  * onClick button open/close widget
1611
1692
  */
1612
1693
  onOpenCloseWidget($event) {
1694
+ this.logger.debug('[APP-COMP] onOpenCloseWidget', $event, this.g.isLogged);
1695
+ if(!this.g.isLogged){
1696
+ this.reloadWidget();
1697
+ } else {
1698
+ this.openCloseWidget();
1699
+ }
1700
+ }
1701
+
1702
+ /** DDP show widget */
1703
+ openCloseWidget() {
1613
1704
  this.g.setParameter('displayEyeCatcherCard', 'none');
1614
1705
  // const conversationActive: ConversationModel = JSON.parse(this.appStorageService.getItem('activeConversation'));
1615
1706
  const recipientId : string = this.appStorageService.getItem('recipientId')
1616
1707
  this.g.setParameter('recipientId', recipientId);
1617
1708
  this.logger.debug('[APP-COMP] openCloseWidget', recipientId, this.g.isOpen, this.g.startFromHome);
1709
+
1618
1710
  if (this.g.isOpen === false) {
1619
1711
  if(this.forceDisconnect){
1620
1712
  this.logger.log('[FORCE] onOpenCloseWidget --> reconnect', this.forceDisconnect)
1621
1713
  this.messagingAuthService.createCustomToken(this.g.tiledeskToken)
1622
1714
  this.forceDisconnect = false;
1623
1715
  }
1624
-
1625
1716
  if (!recipientId) {
1626
1717
  if(this.g.singleConversation){
1627
1718
  this.isOpenHome = false;
@@ -1641,29 +1732,22 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
1641
1732
  this.isOpenHome = false;
1642
1733
  this.isOpenConversation = true;
1643
1734
  this.startUI()
1644
- // this.isOpenSelectionDepartment = false;
1645
1735
  }
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
1736
  this.triggerOnOpenEvent();
1656
-
1657
1737
  } else {
1658
1738
  this.triggerOnCloseEvent();
1659
1739
  }
1660
1740
  //change status to the widget
1661
1741
  this.g.setIsOpen(!this.g.isOpen);
1662
1742
  this.appStorageService.setItem('isOpen', this.g.isOpen);
1663
-
1743
+ //show loading if widget is open and user is not logged
1744
+ if(this.g.isOpen === true && !this.g.isLogged){
1745
+ this.loading = true;
1746
+ }
1664
1747
  // this.saveBadgeNewConverstionNumber();
1665
1748
  }
1666
1749
 
1750
+
1667
1751
  /**
1668
1752
  * MODAL SELECTION DEPARTMENT:
1669
1753
  * selected department
@@ -2039,6 +2123,12 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
2039
2123
  }
2040
2124
 
2041
2125
  onWidgetSizeChange(mode: any) {
2126
+ if (this.g?.isMobile) {
2127
+ this.g.fullscreenMode = true;
2128
+ this.g.size = 'max';
2129
+ return;
2130
+ }
2131
+
2042
2132
  const normalize = (val: any): 'min' | 'max' | 'top' => {
2043
2133
  const v = (typeof val === 'string') ? val.toLowerCase().trim() : '';
2044
2134
  return (v === 'min' || v === 'max' || v === 'top') ? (v as any) : 'min';
@@ -2217,6 +2307,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
2217
2307
  this.el.nativeElement.style.setProperty('--chat-header-height', this.g.hideHeaderConversation? '0px': null)
2218
2308
  this.el.nativeElement.style.setProperty('--font-size-bubble-message', this.g.fontSize)
2219
2309
  this.el.nativeElement.style.setProperty('--font-family-bubble-message', this.g.fontFamily)
2310
+ this.el.nativeElement.style.setProperty('--chat-footer-close-button-height', this.g.closeChatInConversation? '30px': '0px')
2220
2311
 
2221
2312
  }
2222
2313
 
@@ -2266,6 +2357,10 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
2266
2357
  /** elimino tutte le sottoscrizioni */
2267
2358
  ngOnDestroy() {
2268
2359
  this.logger.debug('[APP-COMP] this.subscriptions', this.subscriptions);
2360
+ if (this.calloutScheduleTimeout) {
2361
+ clearTimeout(this.calloutScheduleTimeout);
2362
+ this.calloutScheduleTimeout = null;
2363
+ }
2269
2364
  const windowContext = this.g.windowContext;
2270
2365
  if (windowContext && windowContext['tiledesk']) {
2271
2366
  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
  }