@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.
- package/.github/workflows/docker-community-push-latest.yml +13 -23
- package/.github/workflows/docker-image-tag-community-tag-push.yml +12 -22
- package/CHANGELOG.md +28 -82
- package/Dockerfile +5 -4
- package/angular.json +1 -2
- package/deploy_amazon_beta.sh +7 -17
- package/deploy_amazon_prod.sh +41 -0
- package/docs/changelog/badge_Bot_Umano.md +85 -0
- package/docs/changelog/this-branch.md +35 -24
- package/package.json +1 -1
- package/src/app/app.component.ts +17 -6
- package/src/app/component/conversation-detail/conversation/conversation.component.html +1 -3
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -2
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +27 -153
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -1
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +80 -103
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +13 -40
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +0 -7
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +1 -1
- package/src/app/providers/global-settings.service.ts +2 -25
- package/src/app/providers/translator.service.ts +0 -2
- package/src/app/sass/_variables.scss +0 -1
- package/src/app/utils/conversation-sender-classifier.ts +116 -0
- package/src/app/utils/globals.ts +26 -7
- package/src/assets/i18n/en.json +0 -1
- package/src/assets/i18n/es.json +0 -1
- package/src/assets/i18n/fr.json +0 -1
- package/src/assets/i18n/it.json +0 -1
- package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
- 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)
|
|
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) +
|
|
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
|
-
*
|
|
429
|
-
*
|
|
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
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
632
|
-
|
|
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.
|
|
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
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
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)
|
|
47
|
+
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
|
|
48
48
|
overflow: hidden;
|
|
49
49
|
.time{
|
|
50
50
|
margin-bottom: 20px;
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html
CHANGED
|
@@ -14,117 +14,94 @@
|
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
}
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts
CHANGED
|
@@ -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)
|
package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
</button>
|
|
17
17
|
|
|
18
18
|
<!-- ICON MENU OPTION -->
|
|
19
|
-
<button
|
|
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" />
|