@chat21/chat21-web-widget 5.1.32-rc1 → 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/CHANGELOG.md +51 -0
- package/deploy_amazon_beta.sh +17 -7
- package/nginx.conf +22 -2
- package/package.json +1 -1
- package/src/app/app.module.ts +2 -0
- package/src/app/component/conversation-detail/conversation/conversation.component.html +2 -1
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +10 -0
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +13 -1
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +7 -3
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +18 -0
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +24 -41
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +37 -51
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +37 -26
- 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/message/audio-sync/audio-sync.component.html +0 -1
- package/src/app/component/message/audio-sync/audio-sync.component.scss +0 -1
- package/src/app/component/message/audio-sync/audio-sync.component.ts +378 -17
- package/src/app/providers/tts-audio-playback-coordinator.service.ts +93 -0
- package/src/app/providers/voice/voice.service.ts +117 -5
- package/src/app/sass/_variables.scss +2 -0
- package/src/launch.js +41 -32
- package/src/launch_template.js +41 -32
- package/deploy_amazon_prod.sh +0 -41
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Inject, Injectable, Optional } from '@angular/core';
|
|
2
2
|
import type { MicVAD } from '@ricky0123/vad-web';
|
|
3
3
|
import { getDefaultRealTimeVADOptions } from '@ricky0123/vad-web';
|
|
4
|
-
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
|
4
|
+
import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs';
|
|
5
5
|
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
6
6
|
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
7
7
|
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from './audio.types';
|
|
13
13
|
import { SpeechToTextProvider } from './STT&TTS/speech-provider.abstract';
|
|
14
14
|
import { VadService } from './vad.service';
|
|
15
|
+
import { TtsAudioPlaybackCoordinator } from '../tts-audio-playback-coordinator.service';
|
|
15
16
|
|
|
16
17
|
const VOICE_RECORDING_MIME = 'audio/webm';
|
|
17
18
|
|
|
@@ -33,10 +34,29 @@ export class VoiceService {
|
|
|
33
34
|
/** Emesso a ogni fine segmento parlato: audio WebM + opzionalmente `transcript` / `transcriptionError`. */
|
|
34
35
|
readonly audioSegment$: Observable<VoiceSegmentPayload> = this.audioSegmentSubject.asObservable();
|
|
35
36
|
|
|
37
|
+
private readonly speechStartSubject = new Subject<void>();
|
|
38
|
+
/** Emesso quando il microfono intercetta parlato (VAD speech start). */
|
|
39
|
+
readonly speechStart$: Observable<void> = this.speechStartSubject.asObservable();
|
|
40
|
+
|
|
41
|
+
private readonly speechEndSubject = new Subject<void>();
|
|
42
|
+
/** Emesso quando il parlato termina (VAD speech end). */
|
|
43
|
+
readonly speechEnd$: Observable<void> = this.speechEndSubject.asObservable();
|
|
44
|
+
|
|
36
45
|
// 🔊 REALTIME VOLUME STREAM
|
|
37
46
|
private readonly volumeSubject = new BehaviorSubject<number>(0);
|
|
38
47
|
readonly volume$: Observable<number> = this.volumeSubject.asObservable();
|
|
39
48
|
|
|
49
|
+
// 🎙️ TTS GATE — suppresses segment emission while TTS is playing
|
|
50
|
+
private isTTSActive = false;
|
|
51
|
+
private ttsGateSub?: Subscription;
|
|
52
|
+
|
|
53
|
+
// 🚫 ACQUISITION GATE — pauses VAD from speech-end until TTS response cycle completes
|
|
54
|
+
private isWaitingForResponse = false;
|
|
55
|
+
private responseTimeoutId?: ReturnType<typeof setTimeout>;
|
|
56
|
+
private readonly _isAcquisitionBlocked$ = new BehaviorSubject<boolean>(false);
|
|
57
|
+
/** Emits `true` from user speech-end until VAD resumes after TTS finishes; drives the grey orb. */
|
|
58
|
+
readonly isAcquisitionBlocked$: Observable<boolean> = this._isAcquisitionBlocked$.asObservable();
|
|
59
|
+
|
|
40
60
|
// 🎧 AUDIO ANALYSER
|
|
41
61
|
private audioContext?: AudioContext;
|
|
42
62
|
private analyser?: AnalyserNode;
|
|
@@ -47,6 +67,7 @@ export class VoiceService {
|
|
|
47
67
|
|
|
48
68
|
constructor(
|
|
49
69
|
private readonly vadService: VadService,
|
|
70
|
+
private readonly ttsPlayback: TtsAudioPlaybackCoordinator,
|
|
50
71
|
@Optional() @Inject(SpeechToTextProvider) private readonly speechToText: SpeechToTextProvider | null,
|
|
51
72
|
) {}
|
|
52
73
|
|
|
@@ -83,11 +104,18 @@ export class VoiceService {
|
|
|
83
104
|
},
|
|
84
105
|
onSpeechStart: () => {
|
|
85
106
|
this.logger.log('[VoiceService] speech start');
|
|
107
|
+
this.speechStartSubject.next();
|
|
86
108
|
this.startMediaRecorderSegment();
|
|
87
109
|
},
|
|
88
110
|
onSpeechEnd: () => {
|
|
89
111
|
this.logger.log('[VoiceService] speech end');
|
|
112
|
+
this.speechEndSubject.next();
|
|
90
113
|
this.stopMediaRecorderSegment();
|
|
114
|
+
// Pause VAD immediately — new recordings are blocked until the TTS response cycle completes.
|
|
115
|
+
this.isWaitingForResponse = true;
|
|
116
|
+
this._isAcquisitionBlocked$.next(true);
|
|
117
|
+
this.setResponseSafetyTimeout();
|
|
118
|
+
void this.vad?.pause();
|
|
91
119
|
},
|
|
92
120
|
minSpeechMs: 480,
|
|
93
121
|
redemptionMs: 1920,
|
|
@@ -98,11 +126,31 @@ export class VoiceService {
|
|
|
98
126
|
|
|
99
127
|
// 🔁 start volume loop
|
|
100
128
|
this.startVolumeLoop();
|
|
129
|
+
|
|
130
|
+
// 🎙️ gate segments while TTS is playing; resume VAD when TTS cycle completes
|
|
131
|
+
this.ttsGateSub = this.ttsPlayback.isTTSPlaying$.subscribe((playing) => {
|
|
132
|
+
this.isTTSActive = playing;
|
|
133
|
+
this.logger.log('[VoiceService] TTS gate', playing ? 'closed (bot speaking)' : 'open (listening)');
|
|
134
|
+
if (!playing && this.isWaitingForResponse) {
|
|
135
|
+
this.resumeVadAfterResponse();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
101
138
|
}
|
|
102
139
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
140
|
+
/**
|
|
141
|
+
* @param options.discardInProgressSegment — non inviare STT/upload per il segmento WebM corrente (es. interruzione da messaggio in arrivo).
|
|
142
|
+
*/
|
|
143
|
+
async stopSession(options?: { discardInProgressSegment?: boolean }): Promise<void> {
|
|
144
|
+
const discard = options?.discardInProgressSegment === true;
|
|
145
|
+
|
|
146
|
+
if (this.mediaRecorder) {
|
|
147
|
+
if (discard) {
|
|
148
|
+
this.mediaRecorder.onstop = null;
|
|
149
|
+
this.mediaRecorder.ondataavailable = null;
|
|
150
|
+
}
|
|
151
|
+
if (this.mediaRecorder.state === 'recording') {
|
|
152
|
+
this.mediaRecorder.stop();
|
|
153
|
+
}
|
|
106
154
|
}
|
|
107
155
|
|
|
108
156
|
this.mediaRecorder = undefined;
|
|
@@ -132,6 +180,63 @@ export class VoiceService {
|
|
|
132
180
|
this.volumeSubject.next(0);
|
|
133
181
|
|
|
134
182
|
this.onRecordingComplete = undefined;
|
|
183
|
+
|
|
184
|
+
// 🎙️ release TTS gate subscription
|
|
185
|
+
this.ttsGateSub?.unsubscribe();
|
|
186
|
+
this.ttsGateSub = undefined;
|
|
187
|
+
this.isTTSActive = false;
|
|
188
|
+
|
|
189
|
+
// 🚫 clear acquisition gate
|
|
190
|
+
clearTimeout(this.responseTimeoutId);
|
|
191
|
+
this.responseTimeoutId = undefined;
|
|
192
|
+
this.isWaitingForResponse = false;
|
|
193
|
+
this._isAcquisitionBlocked$.next(false);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Scarta il segmento WebM in corso (nessun upload/STT) senza chiudere VAD, mic o sessione.
|
|
198
|
+
* Lo stream resta in ascolto per il prossimo `onSpeechStart`.
|
|
199
|
+
*/
|
|
200
|
+
discardCurrentRecordingSegment(): void {
|
|
201
|
+
if (this.mediaRecorder) {
|
|
202
|
+
this.mediaRecorder.onstop = null;
|
|
203
|
+
this.mediaRecorder.ondataavailable = null;
|
|
204
|
+
if (this.mediaRecorder.state === 'recording') {
|
|
205
|
+
this.mediaRecorder.stop();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
this.mediaRecorder = undefined;
|
|
209
|
+
this.audioChunks = [];
|
|
210
|
+
this.logger.log('[VoiceService] discarded in-progress segment; VAD session unchanged');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 🔄 RESUME VAD AFTER RESPONSE
|
|
215
|
+
* Called when isTTSPlaying$ goes false while isWaitingForResponse is true,
|
|
216
|
+
* or by the safety timeout if no TTS response arrives within 30 s.
|
|
217
|
+
*/
|
|
218
|
+
private resumeVadAfterResponse(): void {
|
|
219
|
+
this.isWaitingForResponse = false;
|
|
220
|
+
clearTimeout(this.responseTimeoutId);
|
|
221
|
+
this.responseTimeoutId = undefined;
|
|
222
|
+
this._isAcquisitionBlocked$.next(false);
|
|
223
|
+
if (this.vad) {
|
|
224
|
+
this.vad.start().catch((e) =>
|
|
225
|
+
this.logger.log('[VoiceService] VAD resume error', e),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* ⏱️ SAFETY TIMEOUT
|
|
232
|
+
* Forces VAD re-enable after 30 s in case no TTS response ever arrives.
|
|
233
|
+
*/
|
|
234
|
+
private setResponseSafetyTimeout(): void {
|
|
235
|
+
clearTimeout(this.responseTimeoutId);
|
|
236
|
+
this.responseTimeoutId = setTimeout(() => {
|
|
237
|
+
this.logger.log('[VoiceService] safety timeout: re-enabling VAD acquisition');
|
|
238
|
+
this.resumeVadAfterResponse();
|
|
239
|
+
}, 30_000);
|
|
135
240
|
}
|
|
136
241
|
|
|
137
242
|
/**
|
|
@@ -161,7 +266,9 @@ export class VoiceService {
|
|
|
161
266
|
return;
|
|
162
267
|
}
|
|
163
268
|
|
|
164
|
-
this.analyser.getByteFrequencyData(
|
|
269
|
+
this.analyser.getByteFrequencyData(
|
|
270
|
+
this.dataArray as Parameters<AnalyserNode['getByteFrequencyData']>[0],
|
|
271
|
+
);
|
|
165
272
|
|
|
166
273
|
let sum = 0;
|
|
167
274
|
for (let i = 0; i < this.dataArray.length; i++) {
|
|
@@ -255,6 +362,11 @@ export class VoiceService {
|
|
|
255
362
|
* 📡 EMIT RESULT
|
|
256
363
|
*/
|
|
257
364
|
private emitSegmentPayload(payload: VoiceSegmentPayload): void {
|
|
365
|
+
if (this.isTTSActive) {
|
|
366
|
+
this.logger.log('[VoiceService] segment suppressed — TTS is playing');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
258
370
|
this.logger.log( '[VoiceService] segment ready', payload.transcript ?? payload.transcriptionError ?? payload.blob.size);
|
|
259
371
|
|
|
260
372
|
this.audioSegmentSubject.next(payload);
|
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
--chat-footer-logo-height: 30px;
|
|
39
39
|
--chat-footer-close-button-height: 30px;
|
|
40
40
|
--chat-footer-border-radius: 16px;
|
|
41
|
+
--chat-footer-stream-button-height: 0px; //96px;
|
|
42
|
+
--chat-footer-stream-button-padding: 10px 0;
|
|
41
43
|
--chat-footer-background-color: #f6f7fb;
|
|
42
44
|
--chat-footer-color: #1a1a1a;
|
|
43
45
|
--chat-footer-border-color-error: #aa0404;
|
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
|
}
|
package/src/launch_template.js
CHANGED
|
@@ -219,67 +219,76 @@ function loadIframe(tiledeskScriptBaseLocation) {
|
|
|
219
219
|
iDiv.appendChild(ifrm);
|
|
220
220
|
|
|
221
221
|
// Funzione helper per caricare iframe con fallback per compatibilità CSP (Wix, etc.)
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
// Priorità: document.write / srcdoc prima della Blob URL. Le Blob URL spesso danno origine opaca
|
|
223
|
+
// (blob:null): l'iframe non può leggere window.parent.tiledeskSettings → projectid mancante.
|
|
224
|
+
function loadIframeContent(iframe, htmlContent) {
|
|
225
225
|
var blobUrl = null;
|
|
226
|
-
|
|
227
|
-
//
|
|
228
|
-
|
|
226
|
+
|
|
227
|
+
// 1) document.write: iframe stessa origine della pagina host → tiledeskSettings sul parent accessibile
|
|
228
|
+
try {
|
|
229
|
+
var cw = iframe.contentWindow;
|
|
230
|
+
if (cw && cw.document) {
|
|
231
|
+
cw.document.open();
|
|
232
|
+
cw.document.write(htmlContent);
|
|
233
|
+
cw.document.close();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
} catch (e) {
|
|
237
|
+
console.warn('[Tiledesk] iframe document.write failed, trying srcdoc/blob:', e);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 2) srcdoc: stessa origine del parent (HTML5); utile se document.write è bloccato
|
|
241
|
+
if ('srcdoc' in iframe) {
|
|
242
|
+
try {
|
|
243
|
+
iframe.srcdoc = htmlContent;
|
|
244
|
+
return;
|
|
245
|
+
} catch (e) {
|
|
246
|
+
console.warn('[Tiledesk] iframe srcdoc failed, trying blob:', e);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 3) Blob URL (spesso permesso da CSP dove srcdoc/write no; può rompere lettura parent.tiledeskSettings)
|
|
229
251
|
if (typeof Blob !== 'undefined' && typeof URL !== 'undefined' && URL.createObjectURL) {
|
|
230
252
|
try {
|
|
231
253
|
var blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
|
|
232
254
|
blobUrl = URL.createObjectURL(blob);
|
|
233
255
|
iframe.src = blobUrl;
|
|
234
|
-
|
|
235
|
-
// Cleanup del blob URL dopo il caricamento per liberare memoria
|
|
256
|
+
|
|
236
257
|
var originalOnload = iframe.onload;
|
|
237
258
|
iframe.onload = function() {
|
|
238
|
-
// Revoca il blob URL dopo un delay per assicurarsi che tutto sia caricato
|
|
239
259
|
setTimeout(function() {
|
|
240
260
|
if (blobUrl) {
|
|
241
261
|
try {
|
|
242
262
|
URL.revokeObjectURL(blobUrl);
|
|
243
263
|
blobUrl = null;
|
|
244
|
-
} catch(
|
|
245
|
-
console.warn('Error revoking blob URL:',
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.warn('Error revoking blob URL:', err);
|
|
246
266
|
}
|
|
247
267
|
}
|
|
248
268
|
}, 1000);
|
|
249
269
|
if (originalOnload) originalOnload.call(this);
|
|
250
270
|
};
|
|
251
|
-
return;
|
|
252
|
-
} catch(e) {
|
|
253
|
-
console.warn('Blob URL not available
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Metodo 2: srcdoc (fallback se Blob URL non disponibile)
|
|
258
|
-
// Skip per localhost (usa document.write per compatibilità sviluppo)
|
|
259
|
-
if (!isLocalhost && 'srcdoc' in iframe) {
|
|
260
|
-
try {
|
|
261
|
-
iframe.srcdoc = htmlContent;
|
|
262
|
-
return; // srcdoc impostato
|
|
263
|
-
} catch(e) {
|
|
264
|
-
console.warn('srcdoc not allowed, trying document.write:', e);
|
|
271
|
+
return;
|
|
272
|
+
} catch (e) {
|
|
273
|
+
console.warn('Blob URL not available:', e);
|
|
265
274
|
}
|
|
266
275
|
}
|
|
267
|
-
|
|
268
|
-
//
|
|
269
|
-
if (
|
|
276
|
+
|
|
277
|
+
// 4) Ultimo tentativo document.write (iframe magari non pronto al primo passo)
|
|
278
|
+
if (iframe.contentWindow && iframe.contentWindow.document) {
|
|
270
279
|
try {
|
|
271
280
|
iframe.contentWindow.document.open();
|
|
272
281
|
iframe.contentWindow.document.write(htmlContent);
|
|
273
282
|
iframe.contentWindow.document.close();
|
|
274
|
-
return;
|
|
275
|
-
} catch(e) {
|
|
276
|
-
console.error('All iframe loading methods failed:', e);
|
|
283
|
+
return;
|
|
284
|
+
} catch (e) {
|
|
285
|
+
console.error('[Tiledesk] All iframe loading methods failed:', e);
|
|
277
286
|
}
|
|
278
287
|
}
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
// Carica il contenuto dell'iframe con fallback automatico
|
|
282
|
-
loadIframeContent(ifrm, srcTileDesk
|
|
291
|
+
loadIframeContent(ifrm, srcTileDesk);
|
|
283
292
|
|
|
284
293
|
|
|
285
294
|
}
|
package/deploy_amazon_prod.sh
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# npm version patch
|
|
2
|
-
version=`node -e 'console.log(require("./package.json").version)'`
|
|
3
|
-
echo "version $version"
|
|
4
|
-
|
|
5
|
-
npm i
|
|
6
|
-
|
|
7
|
-
cp src/environments/real_data/environment.prod.ts src/environments/environment.prod.ts
|
|
8
|
-
|
|
9
|
-
# --build-optimizer=false if localstorage is disabled (webview) appears https://github.com/firebase/angularfire/issues/970
|
|
10
|
-
ng build --configuration="prod" --aot=true
|
|
11
|
-
##--base-href='./v5/' --output-hashing none
|
|
12
|
-
|
|
13
|
-
### SET HASHING : START ###
|
|
14
|
-
cp ./src/launch_template.js ./dist/browser/launch.js
|
|
15
|
-
node ./src/build_launch.js
|
|
16
|
-
### SET HASHING : END ###
|
|
17
|
-
|
|
18
|
-
#### FIREBASE #####
|
|
19
|
-
# cd dist
|
|
20
|
-
# # aws s3 sync . s3://tiledesk-widget/v5/latest/
|
|
21
|
-
# aws s3 sync . s3://tiledesk-widget/v5/$version/ --cache-control max-age=300
|
|
22
|
-
# aws s3 sync . s3://tiledesk-widget/v5/ --cache-control max-age=300
|
|
23
|
-
# cd ..
|
|
24
|
-
|
|
25
|
-
# #### MQTT #####
|
|
26
|
-
cd dist/browser
|
|
27
|
-
# aws s3 sync . s3://tiledesk-widget/v5/latest/
|
|
28
|
-
aws s3 sync . s3://tiledesk-widget/v6/$version/ --cache-control max-age=86400 --exclude='launch.js' #8days
|
|
29
|
-
aws s3 sync . s3://tiledesk-widget/v6/$version/ --cache-control "no-store,no-cache,private" --exclude='*' --include='launch.js'
|
|
30
|
-
aws s3 sync . s3://tiledesk-widget/v6/ --cache-control max-age=86400 --exclude='launch.js' #8days
|
|
31
|
-
aws s3 sync . s3://tiledesk-widget/v6/ --cache-control "no-store,no-cache,private" --exclude='*' --include='launch.js'
|
|
32
|
-
cd ../..
|
|
33
|
-
|
|
34
|
-
aws cloudfront create-invalidation --distribution-id E3EJDWEHY08CZZ --paths "/*"
|
|
35
|
-
|
|
36
|
-
git restore src/environments/environment.prod.ts
|
|
37
|
-
|
|
38
|
-
echo new version deployed $version on s3://tiledesk-widget/v6
|
|
39
|
-
echo available on https://s3.eu-west-1.amazonaws.com/tiledesk-widget/v6/index.html
|
|
40
|
-
echo https://widget.tiledesk.com/v6/index.html
|
|
41
|
-
echo https://widget.tiledesk.com/v6/$version/index.html
|