@chat21/chat21-web-widget 5.1.27-rc1 → 5.1.30

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 (30) hide show
  1. package/.github/workflows/docker-community-push-latest.yml +13 -23
  2. package/.github/workflows/docker-image-tag-community-tag-push.yml +12 -22
  3. package/CHANGELOG.md +28 -82
  4. package/Dockerfile +5 -4
  5. package/angular.json +1 -2
  6. package/deploy_amazon_beta.sh +7 -17
  7. package/deploy_amazon_prod.sh +41 -0
  8. package/docs/changelog/badge_Bot_Umano.md +85 -0
  9. package/docs/changelog/this-branch.md +35 -24
  10. package/package.json +1 -1
  11. package/src/app/app.component.ts +17 -6
  12. package/src/app/component/conversation-detail/conversation/conversation.component.html +1 -3
  13. package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -2
  14. package/src/app/component/conversation-detail/conversation/conversation.component.ts +27 -153
  15. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -1
  16. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +80 -103
  17. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +13 -40
  18. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +0 -7
  19. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +1 -1
  20. package/src/app/providers/global-settings.service.ts +2 -25
  21. package/src/app/providers/translator.service.ts +0 -2
  22. package/src/app/sass/_variables.scss +0 -1
  23. package/src/app/utils/conversation-sender-classifier.ts +116 -0
  24. package/src/app/utils/globals.ts +26 -7
  25. package/src/assets/i18n/en.json +0 -1
  26. package/src/assets/i18n/es.json +0 -1
  27. package/src/assets/i18n/fr.json +0 -1
  28. package/src/assets/i18n/it.json +0 -1
  29. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
  30. package/src/chat21-core/utils/utils.ts +2 -5
@@ -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) + var(--chat-footer-close-button-height));
140
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
141
141
  left: 0;
142
142
  right: 0;
143
143
  background-color: rgba(240,248,255,0.6);
@@ -240,7 +240,7 @@ dialog:-internal-dialog-in-top-layer{
240
240
 
241
241
 
242
242
  ::ng-deep .chat21-sheet-content{
243
- bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height) + 34px)!important;
243
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + 34px)!important;
244
244
  }
245
245
 
246
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({
@@ -115,12 +116,13 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
115
116
 
116
117
  // Temporary "thinking" state after a client message is sent.
117
118
  public showThinkingMessage: boolean = false;
118
- private waitingServerReply: boolean = false;
119
119
 
120
120
  // Badge "ultimo messaggio ricevuto dal server" (bot/umano)
121
121
  public showLastServerSenderBadge: boolean = false;
122
122
  public lastServerSenderKind: 'bot' | 'human' | null = null;
123
123
  public lastServerSenderBadgeText: string = '';
124
+ // Diagnostics/internal state: kind of the latest *server* message (including system).
125
+ public latestServerMessageKind: 'bot' | 'human' | 'system' | 'unknown' = 'unknown';
124
126
 
125
127
 
126
128
  CLIENT_BROWSER: string = navigator.userAgent;
@@ -244,8 +246,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
244
246
  'CONTINUE',
245
247
  'EMOJI_NOT_ELLOWED',
246
248
  'ATTACHMENT',
247
- 'EMOJI',
248
- 'CLOSE_CHAT'
249
+ 'EMOJI'
249
250
  ];
250
251
 
251
252
  const keysContent = [
@@ -367,129 +368,20 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
367
368
 
368
369
  }
369
370
 
370
- private classifyMessageSenderKind(msg: MessageModel | null | undefined): 'bot' | 'human' | 'system' | 'unknown' {
371
- if (!msg) return 'unknown';
372
-
373
- const sender = msg.sender;
374
- const senderFullname = msg.sender_fullname;
375
- const senderFullnameLower = (senderFullname || '').toString().toLowerCase();
376
-
377
- // System messages are always from "system".
378
- if (sender === 'system' || senderFullnameLower === 'system') {
379
- return 'system';
380
- }
381
-
382
- const chatbotId = msg?.attributes?.flowAttributes?.chatbot_id;
383
- if (chatbotId && sender && String(chatbotId) === String(sender)) {
384
- return 'bot';
385
- }
386
-
387
- // Fallback heuristics (used when chatbot_id is missing)
388
- if (sender && String(sender).includes('bot_')) {
389
- return 'bot';
390
- }
391
- if (senderFullnameLower.includes('bot')) {
392
- return 'bot';
393
- }
394
-
395
- return 'human';
396
- }
397
-
398
- /**
399
- * Detects explicit handoff-to-human system messages.
400
- * Example:
401
- * attributes.subtype = "info"
402
- * attributes.updateconversation = true
403
- * attributes.messagelabel.key = "MEMBER_JOINED_GROUP"
404
- * attributes.messagelabel.parameters.member_id = "<human-agent-id>"
405
- */
406
- private isHumanHandoffSystemMessage(msg: MessageModel | null | undefined): boolean {
407
- if (!msg) return false;
408
- if (msg.sender !== 'system') return false;
409
-
410
- const attrs: any = msg.attributes || {};
411
- const key = attrs?.messagelabel?.key;
412
- const memberId = attrs?.messagelabel?.parameters?.member_id;
413
-
414
- if (attrs?.subtype !== 'info') return false;
415
- if (attrs?.updateconversation !== true) return false;
416
- if (key !== 'MEMBER_JOINED_GROUP') return false;
417
- if (!memberId || typeof memberId !== 'string') return false;
418
-
419
- // Exclude system/bot/self joins.
420
- if (memberId === 'system') return false;
421
- if (memberId.startsWith('bot_')) return false;
422
- if (this.senderId && memberId === this.senderId) return false;
423
-
424
- return true;
425
- }
426
-
427
371
  /**
428
- * Finds the last server message (sender != client) and classifies it as bot/human.
429
- * If the last server message is "system", it scans backward to find the last non-system server message.
372
+ * Backward-compat wrappers: keep component API stable while delegating
373
+ * the sender classification logic to a pure utility module.
430
374
  */
431
375
  private refreshLastServerSenderBadge() {
432
- const senderId = this.senderId;
433
- const msgs = this.messages || [];
434
-
435
- let found: 'bot' | 'human' | null = null;
436
- let latestServerMsg: MessageModel | null = null;
437
-
438
- // messages are kept sorted by the handler, but we still scan from the end for "latest".
439
- for (let i = msgs.length - 1; i >= 0; i--) {
440
- const m = msgs[i];
441
- if (!m) continue;
442
-
443
- // Skip messages sent by the current client/user.
444
- if (senderId && m.sender === senderId) continue;
445
-
446
- if (!latestServerMsg) {
447
- latestServerMsg = m;
448
- }
449
-
450
- const kind = this.classifyMessageSenderKind(m);
451
- if (kind === 'system') continue;
452
- if (kind === 'bot' || kind === 'human') {
453
- found = kind;
454
- break;
455
- }
456
- }
457
-
458
- // Priority rule requested: if the latest server message is a system handoff message,
459
- // consider the conversation as "human".
460
- if (this.isHumanHandoffSystemMessage(latestServerMsg)) {
461
- found = 'human';
462
- }
463
-
464
- this.lastServerSenderKind = found;
465
- this.showLastServerSenderBadge = found !== null;
466
- this.lastServerSenderBadgeText = found === 'bot' ? 'Bot' : (found === 'human' ? 'Umano' : '');
376
+ const state = computeConversationBadgeState(this.messages || [], this.senderId);
377
+ this.latestServerMessageKind = state.latestServerMessageKind;
378
+ this.lastServerSenderKind = state.latestNonSystemResponderKind;
379
+ this.showLastServerSenderBadge = state.showBadge;
380
+ this.lastServerSenderBadgeText = state.badgeText;
467
381
  }
468
382
 
469
- private startThinkingMessage() {
470
- this.waitingServerReply = true;
471
- this.showThinkingMessage = true;
472
- }
383
+ // (Implementation moved to src/app/utils/conversation-sender-classifier.ts)
473
384
 
474
- private stopThinkingMessageImmediately() {
475
- if (!this.waitingServerReply) {
476
- return;
477
- }
478
- this.waitingServerReply = false;
479
- this.showThinkingMessage = false;
480
- }
481
-
482
- private shouldShowThinkingForBot(): boolean {
483
- // Primary source: latest server-side classification already computed.
484
- if (this.lastServerSenderKind === 'bot') {
485
- return true;
486
- }
487
- // Safe fallback for bot-targeted direct conversations.
488
- if (this.conversationWith && this.conversationWith.includes('bot_')) {
489
- return true;
490
- }
491
- return false;
492
- }
493
385
 
494
386
  /**
495
387
  * do per scontato che this.userId esiste!!!
@@ -506,7 +398,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
506
398
  // After loading/connecting, compute "ultimo messaggio ricevuto dal server"
507
399
  // (excluding messages sent by the client).
508
400
  this.refreshLastServerSenderBadge();
509
- setTimeout(() => this.refreshLastServerSenderBadge(), 300);
510
401
 
511
402
  this.logger.debug('[CONV-COMP] ------ 4: initializeChatManager ------ ');
512
403
  //this.initializeChatManager();
@@ -610,7 +501,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
610
501
  return this.isConversationArchived;
611
502
  }
612
503
 
613
- // //FALLBACK TO TILEDESK
504
+ //FALLBACK TO TILEDESK
614
505
  const requests_list = await this.tiledeskRequestService.getMyRequests().catch(err => {
615
506
  this.logger.error('[CONV-COMP] getConversationDetail: error getting request from Tiledesk', err);
616
507
  this.isConversationArchived=true
@@ -628,9 +519,9 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
628
519
  return this.isConversationArchived
629
520
  }
630
521
 
631
- this.isConversationArchived = false;
632
- return null;
633
- }
522
+ this.isConversationArchived = true;
523
+ return null;
524
+ }
634
525
 
635
526
  /**
636
527
  * this.g.recipientId:
@@ -928,7 +819,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
928
819
  this.logger.debug('[CONV-COMP] ***** DETAIL messageAdded *****', msg);
929
820
  if (msg) {
930
821
  if (msg.sender !== this.senderId) {
931
- this.stopThinkingMessageImmediately();
822
+ this.showThinkingMessage = false;
932
823
  }
933
824
 
934
825
  that.newMessageAdded(msg);
@@ -986,20 +877,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
986
877
  this.subscriptions.push(subscribe);
987
878
  }
988
879
 
989
- subscribtionKey = 'conversationsAdded';
990
- subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
991
- if(!subscribtion){
992
-
993
- subscribtion = this.chatManager.conversationsHandlerService.conversationChanged.pipe(takeUntil(this.unsubscribe$)).subscribe((conversation) => {
994
- this.logger.debug('[CONV-COMP] ***** DATAIL conversationsChanged *****', conversation, this.conversationWith, this.isConversationArchived);
995
- if(conversation && conversation.recipient === this.conversationId){
996
- this.isConversationArchived = false
997
- }
998
- });
999
- const subscribe = {key: subscribtionKey, value: subscribtion };
1000
- this.subscriptions.push(subscribe);
1001
- }
1002
-
1003
880
  subscribtionKey = 'messageWait';
1004
881
  subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
1005
882
  if (!subscribtion) {
@@ -1463,13 +1340,17 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
1463
1340
  onAfterSendMessageFN(message: MessageModel){
1464
1341
  // Manage thinking state only for messages sent by the current client.
1465
1342
  // Do not force-hide here for other message types/events.
1343
+ this.logger.debug('[CONV-COMP] onAfterSendMessageFN::::')
1466
1344
  if (message && message.sender === this.senderId) {
1467
- if (this.shouldShowThinkingForBot()) {
1468
- this.startThinkingMessage();
1469
- } else {
1470
- this.showThinkingMessage = false;
1471
- this.waitingServerReply = false;
1472
- }
1345
+ this.logger.debug('[CONV-COMP] onAfterSendMessageFN:::: message', message)
1346
+ // if (this.shouldShowThinkingForBot()) {
1347
+ // this.logger.debug('[CONV-COMP] shouldShowThinkingForBot::::', true)
1348
+ // this.startThinkingMessage();
1349
+ // } else {
1350
+ // this.logger.debug('[CONV-COMP] shouldShowThinkingForBot::::', false)
1351
+ // this.showThinkingMessage = false;
1352
+ // }
1353
+ this.showThinkingMessage = true;
1473
1354
  }
1474
1355
  this.onAfterSendMessage.emit(message)
1475
1356
  }
@@ -1502,12 +1383,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
1502
1383
  this.logger.debug('[CONV-COMP] floating onNewConversationButtonClicked')
1503
1384
  this.onNewConversationButtonClicked.emit()
1504
1385
  }
1505
-
1506
- /** CALLED BY: conv-footer component */
1507
- onCloseChatButtonClickedFN(event){
1508
- this.logger.debug('[CONV-COMP] onCloseChatButtonClicked::::', event)
1509
- this.onCloseChat()
1510
- }
1511
1386
  // =========== END: event emitter function ====== //
1512
1387
 
1513
1388
 
@@ -1529,7 +1404,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
1529
1404
  this.isConversationArchived = false;
1530
1405
  this.hideTextAreaContent = false;
1531
1406
  this.showThinkingMessage = false;
1532
- this.waitingServerReply = false;
1533
1407
  this.conversationFooter.textInputTextArea='';
1534
1408
  this.hideFooterTextReply = false;
1535
1409
  this.footerMessagePlaceholder = '';
@@ -44,7 +44,7 @@
44
44
  top: 0;
45
45
  right: 0;
46
46
  left: 0;
47
- bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height));
47
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
48
48
  overflow: hidden;
49
49
  .time{
50
50
  margin-bottom: 20px;
@@ -14,117 +14,94 @@
14
14
 
15
15
  </div>
16
16
 
17
- <div class="textarea-container-wrapper" *ngIf="!hideTextAreaContent && !hideTextReply">
18
- <!-- TEXTAREA + ICONS: conv active-->
19
- <div class="textarea-container">
20
-
21
- <div *ngIf="!isStopRec" class="icons-container">
22
- <!-- ICON ATTACHMENT -->
23
- <label *ngIf="showAttachmentFooterButton" tabindex="1502" aria-label="allegati" for="chat21-file" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-start-upload-doc">
24
- <span class="v-align-center">
25
- <svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" width="24px" height="24" viewBox="0 0 24 24" fill="currentColor">
26
- <path d="M9.9,22.7c0,0-.1,0-.2,0-1.9.3-3.7-.2-5.2-1.4-3-2.3-3.6-6.4-1.4-9.5L9.5,2.5c.4-.5,1.1-.6,1.6-.3.5.4.6,1.1.3,1.6l-6.5,9.4c-1.4,2-1,4.8.9,6.3,1,.8,2.2,1.1,3.5.9,1.3-.2,2.4-.9,3.1-1.9l6-8.7c.9-1.2.6-3-.6-3.9-.6-.5-1.4-.6-2.1-.5-.8.1-1.4.5-1.9,1.1l-5.8,8.2c-.3.5-.2,1.1.2,1.5.2.2.5.3.8.2.3,0,.6-.2.7-.4l4.7-6.2c.4-.5,1.1-.6,1.6-.2.5.4.6,1.1.2,1.6l-4.7,6.2c-.5.7-1.4,1.2-2.3,1.3-.9.1-1.8-.2-2.5-.7-1.4-1.1-1.6-3.1-.6-4.6l5.8-8.2c.8-1.1,2-1.9,3.4-2.1,1.4-.2,2.7.1,3.8,1,2.2,1.7,2.7,4.8,1.1,7.1l-6,8.7c-1.1,1.5-2.6,2.5-4.4,2.8h0Z"/>
27
- <title id="altIconTitle">{{ 'MAX_ATTACHMENT' | translate: { FILE_SIZE_LIMIT: file_size_limit } }}</title>
28
- </svg>
29
-
30
- </span>
31
- <input
32
- [attr.disabled] = "(isFilePendingToUpload || isConversationArchived || hideTextReply)? true : null"
33
- tabindex="1503"
34
- type="file"
35
- aria-label="seleziona allegato"
36
- [accept]="fileUploadAccept"
37
- name="chat21-file"
38
- id="chat21-file"
39
- #chat21_file
40
- class="inputfile"
41
- [ngStyle]="{'display': 'block', height:'1px', width:'1px', overflow: 'hidden' }"
42
- (change)="detectFiles($event)"/>
43
- </label>
44
- <!-- ICON EMOJII -->
45
- <label *ngIf="showEmojiFooterButton" tabindex="1504" aria-label="emojii" for="chat21-emojii" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-emoticon-picker" (click)="onEmojiiPickerClicked()">
46
- <span class="v-align-center">
47
- <svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
48
- <path stroke-width=".4px" stroke="currentColor" d="M12,20.8c-5.1,0-9.3-4.2-9.3-9.3S6.9,2.2,12,2.2s9.3,4.2,9.3,9.3-4.2,9.3-9.3,9.3ZM12,3.6c-4.4,0-7.9,3.6-7.9,7.9s3.6,7.9,7.9,7.9,7.9-3.6,7.9-7.9-3.6-7.9-7.9-7.9Z"/>
49
- <path stroke-width=".4px" stroke="currentColor" d="M12,17.2c-2.7,0-4.3-1.9-4.6-2.3-.2-.3-.2-.7.1-1s.7-.2,1,.1c.1.2,1.4,1.8,3.5,1.8s2.2,0,3.5-1.8c.2-.3.7-.4,1-.1s.4.7.1,1c-1.7,2.2-4.1,2.3-4.6,2.3Z"/>
50
- <path d="M8.7,10.9c-.9,0-1.6-.7-1.6-1.6s.7-1.6,1.6-1.6,1.6.7,1.6,1.6-.7,1.6-1.6,1.6Z"/>
51
- <path d="M15.5,10.9c-.9,0-1.6-.7-1.6-1.6s.7-1.6,1.6-1.6,1.6.7,1.6,1.6-.7,1.6-1.6,1.6Z"/>
52
- <title id="altIconTitle">{{ translationMap?.get('EMOJI') }}</title>
53
-
54
- <!-- <path d="M0,0H20.57V20.57H0V0Z" fill="none"/>
55
- <circle cx="15.02" cy="9.86" r="1.29"/>
56
- <circle cx="9.02" cy="9.86" r="1.29"/>
57
- <path d="M12.02,15.43c-1.27,0-2.36-.69-2.96-1.71h-1.43c.69,1.76,2.39,3,4.39,3s3.7-1.24,4.39-3h-1.43c-.6,1.02-1.69,1.71-2.96,1.71Zm0-12C7.28,3.43,3.45,7.27,3.45,12s3.83,8.57,8.56,8.57,8.58-3.84,8.58-8.57S16.75,3.43,12.01,3.43Zm0,15.43c-3.79,0-6.86-3.07-6.86-6.86s3.07-6.86,6.86-6.86,6.86,3.07,6.86,6.86-3.07,6.86-6.86,6.86Z"/> -->
58
- </svg>
59
- </span>
60
- </label>
61
- </div>
17
+ <!-- TEXTAREA + ICONS: conv active-->
18
+ <div class="textarea-container" *ngIf="!hideTextAreaContent && !hideTextReply">
19
+
20
+ <div *ngIf="!isStopRec" class="icons-container">
21
+ <!-- ICON ATTACHMENT -->
22
+ <label *ngIf="showAttachmentFooterButton" tabindex="1502" aria-label="allegati" for="chat21-file" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-start-upload-doc">
23
+ <span class="v-align-center">
24
+ <svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" width="24px" height="24" viewBox="0 0 24 24" fill="currentColor">
25
+ <path d="M9.9,22.7c0,0-.1,0-.2,0-1.9.3-3.7-.2-5.2-1.4-3-2.3-3.6-6.4-1.4-9.5L9.5,2.5c.4-.5,1.1-.6,1.6-.3.5.4.6,1.1.3,1.6l-6.5,9.4c-1.4,2-1,4.8.9,6.3,1,.8,2.2,1.1,3.5.9,1.3-.2,2.4-.9,3.1-1.9l6-8.7c.9-1.2.6-3-.6-3.9-.6-.5-1.4-.6-2.1-.5-.8.1-1.4.5-1.9,1.1l-5.8,8.2c-.3.5-.2,1.1.2,1.5.2.2.5.3.8.2.3,0,.6-.2.7-.4l4.7-6.2c.4-.5,1.1-.6,1.6-.2.5.4.6,1.1.2,1.6l-4.7,6.2c-.5.7-1.4,1.2-2.3,1.3-.9.1-1.8-.2-2.5-.7-1.4-1.1-1.6-3.1-.6-4.6l5.8-8.2c.8-1.1,2-1.9,3.4-2.1,1.4-.2,2.7.1,3.8,1,2.2,1.7,2.7,4.8,1.1,7.1l-6,8.7c-1.1,1.5-2.6,2.5-4.4,2.8h0Z"/>
26
+ <title id="altIconTitle">{{ 'MAX_ATTACHMENT' | translate: { FILE_SIZE_LIMIT: file_size_limit } }}</title>
27
+ </svg>
28
+
29
+ </span>
30
+ <input
31
+ [attr.disabled] = "(isFilePendingToUpload || isConversationArchived || hideTextReply)? true : null"
32
+ tabindex="1503"
33
+ type="file"
34
+ aria-label="seleziona allegato"
35
+ [accept]="fileUploadAccept"
36
+ name="chat21-file"
37
+ id="chat21-file"
38
+ #chat21_file
39
+ class="inputfile"
40
+ [ngStyle]="{'display': 'block', height:'1px', width:'1px', overflow: 'hidden' }"
41
+ (change)="detectFiles($event)"/>
42
+ </label>
43
+ <!-- ICON EMOJII -->
44
+ <label *ngIf="showEmojiFooterButton" tabindex="1504" aria-label="emojii" for="chat21-emojii" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-emoticon-picker" (click)="onEmojiiPickerClicked()">
45
+ <span class="v-align-center">
46
+ <svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
47
+ <path stroke-width=".4px" stroke="currentColor" d="M12,20.8c-5.1,0-9.3-4.2-9.3-9.3S6.9,2.2,12,2.2s9.3,4.2,9.3,9.3-4.2,9.3-9.3,9.3ZM12,3.6c-4.4,0-7.9,3.6-7.9,7.9s3.6,7.9,7.9,7.9,7.9-3.6,7.9-7.9-3.6-7.9-7.9-7.9Z"/>
48
+ <path stroke-width=".4px" stroke="currentColor" d="M12,17.2c-2.7,0-4.3-1.9-4.6-2.3-.2-.3-.2-.7.1-1s.7-.2,1,.1c.1.2,1.4,1.8,3.5,1.8s2.2,0,3.5-1.8c.2-.3.7-.4,1-.1s.4.7.1,1c-1.7,2.2-4.1,2.3-4.6,2.3Z"/>
49
+ <path d="M8.7,10.9c-.9,0-1.6-.7-1.6-1.6s.7-1.6,1.6-1.6,1.6.7,1.6,1.6-.7,1.6-1.6,1.6Z"/>
50
+ <path d="M15.5,10.9c-.9,0-1.6-.7-1.6-1.6s.7-1.6,1.6-1.6,1.6.7,1.6,1.6-.7,1.6-1.6,1.6Z"/>
51
+ <title id="altIconTitle">{{ translationMap?.get('EMOJI') }}</title>
62
52
 
53
+ <!-- <path d="M0,0H20.57V20.57H0V0Z" fill="none"/>
54
+ <circle cx="15.02" cy="9.86" r="1.29"/>
55
+ <circle cx="9.02" cy="9.86" r="1.29"/>
56
+ <path d="M12.02,15.43c-1.27,0-2.36-.69-2.96-1.71h-1.43c.69,1.76,2.39,3,4.39,3s3.7-1.24,4.39-3h-1.43c-.6,1.02-1.69,1.71-2.96,1.71Zm0-12C7.28,3.43,3.45,7.27,3.45,12s3.83,8.57,8.56,8.57,8.58-3.84,8.58-8.57S16.75,3.43,12.01,3.43Zm0,15.43c-3.79,0-6.86-3.07-6.86-6.86s3.07-6.86,6.86-6.86,6.86,3.07,6.86,6.86-3.07,6.86-6.86,6.86Z"/> -->
57
+ </svg>
58
+ </span>
59
+ </label>
60
+ </div>
63
61
 
64
62
 
65
63
 
66
- <div *ngIf="!isStopRec" class="visible-text-area" [class.hasError]="showAlertEmoji" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
67
- <!-- isFilePendingToUpload || -->
68
- <textarea
69
- [attr.disabled] = "(hideTextReply)? true : null"
70
- [attr.placeholder] ="(footerMessagePlaceholder)? footerMessagePlaceholder : translationMap?.get('LABEL_PLACEHOLDER')"
71
- start-focus-chat21-conversation-component
72
- inputTextArea
73
- #textbox
74
- tabindex="1501"
75
- aria-labelledby="altTextArea"
76
- rows="1"
77
- id="chat21-main-message-context"
78
- class='f21textarea c21-button-clean'
79
- [(ngModel)]="textInputTextArea"
80
- (ngModelChange)="onTextAreaChange()"
81
- (keypress)="onkeypress($event)"
82
- (keydown)="onkeydown($event)"
83
- (paste)="onPaste($event)">
84
- </textarea>
85
-
86
- </div>
87
64
 
88
- <!-- ICON SEND -->
89
- <div *ngIf="(textInputTextArea !== '' && !isStopRec) || !showAudioRecorderFooterButton" tabindex="-1" class="chat21-textarea-button" [class.disabled]="showAlertEmoji" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
90
- <span class="v-align-center">
91
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="24" width="24" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve" fill="currentColor">
92
- <path d="M1.8,20.6V3.4l20.2,8.6L1.8,20.6ZM3.9,17.3l12.6-5.4L3.9,6.6v3.7l6.4,1.6-6.4,1.6v3.8ZM3.9,17.3V6.6v10.7Z"/>
93
- </svg>
94
- </span>
95
- </div>
65
+ <div *ngIf="!isStopRec" class="visible-text-area" [class.hasError]="showAlertEmoji" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
66
+ <!-- isFilePendingToUpload || -->
67
+ <textarea
68
+ [attr.disabled] = "(hideTextReply)? true : null"
69
+ [attr.placeholder] ="(footerMessagePlaceholder)? footerMessagePlaceholder : translationMap?.get('LABEL_PLACEHOLDER')"
70
+ start-focus-chat21-conversation-component
71
+ inputTextArea
72
+ #textbox
73
+ tabindex="1501"
74
+ aria-labelledby="altTextArea"
75
+ rows="1"
76
+ id="chat21-main-message-context"
77
+ class='f21textarea c21-button-clean'
78
+ [(ngModel)]="textInputTextArea"
79
+ (ngModelChange)="onTextAreaChange()"
80
+ (keypress)="onkeypress($event)"
81
+ (keydown)="onkeydown($event)"
82
+ (paste)="onPaste($event)">
83
+ </textarea>
84
+
85
+ </div>
96
86
 
97
- <!-- ICON REC -->
98
- <div *ngIf="showAudioRecorderFooterButton && !textInputTextArea" tabindex="-1" class="chat21-audio-button" [class.active]="isStopRec" id="chat21-button-rec">
99
- <chat-audio-recorder
100
- (startRecordingEvent)="onStartRecording()"
101
- (deleteRecordingEvent)="onDeleteRecording()"
102
- (endRecordingEvent)="onEndRecording($event)"
103
- (sendRecordingEvent)="onSendRecording($event)"
104
- [stylesMap]="stylesMap">
105
- </chat-audio-recorder>
106
- </div>
87
+ <!-- ICON SEND -->
88
+ <div *ngIf="(textInputTextArea !== '' && !isStopRec) || !showAudioRecorderFooterButton" tabindex="-1" class="chat21-textarea-button" [class.disabled]="showAlertEmoji" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
89
+ <span class="v-align-center">
90
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="24" width="24" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve" fill="currentColor">
91
+ <path d="M1.8,20.6V3.4l20.2,8.6L1.8,20.6ZM3.9,17.3l12.6-5.4L3.9,6.6v3.7l6.4,1.6-6.4,1.6v3.8ZM3.9,17.3V6.6v10.7Z"/>
92
+ </svg>
93
+ </span>
107
94
  </div>
108
95
 
109
- <div class="close-chat-container" *ngIf="closeChatInConversation">
110
- <button tabindex="1040" aflistconv #aflistconv class="c21-button-primary c21-close" (click)="onCloseChat($event)" [ngStyle]="{'background-color': stylesMap.get('themeColor'), 'border-color': stylesMap.get('themeColor'), 'color': stylesMap?.get('foregroundColor')}">
111
- <span class="v-align-center">
112
- <!-- <svg [ngStyle]="{'fill': 'yellow' }" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
113
- <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" [ngStyle]="{'fill': stylesMap?.get('foregroundColor')}"/>
114
- </svg> -->
115
- <svg [ngStyle]="{'stroke': stylesMap?.get('foregroundColor'), 'fill': stylesMap?.get('foregroundColor') }" role="img" id="archive" aria-labelledby="altIconTitle" class="icon-menu" xmlns="http://www.w3.org/2000/svg"
116
- width="15px" height="15px" viewBox="0 0 512 512">
117
- <path d="M80 152v256a40.12 40.12 0 0040 40h272a40.12 40.12 0 0040-40V152" stroke-linecap="round" stroke-linejoin="round" stroke-width="50px" fill="none"></path>
118
- <rect x="48" y="64" width="416" height="80" rx="28" ry="28" stroke-linejoin="round" stroke-width="50px" fill="none" ></rect>
119
- <path stroke-linecap="round" stroke-linejoin="round" d="M320 304l-64 64-64-64M256 345.89V224" stroke-width="50px" fill="none"></path>
120
- <title id="altIconTitle">{{ translationMap?.get('CLOSE_CHAT') }}</title>
121
- </svg>
122
- </span>
123
- <span class="v-align-center c21-label-button">
124
- {{translationMap?.get('CLOSE_CHAT')}}
125
- </span>
126
- <div class="clear"></div>
127
- </button>
96
+ <!-- ICON REC -->
97
+ <div *ngIf="showAudioRecorderFooterButton && !textInputTextArea" tabindex="-1" class="chat21-audio-button" [class.active]="isStopRec" id="chat21-button-rec">
98
+ <chat-audio-recorder
99
+ (startRecordingEvent)="onStartRecording()"
100
+ (deleteRecordingEvent)="onDeleteRecording()"
101
+ (endRecordingEvent)="onEndRecording($event)"
102
+ (sendRecordingEvent)="onSendRecording($event)"
103
+ [stylesMap]="stylesMap">
104
+ </chat-audio-recorder>
128
105
  </div>
129
106
  </div>
130
107
 
@@ -1,25 +1,23 @@
1
- .textarea-container-wrapper{
2
- display: flex;
3
- flex-direction: column;
4
- gap: 8px;
5
- }
1
+
6
2
  .textarea-container{
3
+ // padding: 8px 34px;
4
+ // padding-left: 70px;
5
+ // padding-right: 45px;
7
6
  display: flex;
7
+ // width: 100%;
8
8
  align-items: center;
9
9
  justify-content: space-between;
10
10
  gap: 8px;
11
- }
12
- .close-chat-container{
13
- display: flex;
14
- flex-direction: column;
15
- align-items: center;
16
- justify-content: center;
17
- gap: 8px;
18
11
 
19
- .c21-close{
20
- height: 30px !important;
21
- margin: 0px !important;
12
+ //if attachment icon OR emoji icon is not in DOM -> increment textarea width
13
+ &:has(:not(#chat21-start-upload-doc), :not(#chat21-emoticon-picker)) .visible-text-area {
14
+ width: 80%;
22
15
  }
16
+ //if attachment icon AND emoji icon is not in DOM -> increment textarea width
17
+ &:has(:not(#chat21-start-upload-doc)):has(:not(#chat21-emoticon-picker)) .visible-text-area {
18
+ width: 90%;
19
+ }
20
+
23
21
  }
24
22
 
25
23
  .icons-container{
@@ -421,28 +419,3 @@ textarea:active{
421
419
  border: none;
422
420
  // margin: -2px -2px 0px;
423
421
  }
424
-
425
-
426
- // aggiungi un'animazione di fade in e fade out quando .star-rating-widget è visibile con transition
427
- .star-rating-widget {
428
- transition: all 0.5s ease-in-out;
429
- }
430
-
431
- .star-rating-widget {
432
- position: absolute;
433
- left: 0;
434
- right: 0;
435
- bottom: -52px;
436
- height: 100%;
437
- width: 100%;
438
- flex-direction: row;
439
- justify-content: center;
440
- background-color: rgb(255, 255, 255);
441
- flex-wrap: nowrap;
442
- &.active {
443
- bottom: 0px;
444
- }
445
- &.inactive {
446
- bottom: -52px;
447
- }
448
- }
@@ -42,7 +42,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
42
42
  @Input() isEmojiiPickerShow: boolean;
43
43
  @Input() footerMessagePlaceholder: string;
44
44
  @Input() fileUploadAccept: string;
45
- @Input() closeChatInConversation: boolean;
46
45
  @Input() dropEvent: Event;
47
46
  @Input() poweredBy: string;
48
47
  @Input() stylesMap: Map<string, string>
@@ -53,7 +52,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
53
52
  @Output() onChangeTextArea = new EventEmitter<any>();
54
53
  @Output() onAttachmentFileButtonClicked = new EventEmitter<any>();
55
54
  @Output() onNewConversationButtonClicked = new EventEmitter();
56
- @Output() onCloseChatButtonClicked = new EventEmitter();
57
55
 
58
56
  @ViewChild('chat21_file') public chat21_file: ElementRef;
59
57
  // @ViewChild('emojii_container', {read: ViewContainerRef}) selector;
@@ -89,7 +87,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
89
87
 
90
88
  file_size_limit = FILE_SIZE_LIMIT;
91
89
  attachmentTooltip: string = '';
92
- isErrorNetwork: boolean = false;
93
90
 
94
91
 
95
92
  convertColorToRGBA = convertColorToRGBA;
@@ -712,10 +709,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
712
709
  this.onNewConversationButtonClicked.emit();
713
710
  }
714
711
 
715
- onCloseChat(event){
716
- this.onCloseChatButtonClicked.emit();
717
- }
718
-
719
712
  // onContinueConversation(){
720
713
  // this.hideTextAreaContent = false;
721
714
  // this.onBackButton.emit(false)
@@ -16,7 +16,7 @@
16
16
  </button>
17
17
 
18
18
  <!-- ICON MENU OPTION -->
19
- <button *ngIf="!isMobile" [attr.disabled]="(isButtonsDisabled)?true:null" tabindex="-1" class="c21-header-button c21-right c21-button-clean" [ngStyle]="{'display': (hideHeaderConversationOptionsMenu)?'none':'flex'}" (click)="toggleMenu()" >
19
+ <button [attr.disabled]="(isButtonsDisabled)?true:null" tabindex="-1" class="c21-header-button c21-right c21-button-clean" [ngStyle]="{'display': (hideHeaderConversationOptionsMenu)?'none':'flex'}" (click)="toggleMenu()" >
20
20
  <svg aria-labelledby="altIconTitle" [ngStyle]="{'fill': stylesMap?.get('foregroundColor') }" xmlns="http://www.w3.org/2000/svg"
21
21
  width="24" height="24" viewBox="0 0 24 24">
22
22
  <path fill="none" d="M0 0h24v24H0V0z" />