@chat21/chat21-web-widget 5.1.32-rc4 → 5.1.33
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 +7 -50
- package/Dockerfile +5 -4
- package/angular.json +2 -5
- package/deploy_amazon_beta.sh +7 -17
- package/deploy_amazon_prod.sh +41 -0
- package/docs/changelog/this-branch.md +0 -36
- package/nginx.conf +2 -22
- package/package.json +1 -4
- package/src/app/app.component.ts +9 -10
- package/src/app/app.module.ts +0 -9
- package/src/app/component/conversation-detail/conversation/conversation.component.html +2 -8
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -12
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +5 -45
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +1 -1
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -10
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +0 -1
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +79 -146
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +13 -140
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +7 -124
- package/src/app/component/last-message/last-message.component.ts +1 -4
- package/src/app/component/message/audio/audio.component.ts +5 -0
- package/src/app/component/message/bubble-message/bubble-message.component.html +1 -6
- package/src/app/component/message/bubble-message/bubble-message.component.ts +1 -2
- package/src/app/providers/global-settings.service.ts +0 -21
- package/src/app/providers/translator.service.ts +0 -2
- package/src/app/sass/_variables.scss +0 -3
- package/src/app/utils/globals.ts +1 -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/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/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/launch.js +41 -32
- package/src/launch_template.js +41 -32
- package/tsconfig.json +0 -5
- package/src/app/component/message/audio-sync/audio-sync.component.html +0 -19
- 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 -23
- package/src/app/component/message/audio-sync/audio-sync.component.ts +0 -197
- 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 -34
- 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.service.spec.ts +0 -60
- package/src/app/providers/voice/voice.service.ts +0 -294
- 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/vad/silero_vad_legacy.onnx +0 -0
- package/src/assets/vad/vad.worklet.bundle.min.js +0 -1
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,15 +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
18
|
|
|
21
19
|
@Component({
|
|
22
20
|
selector: 'chat-conversation-footer',
|
|
23
21
|
templateUrl: './conversation-footer.component.html',
|
|
24
22
|
styleUrls: ['./conversation-footer.component.scss']
|
|
25
23
|
})
|
|
26
|
-
export class ConversationFooterComponent implements OnInit, OnChanges
|
|
24
|
+
export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
27
25
|
|
|
28
26
|
@Input() conversationWith: string;
|
|
29
27
|
@Input() attributes: string;
|
|
@@ -34,9 +32,8 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
34
32
|
@Input() userFullname: string;
|
|
35
33
|
@Input() userEmail: string;
|
|
36
34
|
@Input() showAttachmentFooterButton: boolean;
|
|
37
|
-
@Input() showEmojiFooterButton: boolean
|
|
38
|
-
@Input() showAudioRecorderFooterButton: boolean
|
|
39
|
-
@Input() showAudioStreamFooterButton: boolean;
|
|
35
|
+
@Input() showEmojiFooterButton: boolean
|
|
36
|
+
@Input() showAudioRecorderFooterButton: boolean
|
|
40
37
|
// @Input() showContinueConversationButton: boolean;
|
|
41
38
|
@Input() isConversationArchived: boolean;
|
|
42
39
|
@Input() hideTextAreaContent: boolean;
|
|
@@ -45,7 +42,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
45
42
|
@Input() isEmojiiPickerShow: boolean;
|
|
46
43
|
@Input() footerMessagePlaceholder: string;
|
|
47
44
|
@Input() fileUploadAccept: string;
|
|
48
|
-
@Input() closeChatInConversation: boolean;
|
|
49
45
|
@Input() dropEvent: Event;
|
|
50
46
|
@Input() poweredBy: string;
|
|
51
47
|
@Input() stylesMap: Map<string, string>
|
|
@@ -56,8 +52,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
56
52
|
@Output() onChangeTextArea = new EventEmitter<any>();
|
|
57
53
|
@Output() onAttachmentFileButtonClicked = new EventEmitter<any>();
|
|
58
54
|
@Output() onNewConversationButtonClicked = new EventEmitter();
|
|
59
|
-
@Output() onStreamAudioActiveChange = new EventEmitter<boolean>();
|
|
60
|
-
@Output() onCloseChatButtonClicked = new EventEmitter();
|
|
61
55
|
|
|
62
56
|
@ViewChild('chat21_file') public chat21_file: ElementRef;
|
|
63
57
|
// @ViewChild('emojii_container', {read: ViewContainerRef}) selector;
|
|
@@ -91,28 +85,15 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
91
85
|
|
|
92
86
|
showAlertEmoji: boolean = false
|
|
93
87
|
|
|
94
|
-
/** Stream audio UI: icona equalizer → X; alert con onde animate sopra il footer */
|
|
95
|
-
isStreamAudioActive = false;
|
|
96
|
-
/** Sottoscrizione ai segmenti audio (VAD → WebM) dal {@link VoiceService}. */
|
|
97
|
-
private voiceAudioSubscription?: Subscription;
|
|
98
|
-
/** Sottoscrizione al volume audio (real-time) dal {@link VoiceService}. */
|
|
99
|
-
private voiceVolumeSubscription?: Subscription;
|
|
100
|
-
currentVolume = 0;
|
|
101
|
-
wavePath1 = '';
|
|
102
|
-
wavePath2 = '';
|
|
103
|
-
wavePath3 = '';
|
|
104
|
-
|
|
105
88
|
file_size_limit = FILE_SIZE_LIMIT;
|
|
106
89
|
attachmentTooltip: string = '';
|
|
107
|
-
isErrorNetwork: boolean = false;
|
|
108
90
|
|
|
109
91
|
|
|
110
92
|
convertColorToRGBA = convertColorToRGBA;
|
|
111
93
|
private logger: LoggerService = LoggerInstance.getInstance()
|
|
112
94
|
constructor(private chatManager: ChatManager,
|
|
113
95
|
private typingService: TypingService,
|
|
114
|
-
private uploadService: UploadService
|
|
115
|
-
private voiceService: VoiceService) { }
|
|
96
|
+
private uploadService: UploadService) { }
|
|
116
97
|
|
|
117
98
|
ngOnInit() {
|
|
118
99
|
// this.updateAttachmentTooltip();
|
|
@@ -122,8 +103,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
122
103
|
ngOnChanges(changes: SimpleChanges){
|
|
123
104
|
if(changes['conversationWith'] && changes['conversationWith'].currentValue !== undefined){
|
|
124
105
|
this.conversationHandlerService = this.chatManager.getConversationHandlerByConversationId(this.conversationWith);
|
|
125
|
-
this.isStreamAudioActive = false;
|
|
126
|
-
void this.stopVoice();
|
|
127
106
|
}
|
|
128
107
|
if(changes['hideTextReply'] && changes['hideTextReply'].currentValue !== undefined){
|
|
129
108
|
this.restoreTextArea();
|
|
@@ -163,75 +142,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
163
142
|
// }, 500);
|
|
164
143
|
// }
|
|
165
144
|
|
|
166
|
-
/**
|
|
167
|
-
* Microfono + VAD: ogni fine parlato il servizio emette su `audioSegment$` → upload.
|
|
168
|
-
*/
|
|
169
|
-
async initVoice() {
|
|
170
|
-
this.voiceAudioSubscription?.unsubscribe();
|
|
171
|
-
this.voiceVolumeSubscription?.unsubscribe();
|
|
172
|
-
|
|
173
|
-
this.voiceAudioSubscription = this.voiceService.audioSegment$.subscribe((rec) => {
|
|
174
|
-
console.log('[CONV-FOOTER] audioSegment$', rec);
|
|
175
|
-
this.prepareAndUpload(rec.blob);
|
|
176
|
-
});
|
|
177
|
-
this.voiceVolumeSubscription = this.voiceService.volume$.subscribe((volume) => {
|
|
178
|
-
this.currentVolume = volume;
|
|
179
|
-
this.updateWave(volume);
|
|
180
|
-
});
|
|
181
|
-
await this.voiceService.startSession();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async stopVoice(options?: { discardInProgressSegment?: boolean }) {
|
|
185
|
-
this.voiceAudioSubscription?.unsubscribe();
|
|
186
|
-
this.voiceAudioSubscription = undefined;
|
|
187
|
-
|
|
188
|
-
this.voiceVolumeSubscription?.unsubscribe();
|
|
189
|
-
this.voiceVolumeSubscription = undefined;
|
|
190
|
-
|
|
191
|
-
await this.voiceService.stopSession(options);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Messaggio in arrivo da un altro mittente mentre lo stream è attivo: scarta solo il segmento
|
|
196
|
-
* registrato in quel momento (nessun upload); mic + VAD restano attivi, `isStreamAudioActive` true.
|
|
197
|
-
*/
|
|
198
|
-
interruptStreamDueToPeerMessage(): void {
|
|
199
|
-
if (!this.isStreamAudioActive) {
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
this.logger.log('[CONV-FOOTER] discard recording segment: incoming message from peer (stream stays on)');
|
|
203
|
-
try {
|
|
204
|
-
this.voiceService.discardCurrentRecordingSegment();
|
|
205
|
-
} catch (e) {
|
|
206
|
-
this.logger.error('[CONV-FOOTER] interruptStreamDueToPeerMessage', e);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
updateWave(volume: number) {
|
|
211
|
-
const intensity = Math.min(volume / 80, 1); // più sensibile
|
|
212
|
-
|
|
213
|
-
const amp1 = 4 + intensity * 22;
|
|
214
|
-
const amp2 = 2 + intensity * 16;
|
|
215
|
-
const amp3 = 1 + intensity * 12;
|
|
216
|
-
|
|
217
|
-
this.wavePath1 = this.buildWave(42, amp1);
|
|
218
|
-
this.wavePath2 = this.buildWave(50, amp2);
|
|
219
|
-
this.wavePath3 = this.buildWave(58, amp3);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
buildWave(y: number, amp: number): string {
|
|
223
|
-
return `
|
|
224
|
-
M6 ${y}
|
|
225
|
-
Q24 ${y - amp} 42 ${y}
|
|
226
|
-
T78 ${y}
|
|
227
|
-
T98 ${y}
|
|
228
|
-
`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
ngOnDestroy() {
|
|
232
|
-
void this.stopVoice();
|
|
233
|
-
}
|
|
234
|
-
|
|
235
145
|
// ========= begin:: functions send image ======= //
|
|
236
146
|
// START LOAD IMAGE //
|
|
237
147
|
/** load the selected image locally and open the pop up preview */
|
|
@@ -611,7 +521,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
611
521
|
}
|
|
612
522
|
}
|
|
613
523
|
|
|
614
|
-
prepareAndUpload(audioBlob: Blob
|
|
524
|
+
prepareAndUpload(audioBlob: Blob) {
|
|
615
525
|
|
|
616
526
|
this.isFilePendingToUpload = true;
|
|
617
527
|
|
|
@@ -641,7 +551,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
641
551
|
this.logger.log('[UPLOAD] metadata:', metadata);
|
|
642
552
|
|
|
643
553
|
// stesso metodo che già usi
|
|
644
|
-
this.uploadSingle(metadata, file,
|
|
554
|
+
this.uploadSingle(metadata, file, '');
|
|
645
555
|
}
|
|
646
556
|
|
|
647
557
|
// Funzione per convertire Blob in Base64 usando FileReader
|
|
@@ -748,29 +658,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
748
658
|
}
|
|
749
659
|
}
|
|
750
660
|
|
|
751
|
-
async onStreamPressed(event: Event) {
|
|
752
|
-
this.logger.log('[CONV-FOOTER] onStreamPressed:event', event);
|
|
753
|
-
event.preventDefault();
|
|
754
|
-
if (this.showAlertEmoji) {
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
const turningOn = !this.isStreamAudioActive;
|
|
758
|
-
if (turningOn) {
|
|
759
|
-
try {
|
|
760
|
-
await this.initVoice();
|
|
761
|
-
this.isStreamAudioActive = true;
|
|
762
|
-
} catch (e) {
|
|
763
|
-
this.logger.error('[CONV-FOOTER] onStreamPressed: initVoice failed', e);
|
|
764
|
-
this.isStreamAudioActive = false;
|
|
765
|
-
}
|
|
766
|
-
} else {
|
|
767
|
-
await this.stopVoice();
|
|
768
|
-
this.isStreamAudioActive = false;
|
|
769
|
-
}
|
|
770
|
-
this.onStreamAudioActiveChange.emit(this.isStreamAudioActive);
|
|
771
|
-
this.logger.log('[CONV-FOOTER] isStreamAudioActive', this.isStreamAudioActive);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
661
|
async onEmojiiPickerClicked(){
|
|
775
662
|
// if(this.loadPickerModule){
|
|
776
663
|
// this.loadPickerModule = false;
|
|
@@ -822,10 +709,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
822
709
|
this.onNewConversationButtonClicked.emit();
|
|
823
710
|
}
|
|
824
711
|
|
|
825
|
-
onCloseChat(event){
|
|
826
|
-
this.onCloseChatButtonClicked.emit();
|
|
827
|
-
}
|
|
828
|
-
|
|
829
712
|
// onContinueConversation(){
|
|
830
713
|
// this.hideTextAreaContent = false;
|
|
831
714
|
// this.onBackButton.emit(false)
|
|
@@ -12,7 +12,7 @@ import { MIN_WIDTH_IMAGES } from 'src/app/utils/constants';
|
|
|
12
12
|
import { ConversationModel } from 'src/chat21-core/models/conversation';
|
|
13
13
|
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
14
14
|
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
15
|
-
import { commandToMessage, conversationToMessage, isEmojii, isFrame, isImage,
|
|
15
|
+
import { commandToMessage, conversationToMessage, isEmojii, isFrame, isImage, isSameSender } from 'src/chat21-core/utils/utils-message';
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@Component({
|
|
@@ -59,9 +59,6 @@ export class LastMessageComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
59
59
|
ngOnChanges(changes: SimpleChanges) {
|
|
60
60
|
this.logger.debug('[LASTMESSAGE] onChanges', changes)
|
|
61
61
|
if(this.conversation){
|
|
62
|
-
|
|
63
|
-
/** if the message is sent by the logged user, do not add it to the messages array */
|
|
64
|
-
if(isSender(this.conversation.sender, this.g.senderId)) return;
|
|
65
62
|
|
|
66
63
|
if(this.conversation.attributes && this.conversation.attributes.commands){
|
|
67
64
|
this.addCommandMessage(this.conversation)
|
|
@@ -154,10 +154,15 @@ export class AudioComponent implements AfterViewInit {
|
|
|
154
154
|
// });
|
|
155
155
|
|
|
156
156
|
const response = await fetch(this.rawAudioUrl!);
|
|
157
|
+
this.logger.debug('getAudioDuration: response ---> ', response)
|
|
157
158
|
const arrayBuffer = await response.arrayBuffer();
|
|
159
|
+
this.logger.debug('getAudioDuration: arrayBuffer ---> ', arrayBuffer)
|
|
158
160
|
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
|
|
161
|
+
this.logger.debug('getAudioDuration: audioContext ---> ', audioContext)
|
|
159
162
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
163
|
+
this.logger.debug('getAudioDuration: audioBuffer ---> ', audioBuffer)
|
|
160
164
|
this.audioDuration = audioBuffer.duration;
|
|
165
|
+
this.logger.debug('getAudioDuration: audioDuration ---> ', this.audioDuration)
|
|
161
166
|
|
|
162
167
|
}
|
|
163
168
|
|
|
@@ -64,11 +64,6 @@
|
|
|
64
64
|
[stylesMap]="stylesMap">
|
|
65
65
|
</chat-audio>
|
|
66
66
|
|
|
67
|
-
<chat-audio-sync *ngIf="isAudioTTS(message)"
|
|
68
|
-
[message]="message"
|
|
69
|
-
[color]="fontColor">
|
|
70
|
-
</chat-audio-sync>
|
|
71
|
-
|
|
72
67
|
|
|
73
68
|
<!-- <chat-frame *ngIf="message.metadata && message.metadata.type && message.metadata.type.includes('video')"
|
|
74
69
|
[metadata]="message.metadata"
|
|
@@ -80,7 +75,7 @@
|
|
|
80
75
|
<!-- <div *ngIf="message.type == 'text'"> -->
|
|
81
76
|
|
|
82
77
|
<!-- tooltip="{{message.timestamp | dateAgo}} ({{message.timestamp | date:'shortDate'}} {{message.timestamp | date:'HH:mm:ss'}})" placement="bottom" -->
|
|
83
|
-
<div *ngIf="message?.text &&
|
|
78
|
+
<div *ngIf="message?.text && !isAudio(message)" >
|
|
84
79
|
|
|
85
80
|
<!-- [htmlEnabled]="(message?.type==='html')? true : false" -->
|
|
86
81
|
<chat-text *ngIf="message?.type !=='html'"
|
|
@@ -5,7 +5,7 @@ import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service
|
|
|
5
5
|
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
6
6
|
import { MAX_WIDTH_IMAGES, MESSAGE_TYPE_MINE, MESSAGE_TYPE_OTHERS, MIN_WIDTH_IMAGES } from 'src/chat21-core/utils/constants';
|
|
7
7
|
import { convertColorToRGBA } from 'src/chat21-core/utils/utils';
|
|
8
|
-
import { isAudio,
|
|
8
|
+
import { isAudio, isFile, isFrame, isImage, messageType } from 'src/chat21-core/utils/utils-message';
|
|
9
9
|
import { getColorBck } from 'src/chat21-core/utils/utils-user';
|
|
10
10
|
|
|
11
11
|
@Component({
|
|
@@ -26,7 +26,6 @@ export class BubbleMessageComponent implements OnInit {
|
|
|
26
26
|
isFile = isFile;
|
|
27
27
|
isFrame = isFrame;
|
|
28
28
|
isAudio = isAudio;
|
|
29
|
-
isAudioTTS=isAudioTTS;
|
|
30
29
|
convertColorToRGBA = convertColorToRGBA
|
|
31
30
|
|
|
32
31
|
// ========== begin:: check message type functions ======= //
|
|
@@ -1125,22 +1125,11 @@ export class GlobalSettingsService {
|
|
|
1125
1125
|
if (TEMP !== undefined) {
|
|
1126
1126
|
globals.showAudioRecorderFooterButton = (TEMP === true) ? true : false;
|
|
1127
1127
|
}
|
|
1128
|
-
TEMP = tiledeskSettings['showAudioStreamFooterButton'];
|
|
1129
|
-
// this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > showAudioStreamFooterButton:: ', TEMP]);
|
|
1130
|
-
if (TEMP !== undefined) {
|
|
1131
|
-
globals.showAudioStreamFooterButton = (TEMP === true) ? true : false;
|
|
1132
|
-
}
|
|
1133
1128
|
TEMP = tiledeskSettings['size'];
|
|
1134
1129
|
// this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > size:: ', TEMP]);
|
|
1135
1130
|
if (TEMP !== undefined) {
|
|
1136
1131
|
globals.size = TEMP;
|
|
1137
1132
|
}
|
|
1138
|
-
|
|
1139
|
-
TEMP = tiledeskSettings['closeChatInConversation'];
|
|
1140
|
-
// this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > closeChatInConversation:: ', TEMP]);
|
|
1141
|
-
if (TEMP !== undefined) {
|
|
1142
|
-
globals.closeChatInConversation = (TEMP === true) ? true : false;
|
|
1143
|
-
}
|
|
1144
1133
|
}
|
|
1145
1134
|
|
|
1146
1135
|
/**
|
|
@@ -1878,11 +1867,6 @@ export class GlobalSettingsService {
|
|
|
1878
1867
|
globals.showAttachmentFooterButton = stringToBoolean(TEMP);
|
|
1879
1868
|
}
|
|
1880
1869
|
|
|
1881
|
-
TEMP = getParameterByName(windowContext, 'tiledesk_showAudioStreamFooterButton');
|
|
1882
|
-
if (TEMP) {
|
|
1883
|
-
globals.showAudioStreamFooterButton = stringToBoolean(TEMP);
|
|
1884
|
-
}
|
|
1885
|
-
|
|
1886
1870
|
TEMP = getParameterByName(windowContext, 'tiledesk_showEmojiFooterButton');
|
|
1887
1871
|
if (TEMP) {
|
|
1888
1872
|
globals.showEmojiFooterButton = stringToBoolean(TEMP);
|
|
@@ -1892,11 +1876,6 @@ export class GlobalSettingsService {
|
|
|
1892
1876
|
if (TEMP) {
|
|
1893
1877
|
globals.size = TEMP;
|
|
1894
1878
|
}
|
|
1895
|
-
|
|
1896
|
-
TEMP = getParameterByName(windowContext, 'tiledesk_closeChatInConversation');
|
|
1897
|
-
if (TEMP) {
|
|
1898
|
-
globals.closeChatInConversation = stringToBoolean(TEMP);
|
|
1899
|
-
}
|
|
1900
1879
|
|
|
1901
1880
|
}
|
|
1902
1881
|
|
|
@@ -302,7 +302,6 @@ export class TranslatorService {
|
|
|
302
302
|
'CLOSED',
|
|
303
303
|
'LABEL_PREVIEW',
|
|
304
304
|
'MAX_ATTACHMENT',
|
|
305
|
-
'MAX_ATTACHMENT_ERROR',
|
|
306
305
|
'EMOJI'
|
|
307
306
|
];
|
|
308
307
|
|
|
@@ -359,7 +358,6 @@ export class TranslatorService {
|
|
|
359
358
|
globals.LABEL_PREVIEW = res['LABEL_PREVIEW']
|
|
360
359
|
globals.LABEL_ERROR_FIELD_REQUIRED= res['LABEL_ERROR_FIELD_REQUIRED']
|
|
361
360
|
globals.MAX_ATTACHMENT = res['MAX_ATTACHMENT']
|
|
362
|
-
globals.MAX_ATTACHMENT_ERROR = res['MAX_ATTACHMENT_ERROR']
|
|
363
361
|
globals.EMOJI = res['EMOJI']
|
|
364
362
|
|
|
365
363
|
|
|
@@ -36,10 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
--chat-footer-height: 64px;
|
|
38
38
|
--chat-footer-logo-height: 30px;
|
|
39
|
-
--chat-footer-close-button-height: 30px;
|
|
40
39
|
--chat-footer-border-radius: 16px;
|
|
41
|
-
--chat-footer-stream-button-height: 96px;
|
|
42
|
-
--chat-footer-stream-button-padding: 10px 0;
|
|
43
40
|
--chat-footer-background-color: #f6f7fb;
|
|
44
41
|
--chat-footer-color: #1a1a1a;
|
|
45
42
|
--chat-footer-border-color-error: #aa0404;
|
package/src/app/utils/globals.ts
CHANGED
|
@@ -219,7 +219,6 @@ export class Globals {
|
|
|
219
219
|
showEmojiFooterButton: boolean // ******* new ********
|
|
220
220
|
showAttachmentFooterButton: boolean // ******* new ********
|
|
221
221
|
showAudioRecorderFooterButton: boolean // ******* new ********
|
|
222
|
-
showAudioStreamFooterButton: boolean // ******* new ********
|
|
223
222
|
|
|
224
223
|
allowedOnSpecificUrl: boolean // ******* new ********
|
|
225
224
|
allowedOnSpecificUrlList: Array<string> // ******* new ********
|
|
@@ -228,8 +227,6 @@ export class Globals {
|
|
|
228
227
|
fontFamilySource: string; // ******* new ********
|
|
229
228
|
|
|
230
229
|
size: 'min' | 'max' | 'top'; // ******* new ********
|
|
231
|
-
|
|
232
|
-
closeChatInConversation: boolean; // ******* new ********
|
|
233
230
|
constructor(
|
|
234
231
|
) { }
|
|
235
232
|
|
|
@@ -438,8 +435,6 @@ export class Globals {
|
|
|
438
435
|
this.showAttachmentFooterButton = true;
|
|
439
436
|
/** show/hide rec audio option in footer chat-detail page */
|
|
440
437
|
this.showAudioRecorderFooterButton = true;
|
|
441
|
-
/** show/hide stream audio option in footer chat-detail page */
|
|
442
|
-
this.showAudioStreamFooterButton = true;
|
|
443
438
|
/** enabled to set a list of pattern url able to load the widget **/
|
|
444
439
|
this.allowedOnSpecificUrl = false
|
|
445
440
|
/** set a list of pattern url able to load the widget */
|
|
@@ -448,8 +443,7 @@ export class Globals {
|
|
|
448
443
|
this.hasCalloutInWidgetConfig = false;
|
|
449
444
|
/** set widget size from 3 different positions: min, max, top */
|
|
450
445
|
this.size = 'min';
|
|
451
|
-
|
|
452
|
-
this.closeChatInConversation = false;
|
|
446
|
+
|
|
453
447
|
// ============ END: SET EXTERNAL PARAMETERS ==============//
|
|
454
448
|
|
|
455
449
|
|
package/src/assets/i18n/en.json
CHANGED
|
@@ -97,6 +97,5 @@
|
|
|
97
97
|
"EMOJI_NOT_ELLOWED":"Emoji not allowed",
|
|
98
98
|
"DOMAIN_NOT_ALLOWED":"URL contains a non-allowed domain",
|
|
99
99
|
"MAX_ATTACHMENT": "Max allowed size {{FILE_SIZE_LIMIT}}Mb",
|
|
100
|
-
"MAX_ATTACHMENT_ERROR": "The file exceeds the maximum allowed size",
|
|
101
100
|
"EMOJI": "Emoji"
|
|
102
101
|
}
|
package/src/assets/i18n/es.json
CHANGED
|
@@ -97,6 +97,5 @@
|
|
|
97
97
|
"EMOJI_NOT_ELLOWED":"Emoji no permitido",
|
|
98
98
|
"DOMAIN_NOT_ALLOWED":"La URL contiene un dominio no permitido",
|
|
99
99
|
"MAX_ATTACHMENT": "Tamaño máximo permitido {{FILE_SIZE_LIMIT}}Mb",
|
|
100
|
-
"MAX_ATTACHMENT_ERROR": "El archivo supera el tamaño máximo permitido",
|
|
101
100
|
"EMOJI": "Emoji"
|
|
102
101
|
}
|
package/src/assets/i18n/fr.json
CHANGED
|
@@ -97,6 +97,5 @@
|
|
|
97
97
|
"EMOJI_NOT_ELLOWED":"Emoji non autorisé",
|
|
98
98
|
"DOMAIN_NOT_ALLOWED":"L'URL contient un domaine non autorisé",
|
|
99
99
|
"MAX_ATTACHMENT": "Taille maximale autorisée {{FILE_SIZE_LIMIT}}Mo",
|
|
100
|
-
"MAX_ATTACHMENT_ERROR": "Le fichier dépasse la taille maximale autorisée",
|
|
101
100
|
"EMOJI": "Emoji"
|
|
102
101
|
}
|
package/src/assets/i18n/it.json
CHANGED
|
@@ -95,6 +95,5 @@
|
|
|
95
95
|
"EMOJI_NOT_ELLOWED":"Emoji non consentiti",
|
|
96
96
|
"DOMAIN_NOT_ALLOWED":"L'URL contiene un dominio non consentito",
|
|
97
97
|
"MAX_ATTACHMENT": "Dimensione massima consentita {{FILE_SIZE_LIMIT}}Mb",
|
|
98
|
-
"MAX_ATTACHMENT_ERROR": "Il file supera la dimensione massima consentita",
|
|
99
98
|
"EMOJI": "Emoji"
|
|
100
99
|
}
|
|
@@ -253,7 +253,9 @@ export class FirebaseConversationHandler extends ConversationHandlerService {
|
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
|
|
256
257
|
|
|
258
|
+
|
|
257
259
|
this.addRepalceMessageInArray(msg.uid, msg);
|
|
258
260
|
this.messageAdded.next(msg);
|
|
259
261
|
} else {
|
|
@@ -492,9 +494,6 @@ export class FirebaseConversationHandler extends ConversationHandlerService {
|
|
|
492
494
|
commands[i+1]? commands[i+1].time = 0 : null
|
|
493
495
|
}
|
|
494
496
|
}
|
|
495
|
-
|
|
496
|
-
command.message.isJustRecived = isJustRecived( that.startTime.getTime(), msg.timestamp );
|
|
497
|
-
|
|
498
497
|
that.generateMessageObject(msg, command.message, function () {
|
|
499
498
|
i += 1
|
|
500
499
|
if (i < commands.length) {
|
|
@@ -273,16 +273,6 @@ export class MQTTConversationHandler extends ConversationHandlerService {
|
|
|
273
273
|
if(isInfoMessage){
|
|
274
274
|
this.messageInfo.next(msg)
|
|
275
275
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
276
|
|
|
287
277
|
if(isInfoMessage && hideInfoMessage(msg, this.showInfoMessage)){
|
|
288
278
|
//if showBubbleInfoMessage array keys not includes msg.attributes.messagelabel['key'] exclude CURRENT INFO MESSAGE
|
|
@@ -502,8 +492,6 @@ export class MQTTConversationHandler extends ConversationHandlerService {
|
|
|
502
492
|
}
|
|
503
493
|
}
|
|
504
494
|
|
|
505
|
-
command.message.isJustRecived = isJustRecived( that.startTime.getTime(), msg.timestamp );
|
|
506
|
-
|
|
507
495
|
that.generateMessageObject(msg, command.message, i, function () {
|
|
508
496
|
i += 1
|
|
509
497
|
if (i < commands.length) {
|
|
@@ -71,7 +71,7 @@ export class TiledeskRequestsService {
|
|
|
71
71
|
|
|
72
72
|
public getMyRequests(): Promise<{ requests: Array<any>}> {
|
|
73
73
|
this.tiledeskToken = this.appStorage.getItem('tiledeskToken')
|
|
74
|
-
const url = this.URL_TILEDESK_REQUEST + 'me?preflight=true'
|
|
74
|
+
const url = this.URL_TILEDESK_REQUEST + '/me?preflight=true'
|
|
75
75
|
this.logger.log('[TILEDESK-SERVICE] - GET REQUEST url ', url);
|
|
76
76
|
const httpOptions = {
|
|
77
77
|
headers: new HttpHeaders({
|
|
@@ -48,13 +48,6 @@ export function isAudio(message: any) {
|
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export function isAudioTTS(message: any) {
|
|
52
|
-
if (message && message.type && message.type === 'tts' && message.metadata && message.metadata.src && message.metadata.type.includes('audio') ) {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
51
|
/** */
|
|
59
52
|
export function isInfo(message: any) {
|
|
60
53
|
if (message && message.attributes && (message.attributes.subtype === 'info' || message.attributes.subtype === 'info/support')) {
|
|
@@ -773,11 +773,6 @@ export function isAllowedUrlInText(text: string, allowedUrls: string[]) {
|
|
|
773
773
|
return nonWhitelistedDomains.length === 0;
|
|
774
774
|
}
|
|
775
775
|
|
|
776
|
-
// function extractUrls(text: string): string[] {
|
|
777
|
-
// const urlRegex = /https?:\/\/[^\s]+/g;
|
|
778
|
-
// return text.match(urlRegex) || [];
|
|
779
|
-
// }
|
|
780
|
-
|
|
781
776
|
function extractUrls(text: string): string[] {
|
|
782
777
|
// Rileva URL con o senza protocollo (http/https)
|
|
783
778
|
const urlRegex = /\b((https?:\/\/)?(www\.)?[a-z0-9.-]+\.[a-z]{2,})(\/[^\s]*)?/gi;
|
|
@@ -792,3 +787,5 @@ function extractUrls(text: string): string[] {
|
|
|
792
787
|
}
|
|
793
788
|
|
|
794
789
|
|
|
790
|
+
|
|
791
|
+
|
package/src/launch.js
CHANGED
|
@@ -218,67 +218,76 @@ function loadIframe(tiledeskScriptBaseLocation) {
|
|
|
218
218
|
iDiv.appendChild(ifrm);
|
|
219
219
|
|
|
220
220
|
// Funzione helper per caricare iframe con fallback per compatibilità CSP (Wix, etc.)
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
221
|
+
// Priorità: document.write / srcdoc prima della Blob URL. Le Blob URL spesso danno origine opaca
|
|
222
|
+
// (blob:null): l'iframe non può leggere window.parent.tiledeskSettings → projectid mancante.
|
|
223
|
+
function loadIframeContent(iframe, htmlContent) {
|
|
224
224
|
var blobUrl = null;
|
|
225
|
-
|
|
226
|
-
//
|
|
227
|
-
|
|
225
|
+
|
|
226
|
+
// 1) document.write: iframe stessa origine della pagina host → tiledeskSettings sul parent accessibile
|
|
227
|
+
try {
|
|
228
|
+
var cw = iframe.contentWindow;
|
|
229
|
+
if (cw && cw.document) {
|
|
230
|
+
cw.document.open();
|
|
231
|
+
cw.document.write(htmlContent);
|
|
232
|
+
cw.document.close();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
} catch (e) {
|
|
236
|
+
console.warn('[Tiledesk] iframe document.write failed, trying srcdoc/blob:', e);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// 2) srcdoc: stessa origine del parent (HTML5); utile se document.write è bloccato
|
|
240
|
+
if ('srcdoc' in iframe) {
|
|
241
|
+
try {
|
|
242
|
+
iframe.srcdoc = htmlContent;
|
|
243
|
+
return;
|
|
244
|
+
} catch (e) {
|
|
245
|
+
console.warn('[Tiledesk] iframe srcdoc failed, trying blob:', e);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 3) Blob URL (spesso permesso da CSP dove srcdoc/write no; può rompere lettura parent.tiledeskSettings)
|
|
228
250
|
if (typeof Blob !== 'undefined' && typeof URL !== 'undefined' && URL.createObjectURL) {
|
|
229
251
|
try {
|
|
230
252
|
var blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
|
|
231
253
|
blobUrl = URL.createObjectURL(blob);
|
|
232
254
|
iframe.src = blobUrl;
|
|
233
|
-
|
|
234
|
-
// Cleanup del blob URL dopo il caricamento per liberare memoria
|
|
255
|
+
|
|
235
256
|
var originalOnload = iframe.onload;
|
|
236
257
|
iframe.onload = function() {
|
|
237
|
-
// Revoca il blob URL dopo un delay per assicurarsi che tutto sia caricato
|
|
238
258
|
setTimeout(function() {
|
|
239
259
|
if (blobUrl) {
|
|
240
260
|
try {
|
|
241
261
|
URL.revokeObjectURL(blobUrl);
|
|
242
262
|
blobUrl = null;
|
|
243
|
-
} catch(
|
|
244
|
-
console.warn('Error revoking blob URL:',
|
|
263
|
+
} catch (err) {
|
|
264
|
+
console.warn('Error revoking blob URL:', err);
|
|
245
265
|
}
|
|
246
266
|
}
|
|
247
267
|
}, 1000);
|
|
248
268
|
if (originalOnload) originalOnload.call(this);
|
|
249
269
|
};
|
|
250
|
-
return;
|
|
251
|
-
} catch(e) {
|
|
252
|
-
console.warn('Blob URL not available
|
|
270
|
+
return;
|
|
271
|
+
} catch (e) {
|
|
272
|
+
console.warn('Blob URL not available:', e);
|
|
253
273
|
}
|
|
254
274
|
}
|
|
255
|
-
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
if (!isLocalhost && 'srcdoc' in iframe) {
|
|
259
|
-
try {
|
|
260
|
-
iframe.srcdoc = htmlContent;
|
|
261
|
-
return; // srcdoc impostato
|
|
262
|
-
} catch(e) {
|
|
263
|
-
console.warn('srcdoc not allowed, trying document.write:', e);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Metodo 3: document.write (fallback finale, funziona su localhost e browser vecchi)
|
|
268
|
-
if (isLocalhost || (iframe.contentWindow && iframe.contentWindow.document)) {
|
|
275
|
+
|
|
276
|
+
// 4) Ultimo tentativo document.write (iframe magari non pronto al primo passo)
|
|
277
|
+
if (iframe.contentWindow && iframe.contentWindow.document) {
|
|
269
278
|
try {
|
|
270
279
|
iframe.contentWindow.document.open();
|
|
271
280
|
iframe.contentWindow.document.write(htmlContent);
|
|
272
281
|
iframe.contentWindow.document.close();
|
|
273
|
-
return;
|
|
274
|
-
} catch(e) {
|
|
275
|
-
console.error('All iframe loading methods failed:', e);
|
|
282
|
+
return;
|
|
283
|
+
} catch (e) {
|
|
284
|
+
console.error('[Tiledesk] All iframe loading methods failed:', e);
|
|
276
285
|
}
|
|
277
286
|
}
|
|
278
287
|
}
|
|
279
288
|
|
|
280
289
|
// Carica il contenuto dell'iframe con fallback automatico
|
|
281
|
-
loadIframeContent(ifrm, srcTileDesk
|
|
290
|
+
loadIframeContent(ifrm, srcTileDesk);
|
|
282
291
|
|
|
283
292
|
|
|
284
293
|
}
|