@chat21/chat21-web-widget 5.1.30 → 5.1.32-rc13
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 +89 -2
- package/Dockerfile +4 -5
- package/angular.json +5 -2
- package/deploy_amazon_beta.sh +17 -7
- package/docs/changelog/this-branch.md +36 -0
- package/nginx.conf +22 -2
- package/package.json +4 -1
- package/src/app/app.component.ts +10 -9
- package/src/app/app.module.ts +11 -0
- package/src/app/component/conversation-detail/conversation/conversation.component.html +9 -2
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +12 -2
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +46 -5
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +9 -5
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +19 -1
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +2 -0
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +128 -80
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +117 -13
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +120 -8
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +43 -0
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +79 -0
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +192 -0
- package/src/app/component/last-message/last-message.component.ts +4 -1
- package/src/app/component/message/audio/audio.component.ts +0 -5
- package/src/app/component/message/audio-sync/audio-sync.component.html +18 -0
- package/src/app/component/message/audio-sync/audio-sync.component.scss +64 -0
- package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +23 -0
- package/src/app/component/message/audio-sync/audio-sync.component.ts +558 -0
- package/src/app/component/message/bubble-message/bubble-message.component.html +6 -1
- package/src/app/component/message/bubble-message/bubble-message.component.ts +2 -1
- package/src/app/providers/global-settings.service.ts +21 -0
- package/src/app/providers/translator.service.ts +2 -0
- package/src/app/providers/tts-audio-playback-coordinator.service.ts +93 -0
- package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +12 -0
- package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +171 -0
- package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +39 -0
- package/src/app/providers/voice/audio.types.ts +34 -0
- package/src/app/providers/voice/vad.service.spec.ts +28 -0
- package/src/app/providers/voice/vad.service.ts +70 -0
- package/src/app/providers/voice/voice.service.spec.ts +60 -0
- package/src/app/providers/voice/voice.service.ts +376 -0
- package/src/app/sass/_variables.scss +3 -0
- package/src/app/shims/onnxruntime-web-wasm.ts +4 -0
- package/src/app/utils/conversation-sender-classifier.ts +21 -0
- package/src/app/utils/globals.ts +7 -1
- package/src/assets/i18n/en.json +1 -0
- package/src/assets/i18n/es.json +1 -0
- package/src/assets/i18n/fr.json +1 -0
- package/src/assets/i18n/it.json +1 -0
- package/src/assets/onnx/ort-wasm-simd-threaded.mjs +59 -0
- package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
- package/src/assets/vad/silero_vad_legacy.onnx +0 -0
- package/src/assets/vad/vad.worklet.bundle.min.js +1 -0
- package/src/chat21-core/models/message.ts +2 -1
- package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +3 -2
- package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +12 -0
- package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
- package/src/chat21-core/utils/utils-message.ts +7 -0
- package/src/chat21-core/utils/utils.ts +5 -2
- package/src/launch.js +41 -32
- package/src/launch_template.js +41 -32
- package/tsconfig.json +5 -0
- package/deploy_amazon_prod.sh +0 -41
|
@@ -161,6 +161,10 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
161
161
|
membersConversation = ['SYSTEM'];
|
|
162
162
|
// ========== end:: typying =======
|
|
163
163
|
|
|
164
|
+
// ========== begin:: stream audio ======= //
|
|
165
|
+
public isStreamAudioActive = false;
|
|
166
|
+
// ========== end:: stream audio ======= //
|
|
167
|
+
|
|
164
168
|
@ViewChild(ConversationFooterComponent) conversationFooter: ConversationFooterComponent
|
|
165
169
|
@ViewChild(ConversationContentComponent) conversationContent: ConversationContentComponent
|
|
166
170
|
conversationHandlerService: ConversationHandlerService
|
|
@@ -246,7 +250,9 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
246
250
|
'CONTINUE',
|
|
247
251
|
'EMOJI_NOT_ELLOWED',
|
|
248
252
|
'ATTACHMENT',
|
|
249
|
-
'EMOJI'
|
|
253
|
+
'EMOJI',
|
|
254
|
+
'CLOSE_CHAT',
|
|
255
|
+
'CLOSE'
|
|
250
256
|
];
|
|
251
257
|
|
|
252
258
|
const keysContent = [
|
|
@@ -501,7 +507,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
501
507
|
return this.isConversationArchived;
|
|
502
508
|
}
|
|
503
509
|
|
|
504
|
-
//FALLBACK TO TILEDESK
|
|
510
|
+
// //FALLBACK TO TILEDESK
|
|
505
511
|
const requests_list = await this.tiledeskRequestService.getMyRequests().catch(err => {
|
|
506
512
|
this.logger.error('[CONV-COMP] getConversationDetail: error getting request from Tiledesk', err);
|
|
507
513
|
this.isConversationArchived=true
|
|
@@ -519,9 +525,9 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
519
525
|
return this.isConversationArchived
|
|
520
526
|
}
|
|
521
527
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
528
|
+
this.isConversationArchived = false;
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
525
531
|
|
|
526
532
|
/**
|
|
527
533
|
* this.g.recipientId:
|
|
@@ -822,6 +828,10 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
822
828
|
this.showThinkingMessage = false;
|
|
823
829
|
}
|
|
824
830
|
|
|
831
|
+
if (this.isStreamAudioActive && msg.sender !== this.senderId) {
|
|
832
|
+
this.conversationFooter?.interruptStreamDueToPeerMessage();
|
|
833
|
+
}
|
|
834
|
+
|
|
825
835
|
that.newMessageAdded(msg);
|
|
826
836
|
// Update badge based on the latest message received from the server.
|
|
827
837
|
// We rely on `messages` being kept in-sync by the conversation handler.
|
|
@@ -877,6 +887,20 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
877
887
|
this.subscriptions.push(subscribe);
|
|
878
888
|
}
|
|
879
889
|
|
|
890
|
+
subscribtionKey = 'conversationsAdded';
|
|
891
|
+
subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
|
|
892
|
+
if(!subscribtion){
|
|
893
|
+
|
|
894
|
+
subscribtion = this.chatManager.conversationsHandlerService.conversationChanged.pipe(takeUntil(this.unsubscribe$)).subscribe((conversation) => {
|
|
895
|
+
this.logger.debug('[CONV-COMP] ***** DATAIL conversationsChanged *****', conversation, this.conversationWith, this.isConversationArchived);
|
|
896
|
+
if(conversation && conversation.recipient === this.conversationId){
|
|
897
|
+
this.isConversationArchived = false
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
const subscribe = {key: subscribtionKey, value: subscribtion };
|
|
901
|
+
this.subscriptions.push(subscribe);
|
|
902
|
+
}
|
|
903
|
+
|
|
880
904
|
subscribtionKey = 'messageWait';
|
|
881
905
|
subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
|
|
882
906
|
if (!subscribtion) {
|
|
@@ -1383,8 +1407,25 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1383
1407
|
this.logger.debug('[CONV-COMP] floating onNewConversationButtonClicked')
|
|
1384
1408
|
this.onNewConversationButtonClicked.emit()
|
|
1385
1409
|
}
|
|
1410
|
+
|
|
1411
|
+
/** CALLED BY: conv-footer streaming audio button */
|
|
1412
|
+
onStreamAudioActiveChange(event: boolean){
|
|
1413
|
+
this.isStreamAudioActive = event
|
|
1414
|
+
}
|
|
1415
|
+
/** CALLED BY: conv-footer component */
|
|
1416
|
+
onCloseChatButtonClickedFN(event){
|
|
1417
|
+
this.logger.debug('[CONV-COMP] onCloseChatButtonClicked::::', event)
|
|
1418
|
+
this.onCloseChat()
|
|
1419
|
+
}
|
|
1386
1420
|
// =========== END: event emitter function ====== //
|
|
1387
1421
|
|
|
1422
|
+
/**
|
|
1423
|
+
* True quando è visibile il pulsante chiudi stream (`.close-stream-button`, `isStreamAudioActive`).
|
|
1424
|
+
* Solo in quel caso il bottom del foglio include `--chat-footer-stream-button-height`.
|
|
1425
|
+
*/
|
|
1426
|
+
closeStreamButtonActiveForSheetBottom(): boolean {
|
|
1427
|
+
return !!(this.g?.showAudioStreamFooterButton && this.isStreamAudioActive);
|
|
1428
|
+
}
|
|
1388
1429
|
|
|
1389
1430
|
openInputFiles() {
|
|
1390
1431
|
alert('ok');
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
<div *ngFor="let message of messages; let first = first; let last = last; let i = index" tabindex="1521" class="rowMsg">
|
|
23
23
|
|
|
24
24
|
<!-- message SENDER:: -->
|
|
25
|
-
<div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_MINE, message)" class="msg_container base_sent">
|
|
25
|
+
<div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_MINE, message) && (!isStreamAudioActive && !message.isJustRecived)" class="msg_container base_sent">
|
|
26
26
|
|
|
27
27
|
<!--backgroundColor non viene ancora usato -->
|
|
28
28
|
<!-- class="messages msg_sent slide-in-right" -->
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
<!-- message RECIPIENT:: -->
|
|
50
50
|
<div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_OTHERS, message)" class="msg_container base_receive">
|
|
51
51
|
|
|
52
|
-
<chat-avatar-image *ngIf="!isSameSender(message?.sender, i)"
|
|
52
|
+
<chat-avatar-image *ngIf="!isSameSender(message?.sender, i) && !isStreamAudioActive"
|
|
53
53
|
[ngClass]="{'slide-in-left': false}"
|
|
54
54
|
[senderID]="message?.sender"
|
|
55
55
|
[senderFullname]="message?.sender_fullname"
|
|
@@ -62,7 +62,8 @@
|
|
|
62
62
|
[ngClass]="{'slide-in-left': false}"
|
|
63
63
|
[class.no-background]="(isImage(message) || isFrame(message) || isCarousel(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
64
64
|
[class.emoticon]="isEmojii(message?.text)"
|
|
65
|
-
[
|
|
65
|
+
[class.fullSizeMessage]="isStreamAudioActive"
|
|
66
|
+
[style.margin-left]="isSameSender(message?.sender, i) ? 'calc(var(--avatar-width) + 10px)' : null"
|
|
66
67
|
[ngStyle]="{'background': stylesMap.get('bubbleReceivedBackground'), 'color': stylesMap.get('bubbleReceivedTextColor'), 'width':isFrame(message) ?'100%' : null}"
|
|
67
68
|
[isSameSender]="isSameSender(message?.sender, i)"
|
|
68
69
|
[message]="message"
|
|
@@ -134,18 +135,21 @@
|
|
|
134
135
|
[senderFullname]="nameUserTypingNow"
|
|
135
136
|
[baseLocation]="baseLocation">
|
|
136
137
|
</chat-avatar-image>
|
|
138
|
+
|
|
137
139
|
<user-typing
|
|
138
|
-
[ngClass]="{'userTypingNowExist': !idUserTypingNow}"
|
|
139
140
|
[color]="stylesMap?.get('iconColor')"
|
|
141
|
+
[ngClass]="{'userTypingNowExist': !idUserTypingNow}"
|
|
140
142
|
[translationMap]="translationMap"
|
|
141
143
|
[idUserTypingNow]="idUserTypingNow"
|
|
142
144
|
[nameUserTypingNow]="nameUserTypingNow">
|
|
143
145
|
</user-typing>
|
|
144
146
|
</div>
|
|
145
147
|
|
|
146
|
-
<div *ngIf="showThinkingMessage" class="msg_container base_receive thinking_receive">
|
|
148
|
+
<div *ngIf="showThinkingMessage && lastServerSenderKind === 'bot'" class="msg_container base_receive thinking_receive">
|
|
147
149
|
<user-typing class="loading thinking-dots"
|
|
150
|
+
[class.fullSize]="isStreamAudioActive"
|
|
148
151
|
[color]="stylesMap?.get('iconColor')"
|
|
152
|
+
[class.fullSize]="isStreamAudioActive"
|
|
149
153
|
[translationMap]="translationMap"
|
|
150
154
|
[idUserTypingNow]="idUserTypingNow"
|
|
151
155
|
[nameUserTypingNow]="nameUserTypingNow">
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
margin: 25px 50px
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
:host .loading.fullSize ::ng-deep > div.spinner{
|
|
31
|
+
margin: 50px 0px !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
// ============= CSS c21-body ================= //
|
|
31
35
|
.c21-body {
|
|
32
36
|
// -webkit-box-shadow: inset 0 10px 10px -10px rgba(0,0,0,0.4);
|
|
@@ -44,7 +48,7 @@
|
|
|
44
48
|
top: 0;
|
|
45
49
|
right: 0;
|
|
46
50
|
left: 0;
|
|
47
|
-
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
|
|
51
|
+
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height));
|
|
48
52
|
overflow: hidden;
|
|
49
53
|
.time{
|
|
50
54
|
margin-bottom: 20px;
|
|
@@ -236,6 +240,11 @@
|
|
|
236
240
|
height: fit-content;
|
|
237
241
|
width: auto;
|
|
238
242
|
|
|
243
|
+
&.fullSizeMessage {
|
|
244
|
+
max-width: 100%;
|
|
245
|
+
margin: auto 0 auto 0 !important;
|
|
246
|
+
}
|
|
247
|
+
|
|
239
248
|
}
|
|
240
249
|
|
|
241
250
|
|
|
@@ -270,6 +279,15 @@
|
|
|
270
279
|
}// end c21-body-container
|
|
271
280
|
}// end c21-body
|
|
272
281
|
|
|
282
|
+
/* Solo con pulsante chiudi stream (stream in ascolto): altezza extra come #streamAudioAlert */
|
|
283
|
+
:host-context(#chat21-conversation-component.chat21-conversation--close-stream-active) .c21-body .c21-body-container .c21-body-content .chat21-sheet-content {
|
|
284
|
+
bottom: calc(
|
|
285
|
+
var(--chat-footer-logo-height) +
|
|
286
|
+
var(--chat-footer-height) +
|
|
287
|
+
var(--chat-footer-stream-button-height)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
273
291
|
@keyframes thinking-dot {
|
|
274
292
|
0%, 80%, 100% {
|
|
275
293
|
opacity: 0.2;
|
package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts
CHANGED
|
@@ -24,7 +24,9 @@ export class ConversationContentComponent implements OnInit {
|
|
|
24
24
|
@Input() nameUserTypingNow: string;
|
|
25
25
|
@Input() typingLocation: string;
|
|
26
26
|
@Input() showThinkingMessage: boolean;
|
|
27
|
+
@Input() lastServerSenderKind: 'bot' | 'human' | null;
|
|
27
28
|
@Input() fullscreenMode: boolean;
|
|
29
|
+
@Input() isStreamAudioActive: boolean;
|
|
28
30
|
@Input() translationMap: Map< string, string>;
|
|
29
31
|
@Input() stylesMap: Map<string, string>;
|
|
30
32
|
@Output() onBeforeMessageRender = new EventEmitter();
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html
CHANGED
|
@@ -12,97 +12,145 @@
|
|
|
12
12
|
<div tabindex="-1" class="alertText">{{translationMap.get('EMOJI_NOT_ELLOWED')}}</div>
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
|
+
<!-- STREAM AUDIO: cerchio con onde animate -->
|
|
16
|
+
<!-- <div id="streamAudioAlert" *ngIf="!hideTextAreaContent && isStreamAudioActive" class="fade-in-bottom stream-audio-alert" [class.hideTextReply]="hideTextReply" role="status" [attr.aria-label]="translationMap?.get('STREAM_AUDIO_LISTENING') || 'Stream audio attivo'">
|
|
17
|
+
<chat-stream-audio-spectrum
|
|
18
|
+
[volume]="currentVolume"
|
|
19
|
+
[accentColor]="isBotSpeaking ? '#b0b0b0' : stylesMap?.get('themeColor')">
|
|
20
|
+
</chat-stream-audio-spectrum>
|
|
21
|
+
</div> -->
|
|
22
|
+
|
|
15
23
|
</div>
|
|
16
24
|
|
|
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
|
-
|
|
25
|
+
<div class="textarea-container-wrapper" *ngIf="!hideTextAreaContent && !hideTextReply">
|
|
26
|
+
<!-- TEXTAREA + ICONS: conv active-->
|
|
27
|
+
<div class="textarea-container">
|
|
28
|
+
|
|
29
|
+
<div *ngIf="!isStopRec" class="icons-container">
|
|
30
|
+
<!-- ICON ATTACHMENT -->
|
|
31
|
+
<label *ngIf="showAttachmentFooterButton" tabindex="1502" aria-label="allegati" for="chat21-file" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-start-upload-doc">
|
|
32
|
+
<span class="v-align-center">
|
|
33
|
+
<svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" width="24px" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
34
|
+
<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"/>
|
|
35
|
+
<title id="altIconTitle">{{ 'MAX_ATTACHMENT' | translate: { FILE_SIZE_LIMIT: file_size_limit } }}</title>
|
|
36
|
+
</svg>
|
|
37
|
+
|
|
38
|
+
</span>
|
|
39
|
+
<input
|
|
40
|
+
[attr.disabled] = "(isFilePendingToUpload || isConversationArchived || hideTextReply)? true : null"
|
|
41
|
+
tabindex="1503"
|
|
42
|
+
type="file"
|
|
43
|
+
aria-label="seleziona allegato"
|
|
44
|
+
[accept]="fileUploadAccept"
|
|
45
|
+
name="chat21-file"
|
|
46
|
+
id="chat21-file"
|
|
47
|
+
#chat21_file
|
|
48
|
+
class="inputfile"
|
|
49
|
+
[ngStyle]="{'display': 'block', height:'1px', width:'1px', overflow: 'hidden' }"
|
|
50
|
+
(change)="detectFiles($event)"/>
|
|
51
|
+
</label>
|
|
52
|
+
<!-- ICON EMOJII -->
|
|
53
|
+
<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()">
|
|
54
|
+
<span class="v-align-center">
|
|
55
|
+
<svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
|
56
|
+
<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"/>
|
|
57
|
+
<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"/>
|
|
58
|
+
<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"/>
|
|
59
|
+
<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"/>
|
|
60
|
+
<title id="altIconTitle">{{ translationMap?.get('EMOJI') }}</title>
|
|
61
|
+
|
|
62
|
+
<!-- <path d="M0,0H20.57V20.57H0V0Z" fill="none"/>
|
|
63
|
+
<circle cx="15.02" cy="9.86" r="1.29"/>
|
|
64
|
+
<circle cx="9.02" cy="9.86" r="1.29"/>
|
|
65
|
+
<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"/> -->
|
|
66
|
+
</svg>
|
|
67
|
+
</span>
|
|
68
|
+
</label>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
<div *ngIf="!isStopRec" class="visible-text-area" [class.stream-active]="isStreamAudioActive" [class.hasError]="showAlertEmoji" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
|
|
75
|
+
<!-- isFilePendingToUpload || -->
|
|
76
|
+
<textarea
|
|
77
|
+
[attr.disabled] = "(hideTextReply)? true : null"
|
|
78
|
+
[attr.placeholder] ="(footerMessagePlaceholder)? footerMessagePlaceholder : translationMap?.get('LABEL_PLACEHOLDER')"
|
|
79
|
+
start-focus-chat21-conversation-component
|
|
80
|
+
inputTextArea
|
|
81
|
+
#textbox
|
|
82
|
+
tabindex="1501"
|
|
83
|
+
aria-labelledby="altTextArea"
|
|
84
|
+
rows="1"
|
|
85
|
+
id="chat21-main-message-context"
|
|
86
|
+
class='f21textarea c21-button-clean'
|
|
87
|
+
[(ngModel)]="textInputTextArea"
|
|
88
|
+
(ngModelChange)="onTextAreaChange()"
|
|
89
|
+
(keypress)="onkeypress($event)"
|
|
90
|
+
(keydown)="onkeydown($event)"
|
|
91
|
+
(paste)="onPaste($event)">
|
|
92
|
+
</textarea>
|
|
93
|
+
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<!-- ICON SEND -->
|
|
97
|
+
<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)">
|
|
45
98
|
<span class="v-align-center">
|
|
46
|
-
<svg
|
|
47
|
-
<path
|
|
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>
|
|
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"/> -->
|
|
99
|
+
<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">
|
|
100
|
+
<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"/>
|
|
57
101
|
</svg>
|
|
58
102
|
</span>
|
|
59
|
-
</
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
|
|
103
|
+
</div>
|
|
63
104
|
|
|
105
|
+
<!-- ICON REC -->
|
|
106
|
+
<div *ngIf="showAudioRecorderFooterButton && !textInputTextArea && !isStreamAudioActive" tabindex="-1" class="chat21-audio-button" [class.active]="isStopRec" id="chat21-button-rec">
|
|
107
|
+
<chat-audio-recorder
|
|
108
|
+
(startRecordingEvent)="onStartRecording()"
|
|
109
|
+
(deleteRecordingEvent)="onDeleteRecording()"
|
|
110
|
+
(endRecordingEvent)="onEndRecording($event)"
|
|
111
|
+
(sendRecordingEvent)="onSendRecording($event)"
|
|
112
|
+
[stylesMap]="stylesMap">
|
|
113
|
+
</chat-audio-recorder>
|
|
114
|
+
</div>
|
|
64
115
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
(ngModelChange)="onTextAreaChange()"
|
|
80
|
-
(keypress)="onkeypress($event)"
|
|
81
|
-
(keydown)="onkeydown($event)"
|
|
82
|
-
(paste)="onPaste($event)">
|
|
83
|
-
</textarea>
|
|
84
|
-
|
|
116
|
+
<!-- ICON STREAM / CHIUDI STREAM (cerchio, icone bianche su iconColor) -->
|
|
117
|
+
<div *ngIf="showAudioStreamFooterButton" tabindex="-1" id="chat21-button-stream"
|
|
118
|
+
class="chat21-textarea-button chat21-stream-button"
|
|
119
|
+
[class.active]="isStreamAudioActive || (!textInputTextArea && !hideTextReply)"
|
|
120
|
+
[class.chat21-stream-button--expanded]="isStreamAudioActive"
|
|
121
|
+
(click)="onStreamPressed($event)" [attr.aria-label]="isStreamAudioActive ? (translationMap?.get('CLOSE') || 'Chiudi stream') : (translationMap?.get('STREAM_AUDIO') || 'Stream audio')"
|
|
122
|
+
[ngStyle]="{ 'background-color': stylesMap?.get('iconColor') || stylesMap?.get('themeColor') }">
|
|
123
|
+
<chat-stream-audio-spectrum
|
|
124
|
+
mode="button"
|
|
125
|
+
[active]="isStreamAudioActive"
|
|
126
|
+
[volume]="currentVolume"
|
|
127
|
+
[translationMap]="translationMap">
|
|
128
|
+
</chat-stream-audio-spectrum>
|
|
129
|
+
</div>
|
|
85
130
|
</div>
|
|
86
131
|
|
|
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>
|
|
94
|
-
</div>
|
|
95
132
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
133
|
+
<div class="close-chat-container" *ngIf="closeChatInConversation">
|
|
134
|
+
<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')}">
|
|
135
|
+
<span class="v-align-center">
|
|
136
|
+
<!-- <svg [ngStyle]="{'fill': 'yellow' }" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
|
|
137
|
+
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" [ngStyle]="{'fill': stylesMap?.get('foregroundColor')}"/>
|
|
138
|
+
</svg> -->
|
|
139
|
+
<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"
|
|
140
|
+
width="15px" height="15px" viewBox="0 0 512 512">
|
|
141
|
+
<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>
|
|
142
|
+
<rect x="48" y="64" width="416" height="80" rx="28" ry="28" stroke-linejoin="round" stroke-width="50px" fill="none" ></rect>
|
|
143
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M320 304l-64 64-64-64M256 345.89V224" stroke-width="50px" fill="none"></path>
|
|
144
|
+
<title id="altIconTitle">{{ translationMap?.get('CLOSE_CHAT') }}</title>
|
|
145
|
+
</svg>
|
|
146
|
+
</span>
|
|
147
|
+
<span class="v-align-center c21-label-button">
|
|
148
|
+
{{translationMap?.get('CLOSE_CHAT')}}
|
|
149
|
+
</span>
|
|
150
|
+
<div class="clear"></div>
|
|
151
|
+
</button>
|
|
105
152
|
</div>
|
|
153
|
+
|
|
106
154
|
</div>
|
|
107
155
|
|
|
108
156
|
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
.textarea-container-wrapper{
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 8px;
|
|
5
|
+
}
|
|
2
6
|
.textarea-container{
|
|
3
|
-
// padding: 8px 34px;
|
|
4
|
-
// padding-left: 70px;
|
|
5
|
-
// padding-right: 45px;
|
|
6
7
|
display: flex;
|
|
7
|
-
// width: 100%;
|
|
8
8
|
align-items: center;
|
|
9
9
|
justify-content: space-between;
|
|
10
10
|
gap: 8px;
|
|
11
|
+
}
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
width: 90%;
|
|
19
|
-
}
|
|
13
|
+
.close-chat-container{
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
gap: 8px;
|
|
20
19
|
|
|
20
|
+
.c21-close{
|
|
21
|
+
height: 30px !important;
|
|
22
|
+
margin: 0px !important;
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
.icons-container{
|
|
@@ -40,6 +43,10 @@
|
|
|
40
43
|
&.hasError{
|
|
41
44
|
box-shadow: 0 0 0 1px var(--chat-footer-border-color-error) inset;
|
|
42
45
|
}
|
|
46
|
+
|
|
47
|
+
&.stream-active {
|
|
48
|
+
width: 50%;
|
|
49
|
+
}
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
.chat21-textarea-button {
|
|
@@ -82,6 +89,50 @@
|
|
|
82
89
|
border-radius: 50%;
|
|
83
90
|
}
|
|
84
91
|
|
|
92
|
+
/** Stream audio: cerchio pieno, glyph bianco su sfondo iconColor (stylesMap) */
|
|
93
|
+
.chat21-stream-button.chat21-textarea-button {
|
|
94
|
+
width: 36px;
|
|
95
|
+
height: 36px;
|
|
96
|
+
min-width: 36px;
|
|
97
|
+
border-radius: 50%;
|
|
98
|
+
box-sizing: border-box;
|
|
99
|
+
flex-shrink: 0;
|
|
100
|
+
color: #ffffff;
|
|
101
|
+
transition:
|
|
102
|
+
width 180ms ease,
|
|
103
|
+
border-radius 180ms ease,
|
|
104
|
+
padding 180ms ease;
|
|
105
|
+
|
|
106
|
+
.chat21-stream-button__icon svg {
|
|
107
|
+
width: 20px;
|
|
108
|
+
height: 20px;
|
|
109
|
+
path {
|
|
110
|
+
fill: #ffffff;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
&.chat21-textarea-button span svg:hover {
|
|
115
|
+
background: rgba(255, 255, 255, 0.2) !important;
|
|
116
|
+
border-radius: 50%;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&.chat21-stream-button--expanded {
|
|
120
|
+
width: auto;
|
|
121
|
+
// min-width: 150px;
|
|
122
|
+
max-width: 100%;
|
|
123
|
+
border-radius: 999px;
|
|
124
|
+
padding: 0 14px;
|
|
125
|
+
gap: 12px;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
|
|
128
|
+
// stop the circle-hover background from looking odd on pill
|
|
129
|
+
&.chat21-textarea-button span svg:hover {
|
|
130
|
+
background: transparent !important;
|
|
131
|
+
border-radius: 0;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
85
136
|
textarea,
|
|
86
137
|
textarea:visited,
|
|
87
138
|
textarea:focus,
|
|
@@ -364,6 +415,34 @@ textarea:active{
|
|
|
364
415
|
}
|
|
365
416
|
}
|
|
366
417
|
|
|
418
|
+
#streamAudioAlert {
|
|
419
|
+
bottom: 100%;
|
|
420
|
+
width: 100%;
|
|
421
|
+
min-height: var(--chat-footer-stream-button-height);
|
|
422
|
+
display: flex;
|
|
423
|
+
align-items: center;
|
|
424
|
+
justify-content: center;
|
|
425
|
+
position: absolute;
|
|
426
|
+
padding: var(--chat-footer-stream-button-padding);
|
|
427
|
+
/* Satinato / vetro: più trasparenza, blur più marcato */
|
|
428
|
+
background-color: color-mix(in srgb, var(--content-background-color) 34%, transparent);
|
|
429
|
+
backdrop-filter: blur(20px) saturate(1.2);
|
|
430
|
+
-webkit-backdrop-filter: blur(20px) saturate(1.2);
|
|
431
|
+
box-shadow:
|
|
432
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.28),
|
|
433
|
+
0 1px 0 rgba(0, 0, 0, 0.05);
|
|
434
|
+
|
|
435
|
+
&.hideTextReply {
|
|
436
|
+
position: unset;
|
|
437
|
+
min-height: auto;
|
|
438
|
+
padding: 16px 0;
|
|
439
|
+
box-shadow: none;
|
|
440
|
+
backdrop-filter: none;
|
|
441
|
+
-webkit-backdrop-filter: none;
|
|
442
|
+
background-color: var(--content-background-color);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
367
446
|
#textAlert{
|
|
368
447
|
bottom: 100%;
|
|
369
448
|
width: 100%;
|
|
@@ -419,3 +498,28 @@ textarea:active{
|
|
|
419
498
|
border: none;
|
|
420
499
|
// margin: -2px -2px 0px;
|
|
421
500
|
}
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
// aggiungi un'animazione di fade in e fade out quando .star-rating-widget è visibile con transition
|
|
504
|
+
.star-rating-widget {
|
|
505
|
+
transition: all 0.5s ease-in-out;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.star-rating-widget {
|
|
509
|
+
position: absolute;
|
|
510
|
+
left: 0;
|
|
511
|
+
right: 0;
|
|
512
|
+
bottom: -52px;
|
|
513
|
+
height: 100%;
|
|
514
|
+
width: 100%;
|
|
515
|
+
flex-direction: row;
|
|
516
|
+
justify-content: center;
|
|
517
|
+
background-color: rgb(255, 255, 255);
|
|
518
|
+
flex-wrap: nowrap;
|
|
519
|
+
&.active {
|
|
520
|
+
bottom: 0px;
|
|
521
|
+
}
|
|
522
|
+
&.inactive {
|
|
523
|
+
bottom: -52px;
|
|
524
|
+
}
|
|
525
|
+
}
|