@chat21/chat21-web-widget 5.1.34-rc1 → 5.1.34
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 +8 -129
- package/Dockerfile +5 -4
- package/angular.json +3 -21
- package/docs/changelog/this-branch.md +0 -36
- package/env.sample +2 -3
- package/nginx.conf +2 -22
- package/package.json +3 -10
- package/src/app/app.component.html +2 -2
- package/src/app/app.component.scss +14 -25
- package/src/app/app.component.spec.ts +6 -21
- package/src/app/app.component.ts +9 -10
- package/src/app/app.module.ts +0 -13
- package/src/app/component/conversation-detail/conversation/conversation.component.html +11 -25
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -40
- package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +75 -644
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +14 -100
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +13 -25
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +5 -123
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +0 -1
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +10 -23
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -19
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +149 -242
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +5 -8
- package/src/app/component/conversation-detail/conversation-emojii/conversation-emojii.component.spec.ts +3 -53
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +96 -200
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +6 -211
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +78 -452
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +76 -291
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +53 -113
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.scss +4 -12
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +29 -274
- package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html +9 -23
- package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.spec.ts +8 -80
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html +23 -29
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.spec.ts +16 -185
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts +14 -34
- package/src/app/component/error-alert/error-alert.component.spec.ts +5 -65
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html +7 -16
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss +0 -21
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.spec.ts +7 -89
- package/src/app/component/form/form-builder/form-builder.component.html +1 -1
- package/src/app/component/form/form-builder/form-builder.component.spec.ts +21 -163
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.html +4 -8
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.scss +5 -10
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.spec.ts +16 -90
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.ts +0 -26
- package/src/app/component/form/inputs/form-label/form-label.component.spec.ts +11 -45
- package/src/app/component/form/inputs/form-radio-button/form-radio-button.component.spec.ts +6 -24
- package/src/app/component/form/inputs/form-select/form-select.component.spec.ts +5 -14
- package/src/app/component/form/inputs/form-text/form-text.component.html +12 -14
- package/src/app/component/form/inputs/form-text/form-text.component.scss +1 -11
- package/src/app/component/form/inputs/form-text/form-text.component.spec.ts +17 -113
- package/src/app/component/form/inputs/form-text/form-text.component.ts +3 -35
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.html +11 -13
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.scss +5 -6
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.spec.ts +13 -149
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.ts +0 -26
- package/src/app/component/form/prechat-form/prechat-form.component.html +11 -14
- package/src/app/component/form/prechat-form/prechat-form.component.spec.ts +10 -102
- package/src/app/component/form/prechat-form/prechat-form.component.ts +1 -8
- package/src/app/component/home/home.component.html +31 -38
- package/src/app/component/home/home.component.scss +2 -4
- package/src/app/component/home/home.component.spec.ts +11 -226
- package/src/app/component/home-conversations/home-conversations.component.html +26 -30
- package/src/app/component/home-conversations/home-conversations.component.scss +0 -3
- package/src/app/component/home-conversations/home-conversations.component.spec.ts +36 -212
- package/src/app/component/last-message/last-message.component.html +9 -15
- package/src/app/component/last-message/last-message.component.scss +2 -16
- package/src/app/component/last-message/last-message.component.spec.ts +23 -204
- package/src/app/component/last-message/last-message.component.ts +1 -4
- package/src/app/component/launcher-button/launcher-button.component.html +13 -8
- package/src/app/component/launcher-button/launcher-button.component.spec.ts +8 -104
- package/src/app/component/list-all-conversations/list-all-conversations.component.html +17 -12
- package/src/app/component/list-all-conversations/list-all-conversations.component.scss +0 -2
- package/src/app/component/list-conversations/list-conversations.component.html +22 -22
- package/src/app/component/menu-options/menu-options.component.html +20 -30
- package/src/app/component/menu-options/menu-options.component.spec.ts +9 -125
- package/src/app/component/message/audio/audio.component.html +15 -13
- package/src/app/component/message/audio/audio.component.spec.ts +5 -140
- package/src/app/component/message/audio/audio.component.ts +5 -1
- package/src/app/component/message/avatar/avatar.component.html +2 -2
- package/src/app/component/message/avatar/avatar.component.spec.ts +7 -99
- package/src/app/component/message/bubble-message/bubble-message.component.html +51 -38
- package/src/app/component/message/bubble-message/bubble-message.component.scss +1 -54
- package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +57 -154
- package/src/app/component/message/bubble-message/bubble-message.component.ts +11 -89
- package/src/app/component/message/buttons/action-button/action-button.component.html +4 -3
- package/src/app/component/message/buttons/action-button/action-button.component.spec.ts +5 -49
- package/src/app/component/message/buttons/link-button/link-button.component.scss +8 -5
- package/src/app/component/message/buttons/link-button/link-button.component.spec.ts +5 -50
- package/src/app/component/message/buttons/text-button/text-button.component.spec.ts +5 -44
- package/src/app/component/message/carousel/carousel.component.html +16 -29
- package/src/app/component/message/carousel/carousel.component.scss +8 -20
- package/src/app/component/message/carousel/carousel.component.spec.ts +3 -80
- package/src/app/component/message/carousel/carousel.component.ts +0 -16
- package/src/app/component/message/frame/frame.component.html +4 -9
- package/src/app/component/message/frame/frame.component.spec.ts +15 -34
- package/src/app/component/message/frame/frame.component.ts +2 -7
- package/src/app/component/message/html/html.component.html +1 -1
- package/src/app/component/message/html/html.component.scss +1 -1
- package/src/app/component/message/html/html.component.spec.ts +7 -24
- package/src/app/component/message/image/image.component.html +10 -12
- package/src/app/component/message/image/image.component.scss +0 -16
- package/src/app/component/message/image/image.component.spec.ts +15 -101
- package/src/app/component/message/image/image.component.ts +51 -90
- package/src/app/component/message/info-message/info-message.component.spec.ts +14 -26
- package/src/app/component/message/like-unlike/like-unlike.component.html +9 -7
- package/src/app/component/message/like-unlike/like-unlike.component.spec.ts +3 -31
- package/src/app/component/message/return-receipt/return-receipt.component.spec.ts +17 -38
- package/src/app/component/message/text/text.component.html +3 -3
- package/src/app/component/message/text/text.component.scss +86 -80
- package/src/app/component/message/text/text.component.spec.ts +13 -106
- package/src/app/component/message-attachment/message-attachment.component.spec.ts +13 -134
- package/src/app/component/selection-department/selection-department.component.html +23 -21
- package/src/app/component/selection-department/selection-department.component.spec.ts +14 -159
- package/src/app/component/selection-department/selection-department.component.ts +1 -8
- package/src/app/component/send-button/send-button.component.html +13 -5
- package/src/app/component/send-button/send-button.component.spec.ts +2 -2
- package/src/app/component/star-rating-widget/star-rating-widget.component.html +81 -51
- package/src/app/directives/tooltip.directive.spec.ts +4 -8
- package/src/app/modals/confirm-close/confirm-close.component.html +8 -20
- package/src/app/modals/confirm-close/confirm-close.component.scss +0 -3
- package/src/app/modals/confirm-close/confirm-close.component.spec.ts +4 -13
- package/src/app/modals/confirm-close/confirm-close.component.ts +1 -8
- package/src/app/pipe/html-entites-encode.pipe.spec.ts +2 -35
- package/src/app/pipe/marked.pipe.spec.ts +2 -38
- package/src/app/pipe/marked.pipe.ts +41 -51
- package/src/app/providers/app-config.service.ts +2 -4
- package/src/app/providers/brand.service.spec.ts +2 -23
- package/src/app/providers/brand.service.ts +1 -1
- package/src/app/providers/global-settings.service.spec.ts +14 -1009
- package/src/app/providers/global-settings.service.ts +2 -82
- package/src/app/providers/translator.service.ts +6 -26
- package/src/app/sass/_variables.scss +0 -3
- package/src/app/sass/animations.scss +1 -19
- package/src/app/utils/globals.ts +1 -21
- package/src/app/utils/utils-resources.ts +1 -1
- package/src/assets/i18n/en.json +99 -106
- package/src/assets/i18n/es.json +100 -107
- package/src/assets/i18n/fr.json +100 -107
- package/src/assets/i18n/it.json +98 -107
- package/src/assets/twp/index-dev.html +0 -18
- package/src/chat21-core/models/message.ts +1 -2
- package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +2 -3
- package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +0 -12
- package/src/chat21-core/providers/scripts/script.service.spec.ts +2 -12
- package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
- package/src/chat21-core/utils/utils-message.ts +0 -7
- package/src/chat21-core/utils/utils.ts +2 -5
- package/src/widget-config-template.json +1 -4
- package/src/widget-config.json +1 -4
- package/tsconfig.json +0 -5
- package/.angular-mcp-cache/package.json +0 -1
- package/.cursor/angular18-accessibility-auditor-skill.md +0 -442
- package/.cursor/mcp.json +0 -15
- package/.github/workflows/build.yml +0 -22
- package/.github/workflows/playwright.yml +0 -27
- package/mocks/voice-websocket-mock/server.cjs +0 -245
- package/playwright.config.ts +0 -41
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +0 -46
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +0 -83
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +0 -192
- package/src/app/component/form/prechat-form-test-mock.ts +0 -35
- package/src/app/component/message/audio-sync/audio-sync.component.html +0 -18
- package/src/app/component/message/audio-sync/audio-sync.component.scss +0 -65
- package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +0 -103
- package/src/app/component/message/audio-sync/audio-sync.component.ts +0 -643
- package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +0 -117
- package/src/app/providers/tts-audio-playback-coordinator.service.ts +0 -109
- package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +0 -12
- package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +0 -171
- package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +0 -39
- package/src/app/providers/voice/audio.types.ts +0 -40
- package/src/app/providers/voice/vad.service.spec.ts +0 -28
- package/src/app/providers/voice/vad.service.ts +0 -70
- package/src/app/providers/voice/voice-streaming.service.spec.ts +0 -23
- package/src/app/providers/voice/voice-streaming.service.ts +0 -702
- package/src/app/providers/voice/voice-streaming.types.ts +0 -112
- package/src/app/providers/voice/voice.service.spec.ts +0 -227
- package/src/app/providers/voice/voice.service.ts +0 -973
- package/src/app/shims/onnxruntime-web-wasm.ts +0 -4
- package/src/assets/onnx/ort-wasm-simd-threaded.mjs +0 -59
- package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
- package/src/assets/sounds/keyboard.mp3 +0 -0
- package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +0 -14
- package/src/assets/vad/silero_vad_legacy.onnx +0 -0
- package/src/assets/vad/vad.worklet.bundle.min.js +0 -1
- package/src/chat21-core/providers/chat-manager.spec.ts +0 -72
- package/tests/widget-form-rich.spec.ts +0 -67
- package/tests/widget-index-dev-settings.spec.ts +0 -52
- package/tests/widget-twp-iframe.spec.ts +0 -39
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, ComponentFactoryResolver, ElementRef, EventEmitter, Input, OnChanges,
|
|
1
|
+
import { Component, ComponentFactoryResolver, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core';
|
|
2
2
|
import { error } from 'console';
|
|
3
3
|
import { FILE_SIZE_LIMIT } from 'src/app/utils/constants';
|
|
4
4
|
import { Globals } from 'src/app/utils/globals';
|
|
@@ -15,18 +15,13 @@ import { TYPE_MSG_FILE, TYPE_MSG_IMAGE, TYPE_MSG_TEXT } from 'src/chat21-core/ut
|
|
|
15
15
|
import { convertColorToRGBA, isAllowedUrlInText, isEmoji } from 'src/chat21-core/utils/utils';
|
|
16
16
|
import { findAndRemoveEmoji, isImage } from 'src/chat21-core/utils/utils-message';
|
|
17
17
|
import { ProjectModel } from 'src/models/project';
|
|
18
|
-
import { Subscription } from 'rxjs';
|
|
19
|
-
import { VoiceService } from 'src/app/providers/voice/voice.service';
|
|
20
|
-
import { VoiceStreamingSessionConfig } from 'src/app/providers/voice/voice-streaming.types';
|
|
21
|
-
import { TtsAudioPlaybackCoordinator } from 'src/app/providers/tts-audio-playback-coordinator.service';
|
|
22
|
-
import { TiledeskAuthService } from 'src/chat21-core/providers/tiledesk/tiledesk-auth.service';
|
|
23
18
|
|
|
24
19
|
@Component({
|
|
25
20
|
selector: 'chat-conversation-footer',
|
|
26
21
|
templateUrl: './conversation-footer.component.html',
|
|
27
22
|
styleUrls: ['./conversation-footer.component.scss']
|
|
28
23
|
})
|
|
29
|
-
export class ConversationFooterComponent implements OnInit, OnChanges
|
|
24
|
+
export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
30
25
|
|
|
31
26
|
@Input() conversationWith: string;
|
|
32
27
|
@Input() attributes: string;
|
|
@@ -37,9 +32,8 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
37
32
|
@Input() userFullname: string;
|
|
38
33
|
@Input() userEmail: string;
|
|
39
34
|
@Input() showAttachmentFooterButton: boolean;
|
|
40
|
-
@Input() showEmojiFooterButton: boolean
|
|
41
|
-
@Input() showAudioRecorderFooterButton: boolean
|
|
42
|
-
@Input() showAudioStreamFooterButton: boolean;
|
|
35
|
+
@Input() showEmojiFooterButton: boolean
|
|
36
|
+
@Input() showAudioRecorderFooterButton: boolean
|
|
43
37
|
// @Input() showContinueConversationButton: boolean;
|
|
44
38
|
@Input() isConversationArchived: boolean;
|
|
45
39
|
@Input() hideTextAreaContent: boolean;
|
|
@@ -48,7 +42,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
48
42
|
@Input() isEmojiiPickerShow: boolean;
|
|
49
43
|
@Input() footerMessagePlaceholder: string;
|
|
50
44
|
@Input() fileUploadAccept: string;
|
|
51
|
-
@Input() closeChatInConversation: boolean;
|
|
52
45
|
@Input() dropEvent: Event;
|
|
53
46
|
@Input() poweredBy: string;
|
|
54
47
|
@Input() stylesMap: Map<string, string>
|
|
@@ -59,9 +52,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
59
52
|
@Output() onChangeTextArea = new EventEmitter<any>();
|
|
60
53
|
@Output() onAttachmentFileButtonClicked = new EventEmitter<any>();
|
|
61
54
|
@Output() onNewConversationButtonClicked = new EventEmitter();
|
|
62
|
-
@Output() onStreamAudioActiveChange = new EventEmitter<boolean>();
|
|
63
|
-
@Output() onStreamAudioConnectingChange = new EventEmitter<boolean>();
|
|
64
|
-
@Output() onCloseChatButtonClicked = new EventEmitter();
|
|
65
55
|
|
|
66
56
|
@ViewChild('chat21_file') public chat21_file: ElementRef;
|
|
67
57
|
// @ViewChild('emojii_container', {read: ViewContainerRef}) selector;
|
|
@@ -95,67 +85,24 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
95
85
|
|
|
96
86
|
showAlertEmoji: boolean = false
|
|
97
87
|
|
|
98
|
-
/** Stream audio UI: icona equalizer → X; alert con onde animate sopra il footer */
|
|
99
|
-
isStreamAudioActive = false;
|
|
100
|
-
/** True while the WebSocket session is being established (between click and session_started). */
|
|
101
|
-
isStreamAudioConnecting = false;
|
|
102
|
-
/** True while the bot's TTS audio is playing — mic segments are suppressed, spectrum turns grey. */
|
|
103
|
-
isBotSpeaking = false;
|
|
104
|
-
/** Sottoscrizione ai segmenti audio (VAD → WebM) dal {@link VoiceService}. */
|
|
105
|
-
private voiceAudioSubscription?: Subscription;
|
|
106
|
-
/** Sottoscrizione a `transcript` finale dalla WSS. */
|
|
107
|
-
private voiceTranscriptSubscription?: Subscription;
|
|
108
|
-
/** Sottoscrizione al volume audio (real-time) dal {@link VoiceService}. */
|
|
109
|
-
private voiceVolumeSubscription?: Subscription;
|
|
110
|
-
/** Sottoscrizione allo stato TTS (bot sta parlando). */
|
|
111
|
-
private botSpeakingSub?: Subscription;
|
|
112
|
-
/** Passato a {@link StreamAudioSpectrumComponent} per disegnare la linea spettro. */
|
|
113
|
-
currentVolume = 0;
|
|
114
|
-
/** Last user utterance transcribed — persists during bot processing to show in voice panel. */
|
|
115
|
-
lastVoiceTranscript = '';
|
|
116
|
-
|
|
117
|
-
get voiceStatusLabel(): string {
|
|
118
|
-
if (this.isStreamAudioConnecting && !this.isStreamAudioActive) {
|
|
119
|
-
return this.translationMap?.get('VOICE_CONNECTING') || 'Connecting...';
|
|
120
|
-
}
|
|
121
|
-
if (this.isStreamAudioActive && this.isBotSpeaking) {
|
|
122
|
-
return this.translationMap?.get('VOICE_PROCESSING') || 'Processing...';
|
|
123
|
-
}
|
|
124
|
-
return this.translationMap?.get('VOICE_LISTENING') || 'Listening...';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
get maxAttachmentLabel(): string {
|
|
128
|
-
const template = this.translationMap?.get('MAX_ATTACHMENT')
|
|
129
|
-
|| `Max allowed size {{FILE_SIZE_LIMIT}}Mb`;
|
|
130
|
-
return template.replace(/\{\{FILE_SIZE_LIMIT\}\}/g, String(this.file_size_limit));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
88
|
file_size_limit = FILE_SIZE_LIMIT;
|
|
134
89
|
attachmentTooltip: string = '';
|
|
135
|
-
isErrorNetwork: boolean = false;
|
|
136
90
|
|
|
137
91
|
|
|
138
92
|
convertColorToRGBA = convertColorToRGBA;
|
|
139
93
|
private logger: LoggerService = LoggerInstance.getInstance()
|
|
140
|
-
constructor(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
private uploadService: UploadService,
|
|
144
|
-
private voiceService: VoiceService,
|
|
145
|
-
private ttsPlayback: TtsAudioPlaybackCoordinator,
|
|
146
|
-
private tiledeskAuthService: TiledeskAuthService,
|
|
147
|
-
public g: Globals,
|
|
148
|
-
) {}
|
|
94
|
+
constructor(private chatManager: ChatManager,
|
|
95
|
+
private typingService: TypingService,
|
|
96
|
+
private uploadService: UploadService) { }
|
|
149
97
|
|
|
150
98
|
ngOnInit() {
|
|
151
99
|
// this.updateAttachmentTooltip();
|
|
152
100
|
}
|
|
153
101
|
|
|
102
|
+
|
|
154
103
|
ngOnChanges(changes: SimpleChanges){
|
|
155
104
|
if(changes['conversationWith'] && changes['conversationWith'].currentValue !== undefined){
|
|
156
105
|
this.conversationHandlerService = this.chatManager.getConversationHandlerByConversationId(this.conversationWith);
|
|
157
|
-
this.isStreamAudioActive = false;
|
|
158
|
-
void this.stopVoice();
|
|
159
106
|
}
|
|
160
107
|
if(changes['hideTextReply'] && changes['hideTextReply'].currentValue !== undefined){
|
|
161
108
|
this.restoreTextArea();
|
|
@@ -195,159 +142,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
195
142
|
// }, 500);
|
|
196
143
|
// }
|
|
197
144
|
|
|
198
|
-
/**
|
|
199
|
-
* Stream voce: con `voiceIngress` solo WSS (no VAD) — transcript + TTS dal server.
|
|
200
|
-
* Senza ingresso WSS: VAD + upload per segmento.
|
|
201
|
-
*/
|
|
202
|
-
async initVoice() {
|
|
203
|
-
this.voiceAudioSubscription?.unsubscribe();
|
|
204
|
-
this.voiceVolumeSubscription?.unsubscribe();
|
|
205
|
-
this.botSpeakingSub?.unsubscribe();
|
|
206
|
-
this.voiceTranscriptSubscription?.unsubscribe();
|
|
207
|
-
|
|
208
|
-
const voiceIngress = this.buildVoiceIngressStreamConfig();
|
|
209
|
-
this.voiceAudioSubscription = undefined;
|
|
210
|
-
this.voiceTranscriptSubscription = this.voiceService.voiceTranscript$.subscribe(({ text }) => {
|
|
211
|
-
// Guard: stop accepting transcript text once the proxy is processing (thinking/speaking)
|
|
212
|
-
if (text && !this.isBotSpeaking) {
|
|
213
|
-
this.textInputTextArea = text;
|
|
214
|
-
this.lastVoiceTranscript = text;
|
|
215
|
-
// The proxy publishes the user utterance to Chat21 via AMQP on utterance-end;
|
|
216
|
-
// no sendMessage call is needed here — doing so would produce duplicate messages.
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
this.voiceVolumeSubscription = this.voiceService.volume$.subscribe((volume) => {
|
|
221
|
-
this.currentVolume = volume;
|
|
222
|
-
});
|
|
223
|
-
this.botSpeakingSub = this.voiceService.isAcquisitionBlocked$.subscribe((blocked) => {
|
|
224
|
-
this.isBotSpeaking = blocked;
|
|
225
|
-
if (blocked) {
|
|
226
|
-
// Proxy has started thinking/speaking — clear the textarea preview
|
|
227
|
-
this.textInputTextArea = '';
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
await this.voiceService.startSession(voiceIngress ? { voiceIngressStream: voiceIngress } : {});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
private buildVoiceIngressStreamConfig(): VoiceStreamingSessionConfig | null {
|
|
234
|
-
const token = this.tiledeskAuthService.getTiledeskToken() ?? '';
|
|
235
|
-
const sender = this.tiledeskAuthService.getCurrentUser()?.uid ?? '';
|
|
236
|
-
const recipient = this.conversationWith ?? '';
|
|
237
|
-
if (!token || !sender || !recipient) {
|
|
238
|
-
this.logger.warn('[CONV-FOOTER] buildVoiceIngressStreamConfig: missing required fields', {
|
|
239
|
-
hasToken: !!token,
|
|
240
|
-
hasSender: !!sender,
|
|
241
|
-
hasRecipient: !!recipient,
|
|
242
|
-
});
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
const { recipientFullname, attributes, channelType } = this.buildSendMessageContext();
|
|
246
|
-
this.logger.log('[CONV-FOOTER] buildVoiceIngressStreamConfig', { sender, recipient, channelType });
|
|
247
|
-
return {
|
|
248
|
-
token,
|
|
249
|
-
sender,
|
|
250
|
-
recipient,
|
|
251
|
-
// Use Deepgram multilingual code-switching so the model detects the spoken
|
|
252
|
-
// language from the audio stream regardless of browser locale.
|
|
253
|
-
// Source: https://developers.deepgram.com/docs/multilingual-code-switching
|
|
254
|
-
lang: 'multi',
|
|
255
|
-
text: '',
|
|
256
|
-
type: 'text',
|
|
257
|
-
recipient_fullname: recipientFullname ?? '',
|
|
258
|
-
sender_fullname: recipientFullname ?? '',
|
|
259
|
-
attributes: attributes ?? {},
|
|
260
|
-
metadata: '',
|
|
261
|
-
channel_type: channelType ?? '',
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Merge `attributes` di componente con `additional_attributes` e risolve `recipientFullname` come in sendMessage.
|
|
267
|
-
*/
|
|
268
|
-
private buildSendMessageContext(additional_attributes?: any) {
|
|
269
|
-
let recipientFullname = this.translationMap.get('GUEST_LABEL');
|
|
270
|
-
const g_attributes = this.attributes;
|
|
271
|
-
const attributes = <any>{};
|
|
272
|
-
if (g_attributes) {
|
|
273
|
-
for (const [key, value] of Object.entries(g_attributes)) {
|
|
274
|
-
attributes[key] = value;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
if (additional_attributes) {
|
|
278
|
-
for (const [key, value] of Object.entries(additional_attributes)) {
|
|
279
|
-
attributes[key] = value;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
const senderId = this.senderId;
|
|
283
|
-
const projectid = this.project.id;
|
|
284
|
-
const channelType = this.channelType;
|
|
285
|
-
const userFullname = this.userFullname;
|
|
286
|
-
const userEmail = this.userEmail;
|
|
287
|
-
const conversationWith = this.conversationWith;
|
|
288
|
-
|
|
289
|
-
if (userFullname) {
|
|
290
|
-
recipientFullname = userFullname;
|
|
291
|
-
} else if (userEmail) {
|
|
292
|
-
recipientFullname = userEmail;
|
|
293
|
-
} else if (attributes && attributes['userFullname']) {
|
|
294
|
-
recipientFullname = attributes['userFullname'];
|
|
295
|
-
} else {
|
|
296
|
-
recipientFullname = this.translationMap.get('GUEST_LABEL');
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return {
|
|
300
|
-
recipientFullname,
|
|
301
|
-
attributes,
|
|
302
|
-
senderId,
|
|
303
|
-
projectid,
|
|
304
|
-
channelType,
|
|
305
|
-
conversationWith,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
async stopVoice(options?: { discardInProgressSegment?: boolean }) {
|
|
309
|
-
// Stop all active TTS audio immediately and reveal all text.
|
|
310
|
-
this.ttsPlayback.stopAll();
|
|
311
|
-
|
|
312
|
-
this.voiceAudioSubscription?.unsubscribe();
|
|
313
|
-
this.voiceAudioSubscription = undefined;
|
|
314
|
-
|
|
315
|
-
this.voiceTranscriptSubscription?.unsubscribe();
|
|
316
|
-
this.voiceTranscriptSubscription = undefined;
|
|
317
|
-
|
|
318
|
-
this.voiceVolumeSubscription?.unsubscribe();
|
|
319
|
-
this.voiceVolumeSubscription = undefined;
|
|
320
|
-
|
|
321
|
-
this.botSpeakingSub?.unsubscribe();
|
|
322
|
-
this.botSpeakingSub = undefined;
|
|
323
|
-
this.isBotSpeaking = false;
|
|
324
|
-
|
|
325
|
-
await this.voiceService.stopSession(options);
|
|
326
|
-
this.currentVolume = 0;
|
|
327
|
-
this.textInputTextArea = '';
|
|
328
|
-
this.lastVoiceTranscript = '';
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Messaggio in arrivo da un altro mittente mentre lo stream è attivo: con VAD legacy scarta il segmento in corso.
|
|
333
|
-
* Con sola sessione WSS non ha effetto sul mic (nessun recorder a segmenti locale).
|
|
334
|
-
*/
|
|
335
|
-
interruptStreamDueToPeerMessage(): void {
|
|
336
|
-
if (!this.isStreamAudioActive) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
this.logger.log('[CONV-FOOTER] discard recording segment: incoming message from peer (stream stays on)');
|
|
340
|
-
try {
|
|
341
|
-
this.voiceService.discardCurrentRecordingSegment();
|
|
342
|
-
} catch (e) {
|
|
343
|
-
this.logger.error('[CONV-FOOTER] interruptStreamDueToPeerMessage', e);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
ngOnDestroy() {
|
|
348
|
-
void this.stopVoice();
|
|
349
|
-
}
|
|
350
|
-
|
|
351
145
|
// ========= begin:: functions send image ======= //
|
|
352
146
|
// START LOAD IMAGE //
|
|
353
147
|
/** load the selected image locally and open the pop up preview */
|
|
@@ -585,14 +379,40 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
585
379
|
// msg = replaceEndOfLine(msg);
|
|
586
380
|
// msg = msg.trim();
|
|
587
381
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
382
|
+
let recipientFullname = this.translationMap.get('GUEST_LABEL');
|
|
383
|
+
// sponziello: adds ADDITIONAL ATTRIBUTES TO THE MESSAGE
|
|
384
|
+
const g_attributes = this.attributes;
|
|
385
|
+
// added <any> to resolve the Error occurred during the npm installation: Property 'userFullname' does not exist on type '{}'
|
|
386
|
+
const attributes = <any>{};
|
|
387
|
+
if (g_attributes) {
|
|
388
|
+
for (const [key, value] of Object.entries(g_attributes)) {
|
|
389
|
+
attributes[key] = value;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (additional_attributes) {
|
|
393
|
+
for (const [key, value] of Object.entries(additional_attributes)) {
|
|
394
|
+
attributes[key] = value;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// fine-sponziello
|
|
398
|
+
// this.conversationHandlerService = this.chatManager.getConversationHandlerByConversationId(this.conversationWith)
|
|
399
|
+
const senderId = this.senderId;
|
|
400
|
+
const projectid = this.project.id;
|
|
401
|
+
const channelType = this.channelType;
|
|
402
|
+
const userFullname = this.userFullname;
|
|
403
|
+
const userEmail = this.userEmail;
|
|
404
|
+
const conversationWith = this.conversationWith;
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
if (userFullname) {
|
|
408
|
+
recipientFullname = userFullname;
|
|
409
|
+
} else if (userEmail) {
|
|
410
|
+
recipientFullname = userEmail;
|
|
411
|
+
} else if (attributes && attributes['userFullname']) {
|
|
412
|
+
recipientFullname = attributes['userFullname'];
|
|
413
|
+
} else {
|
|
414
|
+
recipientFullname = this.translationMap.get('GUEST_LABEL');
|
|
415
|
+
}
|
|
596
416
|
|
|
597
417
|
this.onBeforeMessageSent.emit({
|
|
598
418
|
senderFullname: recipientFullname,
|
|
@@ -701,7 +521,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
701
521
|
}
|
|
702
522
|
}
|
|
703
523
|
|
|
704
|
-
prepareAndUpload(audioBlob: Blob
|
|
524
|
+
prepareAndUpload(audioBlob: Blob) {
|
|
705
525
|
|
|
706
526
|
this.isFilePendingToUpload = true;
|
|
707
527
|
|
|
@@ -731,7 +551,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
731
551
|
this.logger.log('[UPLOAD] metadata:', metadata);
|
|
732
552
|
|
|
733
553
|
// stesso metodo che già usi
|
|
734
|
-
this.uploadSingle(metadata, file,
|
|
554
|
+
this.uploadSingle(metadata, file, '');
|
|
735
555
|
}
|
|
736
556
|
|
|
737
557
|
// Funzione per convertire Blob in Base64 usando FileReader
|
|
@@ -838,39 +658,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
838
658
|
}
|
|
839
659
|
}
|
|
840
660
|
|
|
841
|
-
async onStreamPressed(event: Event) {
|
|
842
|
-
this.logger.log('[CONV-FOOTER] onStreamPressed:event', event);
|
|
843
|
-
event.preventDefault();
|
|
844
|
-
if (this.showAlertEmoji) {
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
// Treat a click during connecting as a cancel request (same as turning off).
|
|
848
|
-
const turningOn = !this.isStreamAudioActive && !this.isStreamAudioConnecting;
|
|
849
|
-
this.logger.log('[CONV-FOOTER] onStreamPressed', { turningOn });
|
|
850
|
-
if (turningOn) {
|
|
851
|
-
this.isStreamAudioConnecting = true;
|
|
852
|
-
this.onStreamAudioConnectingChange.emit(true);
|
|
853
|
-
try {
|
|
854
|
-
this.currentVolume = 0;
|
|
855
|
-
await this.initVoice();
|
|
856
|
-
this.isStreamAudioActive = true;
|
|
857
|
-
} catch (e) {
|
|
858
|
-
this.logger.error('[CONV-FOOTER] onStreamPressed: initVoice failed', e);
|
|
859
|
-
this.isStreamAudioActive = false;
|
|
860
|
-
} finally {
|
|
861
|
-
this.isStreamAudioConnecting = false;
|
|
862
|
-
this.onStreamAudioConnectingChange.emit(false);
|
|
863
|
-
}
|
|
864
|
-
} else {
|
|
865
|
-
await this.stopVoice();
|
|
866
|
-
this.isStreamAudioActive = false;
|
|
867
|
-
this.isStreamAudioConnecting = false;
|
|
868
|
-
this.onStreamAudioConnectingChange.emit(false);
|
|
869
|
-
}
|
|
870
|
-
this.onStreamAudioActiveChange.emit(this.isStreamAudioActive);
|
|
871
|
-
this.logger.log('[CONV-FOOTER] isStreamAudioActive', this.isStreamAudioActive);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
661
|
async onEmojiiPickerClicked(){
|
|
875
662
|
// if(this.loadPickerModule){
|
|
876
663
|
// this.loadPickerModule = false;
|
|
@@ -922,10 +709,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
922
709
|
this.onNewConversationButtonClicked.emit();
|
|
923
710
|
}
|
|
924
711
|
|
|
925
|
-
onCloseChat(event){
|
|
926
|
-
this.onCloseChatButtonClicked.emit();
|
|
927
|
-
}
|
|
928
|
-
|
|
929
712
|
// onContinueConversation(){
|
|
930
713
|
// this.hideTextAreaContent = false;
|
|
931
714
|
// this.onBackButton.emit(false)
|
|
@@ -962,46 +745,48 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
962
745
|
}
|
|
963
746
|
|
|
964
747
|
/**
|
|
965
|
-
*
|
|
966
|
-
*
|
|
967
|
-
*
|
|
968
|
-
*
|
|
969
|
-
*
|
|
970
|
-
*
|
|
971
|
-
*
|
|
972
|
-
* and does not consistently fire for modifier combos across browsers.
|
|
748
|
+
* when I press a key I call this method which:
|
|
749
|
+
* check if 'enter' has been pressed
|
|
750
|
+
* if you clear text
|
|
751
|
+
* set field height as min by default
|
|
752
|
+
* takes out the focus and resets it after a few moments
|
|
753
|
+
* (this is a patch to keep the focus and eliminate the br of the send !!!)
|
|
754
|
+
* send message
|
|
973
755
|
* @param event
|
|
974
756
|
*/
|
|
975
|
-
|
|
757
|
+
onkeypress(event) {
|
|
976
758
|
const keyCode = event.which || event.keyCode;
|
|
977
|
-
|
|
978
|
-
if (keyCode === 13) { // ENTER
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
// Let the textarea insert a newline on its own (do not preventDefault).
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
// Plain Enter -> send the message
|
|
986
|
-
event.preventDefault();
|
|
987
|
-
|
|
988
|
-
if (this.showAlertEmoji) {
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
const target = document.getElementById('chat21-main-message-context') as HTMLInputElement;
|
|
993
|
-
if (target) {
|
|
994
|
-
this.textInputTextArea = target.value;
|
|
759
|
+
this.textInputTextArea = ((document.getElementById('chat21-main-message-context') as HTMLInputElement).value);
|
|
760
|
+
if (keyCode === 13) { // ENTER pressed
|
|
761
|
+
if(this.showAlertEmoji){
|
|
762
|
+
return;
|
|
995
763
|
}
|
|
996
|
-
|
|
997
764
|
if (this.textInputTextArea && this.textInputTextArea.trim() !== '') {
|
|
765
|
+
// that.logger.log('[CONV-FOOTER] sendMessage -> ', this.textInputTextArea);
|
|
766
|
+
// this.resizeInputField();
|
|
767
|
+
// this.messagingService.sendMessage(msg, TYPE_MSG_TEXT);
|
|
768
|
+
// this.setDepartment();
|
|
769
|
+
// this.textInputTextArea = replaceBr(this.textInputTextArea);
|
|
998
770
|
this.sendMessage(this.textInputTextArea, TYPE_MSG_TEXT);
|
|
771
|
+
// this.restoreTextArea();
|
|
999
772
|
}
|
|
1000
|
-
|
|
773
|
+
} else if (keyCode === 9) { // TAB pressed
|
|
774
|
+
event.preventDefault();
|
|
1001
775
|
}
|
|
776
|
+
}
|
|
1002
777
|
|
|
1003
|
-
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* HANDLE: cmd+enter, shiftKey+enter, alt+enter, ctrl+enter
|
|
781
|
+
* @param event
|
|
782
|
+
*/
|
|
783
|
+
onkeydown(event){
|
|
784
|
+
const keyCode = event.which || event.keyCode;
|
|
785
|
+
// metaKey -> COMMAND , shiftKey -> SHIFT, altKey -> ALT, ctrlKey -> CONTROL
|
|
786
|
+
if( (event.metaKey || event.shiftKey || event.altKey || event.ctrlKey) && keyCode===13){
|
|
1004
787
|
event.preventDefault();
|
|
788
|
+
this.textInputTextArea += '\r\n';
|
|
789
|
+
this.resizeInputField();
|
|
1005
790
|
}
|
|
1006
791
|
}
|
|
1007
792
|
|