@chat21/chat21-web-widget 5.1.20 → 5.1.23
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 +9 -0
- package/package.json +1 -1
- package/src/app/app.component.ts +30 -7
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +1 -1
- package/src/app/component/home-conversations/home-conversations.component.html +16 -6
- package/src/app/component/home-conversations/home-conversations.component.ts +29 -0
- package/src/app/component/list-conversations/list-conversations.component.html +8 -3
- package/src/app/component/list-conversations/list-conversations.component.ts +29 -0
- package/src/app/providers/global-settings.service.ts +1 -1
- package/src/chat21-core/providers/abstract/upload.service.ts +5 -1
- package/src/chat21-core/providers/firebase/firebase-upload.service.ts +141 -12
- package/src/chat21-core/providers/native/native-image-repo.ts +1 -1
- package/src/chat21-core/providers/native/native-upload-service.ts +143 -46
- package/src/launch.js +61 -6
- package/src/launch_template.js +61 -6
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
### **Copyrigth**:
|
|
7
7
|
*Tiledesk SRL*
|
|
8
8
|
|
|
9
|
+
# 5.1.23
|
|
10
|
+
- **changed**: API for upload a file/iamges
|
|
11
|
+
|
|
12
|
+
# 5.1.22
|
|
13
|
+
- **changed**: Updated Launch.js for Wix and Shopify by bypassing scrdoc
|
|
14
|
+
|
|
15
|
+
# 5.1.21
|
|
16
|
+
- **bug fixed**: saved the widget's size state to local storage (in HP conversations)
|
|
17
|
+
|
|
9
18
|
# 5.1.20
|
|
10
19
|
- **changed**: marked pipe do not render /n
|
|
11
20
|
|
package/package.json
CHANGED
package/src/app/app.component.ts
CHANGED
|
@@ -262,7 +262,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
262
262
|
this.tiledeskRequestsService.initialize(this.appConfigService.getConfig().apiUrl, this.g.projectid)
|
|
263
263
|
this.messagingAuthService.initialize();
|
|
264
264
|
this.chatManager.initialize();
|
|
265
|
-
this.uploadService.initialize();
|
|
265
|
+
this.uploadService.initialize(this.g.projectid);
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
|
|
@@ -2038,27 +2038,50 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
2038
2038
|
}
|
|
2039
2039
|
}
|
|
2040
2040
|
|
|
2041
|
-
onWidgetSizeChange(mode:
|
|
2042
|
-
|
|
2043
|
-
|
|
2041
|
+
onWidgetSizeChange(mode: any) {
|
|
2042
|
+
const normalize = (val: any): 'min' | 'max' | 'top' => {
|
|
2043
|
+
const v = (typeof val === 'string') ? val.toLowerCase().trim() : '';
|
|
2044
|
+
return (v === 'min' || v === 'max' || v === 'top') ? (v as any) : 'min';
|
|
2045
|
+
};
|
|
2046
|
+
const normalizedMode = normalize(mode);
|
|
2047
|
+
|
|
2048
|
+
const tiledeskDiv = this.g.windowContext?.window?.document?.getElementById('tiledeskdiv');
|
|
2049
|
+
this.g.size = normalizedMode;
|
|
2050
|
+
if (!tiledeskDiv) {
|
|
2051
|
+
// Widget container not yet available; still persist choice for later restores.
|
|
2052
|
+
try {
|
|
2053
|
+
this.appStorageService.setItem('size', normalizedMode);
|
|
2054
|
+
} catch (e) {
|
|
2055
|
+
this.logger.warn('[APP-COMP] onWidgetSizeChange > cannot persist size', e);
|
|
2056
|
+
}
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2044
2060
|
let parent = tiledeskDiv.parentElement as HTMLElement | null;
|
|
2045
2061
|
|
|
2046
|
-
if(
|
|
2062
|
+
if(normalizedMode==='max'){
|
|
2047
2063
|
tiledeskDiv.classList.add('max-size')
|
|
2048
2064
|
tiledeskDiv.classList.remove('min-size')
|
|
2049
2065
|
tiledeskDiv.classList.remove('top-size')
|
|
2050
2066
|
if(parent) parent.classList.remove('overlay--popup');
|
|
2051
|
-
} else if(
|
|
2067
|
+
} else if(normalizedMode==='min'){
|
|
2052
2068
|
tiledeskDiv.classList.add('min-size')
|
|
2053
2069
|
tiledeskDiv.classList.remove('max-size')
|
|
2054
2070
|
tiledeskDiv.classList.remove('top-size')
|
|
2055
2071
|
if(parent) parent.classList.remove('overlay--popup');
|
|
2056
|
-
} else if(
|
|
2072
|
+
} else if(normalizedMode=== 'top'){
|
|
2057
2073
|
tiledeskDiv.classList.add('top-size')
|
|
2058
2074
|
tiledeskDiv.classList.remove('max-size')
|
|
2059
2075
|
tiledeskDiv.classList.remove('min-size')
|
|
2060
2076
|
if(parent) parent.classList.add('overlay--popup');
|
|
2061
2077
|
}
|
|
2078
|
+
|
|
2079
|
+
// Persist size changes also from the home (conversations list) view.
|
|
2080
|
+
try {
|
|
2081
|
+
this.appStorageService.setItem('size', normalizedMode);
|
|
2082
|
+
} catch (e) {
|
|
2083
|
+
this.logger.warn('[APP-COMP] onWidgetSizeChange > cannot persist size', e);
|
|
2084
|
+
}
|
|
2062
2085
|
}
|
|
2063
2086
|
|
|
2064
2087
|
/**
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts
CHANGED
|
@@ -316,7 +316,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
316
316
|
// });
|
|
317
317
|
// this.resetLoadImage();
|
|
318
318
|
|
|
319
|
-
this.uploadService.
|
|
319
|
+
this.uploadService.uploadFile(this.senderId, currentUpload).then(data => {
|
|
320
320
|
that.logger.log('[CONV-FOOTER] AppComponent::uploadSingle:: downloadURL', data);
|
|
321
321
|
that.logger.log(`[CONV-FOOTER] Successfully uploaded file and got download link - ${data}`);
|
|
322
322
|
|
|
@@ -38,9 +38,14 @@
|
|
|
38
38
|
<!--CASE: no conversations EXIST - >1 agents is available -->
|
|
39
39
|
<div *ngIf="(!listConversations || listConversations.length == 0) && availableAgents && availableAgents.length > 1 && g.showAvailableAgents === true" style="display: flex; margin: 20px 30px;">
|
|
40
40
|
<div *ngFor="let agent of availableAgents" class="c21-pallozzo">
|
|
41
|
-
<div class="c21-ball" [ngStyle] = "{ 'background-color':setColorFromString(agent.firstname) }" >
|
|
42
|
-
<span class="c21-ball-label">{{avatarPlaceholder(agent.firstname)}}</span>
|
|
43
|
-
<
|
|
41
|
+
<div class="c21-ball" [ngStyle] = "{ 'background-color': isImageLoaded(agent) ? 'transparent' : setColorFromString(agent.firstname) }" >
|
|
42
|
+
<span *ngIf="!isImageLoaded(agent)" class="c21-ball-label">{{avatarPlaceholder(agent.firstname)}}</span>
|
|
43
|
+
<img *ngIf="agent.imageurl"
|
|
44
|
+
[src]="agent.imageurl"
|
|
45
|
+
style="display: none;"
|
|
46
|
+
(load)="onImageLoad(agent)"
|
|
47
|
+
(error)="onImageError(agent)">
|
|
48
|
+
<div *ngIf="isImageLoaded(agent)" #avatarImage class="c21-avatar-image" [style.background-image]="'url(' + agent.imageurl + ')'"></div>
|
|
44
49
|
</div>
|
|
45
50
|
</div>
|
|
46
51
|
</div>
|
|
@@ -48,9 +53,14 @@
|
|
|
48
53
|
<!--CASE: no conversations EXIST - 1 agents is available -->
|
|
49
54
|
<div class="flex-container" *ngIf="(!listConversations || listConversations.length == 0) && availableAgents && availableAgents.length === 1 && g.showAvailableAgents === true">
|
|
50
55
|
<div *ngFor="let agent of availableAgents" class="c21-pallozzo flex-inline-agent ">
|
|
51
|
-
<div class="c21-ball" [ngStyle] = "{ 'background-color':setColorFromString(agent.firstname) }" >
|
|
52
|
-
<span class="c21-ball-label">{{avatarPlaceholder(agent.firstname)}}</span>
|
|
53
|
-
<
|
|
56
|
+
<div class="c21-ball" [ngStyle] = "{ 'background-color': isImageLoaded(agent) ? 'transparent' : setColorFromString(agent.firstname) }" >
|
|
57
|
+
<span *ngIf="!isImageLoaded(agent)" class="c21-ball-label">{{avatarPlaceholder(agent.firstname)}}</span>
|
|
58
|
+
<img *ngIf="agent.imageurl"
|
|
59
|
+
[src]="agent.imageurl"
|
|
60
|
+
style="display: none;"
|
|
61
|
+
(load)="onImageLoad(agent)"
|
|
62
|
+
(error)="onImageError(agent)">
|
|
63
|
+
<div *ngIf="isImageLoaded(agent)" #avatarImage class="c21-avatar-image" [style.background-image]="'url(' + agent.imageurl + ')'"></div>
|
|
54
64
|
</div>
|
|
55
65
|
</div>
|
|
56
66
|
<button tabindex="1040" aflistconv #aflistconv class="c21-button-primary" (click)="openNewConversation()" [ngStyle]="{'background-color': g.themeColor, 'border-color': g.themeColor, 'color': g.themeForegroundColor }">
|
|
@@ -65,6 +65,7 @@ export class HomeConversationsComponent implements OnInit, OnDestroy {
|
|
|
65
65
|
themeForegroundColor = '';
|
|
66
66
|
LABEL_START_NW_CONV: string;
|
|
67
67
|
availableAgents: Array<UserAgent> = [];
|
|
68
|
+
imageLoadedMap: Map<string, boolean> = new Map<string, boolean>();
|
|
68
69
|
// ========= end:: variabili del componente ======== //
|
|
69
70
|
|
|
70
71
|
waitingTime: number;
|
|
@@ -203,6 +204,34 @@ export class HomeConversationsComponent implements OnInit, OnDestroy {
|
|
|
203
204
|
this.onConversationLoaded.emit(conversation)
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Verifica se l'immagine dell'agente esiste e si carica correttamente
|
|
209
|
+
*/
|
|
210
|
+
isImageLoaded(agent: UserAgent): boolean {
|
|
211
|
+
if (!agent?.imageurl) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
return this.imageLoadedMap.get(agent.id) === true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Gestisce il caricamento riuscito dell'immagine
|
|
219
|
+
*/
|
|
220
|
+
onImageLoad(agent: UserAgent) {
|
|
221
|
+
if (agent?.id && agent?.imageurl) {
|
|
222
|
+
this.imageLoadedMap.set(agent.id, true);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Gestisce l'errore di caricamento dell'immagine
|
|
228
|
+
*/
|
|
229
|
+
onImageError(agent: UserAgent) {
|
|
230
|
+
if (agent?.id) {
|
|
231
|
+
this.imageLoadedMap.set(agent.id, false);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
206
235
|
private openConversationByID(conversation) {
|
|
207
236
|
this.logger.debug('[HOMECONVERSATIONS] openConversationByID: ', conversation);
|
|
208
237
|
if ( conversation ) {
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
<button tabindex="1103" class="c21-item-conversation" (click)="openConversationByID(conversation)">
|
|
4
4
|
<div class="c21-body-conv">
|
|
5
5
|
<div class="c21-left-conv">
|
|
6
|
-
<div class="c21-ball" [ngStyle]="{'background': 'linear-gradient(rgb(255,255,255) -125%, ' + conversation.color + ')'}">
|
|
7
|
-
<span class="c21-ball-label">{{conversation?.avatar}}</span>
|
|
8
|
-
<
|
|
6
|
+
<div class="c21-ball" [ngStyle]="{'background': isImageLoaded(conversation) ? 'transparent' : 'linear-gradient(rgb(255,255,255) -125%, ' + conversation.color + ')'}">
|
|
7
|
+
<span *ngIf="!isImageLoaded(conversation)" class="c21-ball-label">{{conversation?.avatar}}</span>
|
|
8
|
+
<img *ngIf="conversation?.image"
|
|
9
|
+
[src]="conversation.image"
|
|
10
|
+
style="display: none;"
|
|
11
|
+
(load)="onImageLoad(conversation)"
|
|
12
|
+
(error)="onImageError(conversation)">
|
|
13
|
+
<div *ngIf="isImageLoaded(conversation)" #avatarImage class="c21-avatar-image" [style.background-image]="'url(' + conversation.image + ')'"></div>
|
|
9
14
|
</div>
|
|
10
15
|
</div>
|
|
11
16
|
<div class="c21-right-conv">
|
|
@@ -35,6 +35,7 @@ export class ListConversationsComponent implements OnInit {
|
|
|
35
35
|
arrayDiffer: any;
|
|
36
36
|
|
|
37
37
|
uidConvSelected: string;
|
|
38
|
+
imageLoadedMap: Map<string, boolean> = new Map<string, boolean>();
|
|
38
39
|
constructor(private iterableDiffers: IterableDiffers) {
|
|
39
40
|
this.iterableDifferListConv = this.iterableDiffers.find([]).create(null);
|
|
40
41
|
|
|
@@ -67,5 +68,33 @@ export class ListConversationsComponent implements OnInit {
|
|
|
67
68
|
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Verifica se l'immagine esiste e si carica correttamente
|
|
73
|
+
*/
|
|
74
|
+
isImageLoaded(conversation: ConversationModel): boolean {
|
|
75
|
+
if (!conversation?.image) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
return this.imageLoadedMap.get(conversation.uid) === true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Gestisce il caricamento riuscito dell'immagine
|
|
83
|
+
*/
|
|
84
|
+
onImageLoad(conversation: ConversationModel) {
|
|
85
|
+
if (conversation?.uid && conversation?.image) {
|
|
86
|
+
this.imageLoadedMap.set(conversation.uid, true);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Gestisce l'errore di caricamento dell'immagine
|
|
92
|
+
*/
|
|
93
|
+
onImageError(conversation: ConversationModel) {
|
|
94
|
+
if (conversation?.uid) {
|
|
95
|
+
this.imageLoadedMap.set(conversation.uid, false);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
70
99
|
|
|
71
100
|
}
|
|
@@ -1963,7 +1963,7 @@ export class GlobalSettingsService {
|
|
|
1963
1963
|
this.logger.debug('[GLOBAL-SET] setDepartmentFromExternal > END departmentID ::::' + this.globals.departmentID + isValidID);
|
|
1964
1964
|
}
|
|
1965
1965
|
//remove default department from list
|
|
1966
|
-
this.globals.departments = this.globals.departments
|
|
1966
|
+
this.globals.departments = this.globals.departments?.filter(obj => obj['default'] !== true) ?? []
|
|
1967
1967
|
if(this.globals.departments && this.globals.departments.length === 1){
|
|
1968
1968
|
this.setDepartment(this.globals.departments[0])
|
|
1969
1969
|
}
|
|
@@ -27,9 +27,13 @@ export abstract class UploadService {
|
|
|
27
27
|
abstract BSStateUpload: BehaviorSubject<any>;
|
|
28
28
|
|
|
29
29
|
// functions
|
|
30
|
-
abstract initialize(): void;
|
|
30
|
+
abstract initialize(projectId?: string): void;
|
|
31
31
|
abstract upload(userId: string, upload: UploadModel): Promise<{downloadURL: string, src: string}>;
|
|
32
|
+
abstract uploadFile(userId: string, upload: UploadModel): Promise<{downloadURL: string, src: string}>;
|
|
33
|
+
abstract uploadAsset(userId: string, upload: UploadModel, expiration?: number): Promise<{downloadURL: string, src: string}>;
|
|
32
34
|
abstract uploadProfile(userId: string, upload: UploadModel): Promise<any>;
|
|
33
35
|
abstract delete(userId: string, path: string): Promise<any>;
|
|
36
|
+
abstract deleteFile(userId: string, path: string): Promise<any>;
|
|
37
|
+
abstract deleteAsset(userId: string, path: string): Promise<any>
|
|
34
38
|
abstract deleteProfile(userId: string, path: string): Promise<any>
|
|
35
39
|
}
|
|
@@ -31,7 +31,7 @@ export class FirebaseUploadService extends UploadService {
|
|
|
31
31
|
super();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
public async initialize() {
|
|
34
|
+
public async initialize(projectId: string) {
|
|
35
35
|
this.logger.debug('[FIREBASEUploadSERVICE] initialize');
|
|
36
36
|
|
|
37
37
|
const { default: firebase} = await import("firebase/app");
|
|
@@ -96,14 +96,115 @@ export class FirebaseUploadService extends UploadService {
|
|
|
96
96
|
}, async function complete() {
|
|
97
97
|
// Handle successful uploads on complete
|
|
98
98
|
that.logger.debug('[FIREBASEUploadSERVICE] Upload is complete', upload);
|
|
99
|
-
|
|
99
|
+
|
|
100
|
+
const downloadURL = await uploadTask.snapshot.ref.getDownloadURL();
|
|
101
|
+
resolve({downloadURL: downloadURL, src: downloadURL})
|
|
102
|
+
// that.BSStateUpload.next({upload: upload});
|
|
103
|
+
|
|
104
|
+
});
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public uploadFile(userId: string, upload: UploadModel): Promise<{downloadURL: string, src: any}> {
|
|
110
|
+
const that = this;
|
|
111
|
+
const uid = this.createGuid();
|
|
112
|
+
const urlImagesNodeFirebase = '/public/images/' + userId + '/' + uid + '/' + upload.file.name;
|
|
113
|
+
this.logger.debug('[FIREBASEUploadSERVICE] pushUpload ', urlImagesNodeFirebase, upload.file);
|
|
114
|
+
|
|
115
|
+
// Create a root reference
|
|
116
|
+
const storageRef = this.firebase.storage().ref();
|
|
117
|
+
this.logger.debug('[FIREBASEUploadSERVICE] storageRef', storageRef);
|
|
118
|
+
|
|
119
|
+
// Create a reference to 'mountains.jpg'
|
|
120
|
+
const mountainsRef = storageRef.child(urlImagesNodeFirebase);
|
|
121
|
+
this.logger.debug('[FIREBASEUploadSERVICE] mountainsRef ', mountainsRef);
|
|
122
|
+
|
|
123
|
+
// const metadata = {};
|
|
124
|
+
const metadata = { name: upload.file.name, contentType: upload.file.type, contentDisposition: 'attachment; filename=' + upload.file.name };
|
|
125
|
+
|
|
126
|
+
let uploadTask = mountainsRef.put(upload.file, metadata);
|
|
127
|
+
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
uploadTask.on('state_changed', function progress(snapshot) {
|
|
130
|
+
// Observe state change events such as progress, pause, and resume
|
|
131
|
+
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
|
|
132
|
+
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
|
|
133
|
+
that.logger.debug('[FIREBASEUploadSERVICE] Upload is ' + progress + '% done');
|
|
134
|
+
|
|
135
|
+
// ----------------------------------------------------------------------------------------------------------------------------------------------
|
|
136
|
+
// BehaviorSubject publish the upload progress state - the subscriber is in ion-conversastion-detail.component.ts > listenToUploadFileProgress()
|
|
137
|
+
// ----------------------------------------------------------------------------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
that.BSStateUpload.next({ upload: progress, type: upload.file.type });
|
|
140
|
+
|
|
141
|
+
switch (snapshot.state) {
|
|
142
|
+
case that.firebase.storage.TaskState.PAUSED: // or 'paused'
|
|
143
|
+
that.logger.debug('[FIREBASEUploadSERVICE] Upload is paused');
|
|
144
|
+
|
|
145
|
+
break;
|
|
146
|
+
case that.firebase.storage.TaskState.RUNNING: // or 'running'
|
|
147
|
+
that.logger.debug('[FIREBASEUploadSERVICE] Upload is running');
|
|
148
|
+
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}, function error(error) {
|
|
152
|
+
// Handle unsuccessful uploads
|
|
153
|
+
reject(error)
|
|
154
|
+
}, async function complete() {
|
|
155
|
+
// Handle successful uploads on complete
|
|
156
|
+
that.logger.debug('[FIREBASEUploadSERVICE] Upload is complete', upload);
|
|
157
|
+
|
|
100
158
|
const downloadURL = await uploadTask.snapshot.ref.getDownloadURL();
|
|
101
|
-
resolve({downloadURL
|
|
159
|
+
resolve({downloadURL: downloadURL, src: downloadURL})
|
|
102
160
|
// that.BSStateUpload.next({upload: upload});
|
|
103
161
|
|
|
104
162
|
});
|
|
105
163
|
})
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public uploadAsset(userId: string, upload: UploadModel, expiration: number = 60): Promise<{downloadURL: string, src: any}> {
|
|
167
|
+
// expiration is ignored on Firebase, but kept for API compatibility with NativeUploadService
|
|
168
|
+
const that = this;
|
|
169
|
+
const uid = this.createGuid();
|
|
170
|
+
const urlAssetsNodeFirebase = '/public/assets/' + userId + '/' + uid + '/' + upload.file.name;
|
|
171
|
+
this.logger.debug('[FIREBASEUploadSERVICE] uploadAsset ', urlAssetsNodeFirebase, upload.file, 'expiration:', expiration);
|
|
106
172
|
|
|
173
|
+
// Create a root reference
|
|
174
|
+
const storageRef = this.firebase.storage().ref();
|
|
175
|
+
this.logger.debug('[FIREBASEUploadSERVICE] storageRef', storageRef);
|
|
176
|
+
|
|
177
|
+
const assetRef = storageRef.child(urlAssetsNodeFirebase);
|
|
178
|
+
this.logger.debug('[FIREBASEUploadSERVICE] assetRef ', assetRef);
|
|
179
|
+
|
|
180
|
+
const metadata = { name: upload.file.name, contentType: upload.file.type, contentDisposition: 'attachment; filename=' + upload.file.name };
|
|
181
|
+
|
|
182
|
+
let uploadTask = assetRef.put(upload.file, metadata);
|
|
183
|
+
|
|
184
|
+
return new Promise((resolve, reject) => {
|
|
185
|
+
uploadTask.on('state_changed', function progress(snapshot) {
|
|
186
|
+
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
|
|
187
|
+
that.logger.debug('[FIREBASEUploadSERVICE] uploadAsset is ' + progress + '% done');
|
|
188
|
+
|
|
189
|
+
that.BSStateUpload.next({ upload: progress, type: upload.file.type });
|
|
190
|
+
|
|
191
|
+
switch (snapshot.state) {
|
|
192
|
+
case that.firebase.storage.TaskState.PAUSED:
|
|
193
|
+
that.logger.debug('[FIREBASEUploadSERVICE] uploadAsset is paused');
|
|
194
|
+
break;
|
|
195
|
+
case that.firebase.storage.TaskState.RUNNING:
|
|
196
|
+
that.logger.debug('[FIREBASEUploadSERVICE] uploadAsset is running');
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}, function error(error) {
|
|
200
|
+
reject(error)
|
|
201
|
+
}, async function complete() {
|
|
202
|
+
that.logger.debug('[FIREBASEUploadSERVICE] uploadAsset is complete', upload);
|
|
203
|
+
|
|
204
|
+
const downloadURL = await uploadTask.snapshot.ref.getDownloadURL();
|
|
205
|
+
resolve({downloadURL: downloadURL, src: downloadURL})
|
|
206
|
+
});
|
|
207
|
+
})
|
|
107
208
|
}
|
|
108
209
|
|
|
109
210
|
public uploadProfile(userId: string, upload: UploadModel): Promise<any> {
|
|
@@ -154,7 +255,9 @@ export class FirebaseUploadService extends UploadService {
|
|
|
154
255
|
// Handle successful uploads on complete
|
|
155
256
|
that.logger.debug('[FIREBASEUploadSERVICE] Upload is complete', upload);
|
|
156
257
|
|
|
157
|
-
|
|
258
|
+
const downloadURL = uploadTask.snapshot.ref.getDownloadURL();
|
|
259
|
+
resolve({downloadURL : downloadURL, url: downloadURL})
|
|
260
|
+
|
|
158
261
|
// that.BSStateUpload.next({upload: upload});
|
|
159
262
|
|
|
160
263
|
});
|
|
@@ -192,6 +295,40 @@ export class FirebaseUploadService extends UploadService {
|
|
|
192
295
|
})
|
|
193
296
|
}
|
|
194
297
|
|
|
298
|
+
public async deleteFile(userId: string, path: string): Promise<any>{
|
|
299
|
+
const that = this;
|
|
300
|
+
const file_name_photo = 'photo.jpg';
|
|
301
|
+
const file_name_thumb_photo = 'thumb_photo.jpg';
|
|
302
|
+
|
|
303
|
+
that.logger.debug('[FIREBASEUploadSERVICE] delete image for USER', userId, path);
|
|
304
|
+
|
|
305
|
+
let uid = path.split(userId)[1].split('%2F')[1]; // get the UID of the image
|
|
306
|
+
let imageName = path.split(uid + '%2F')[1].split('?')[0];
|
|
307
|
+
|
|
308
|
+
// Create a root reference
|
|
309
|
+
const storageRef = this.firebase.storage().ref();
|
|
310
|
+
const ref = storageRef.child('public/images/' + userId + '/'+ uid + '/')
|
|
311
|
+
let arrayPromise = []
|
|
312
|
+
await ref.listAll().then((dir => {
|
|
313
|
+
dir.items.forEach(fileRef => arrayPromise.push(this.deleteFile(ref.fullPath, fileRef.name)));
|
|
314
|
+
})).catch(error => {
|
|
315
|
+
that.logger.error('[FIREBASEUploadSERVICE] delete: listAll error', error)
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
//AWAIT to return ALL the promise delete()
|
|
319
|
+
return new Promise((resolve, reject)=> {
|
|
320
|
+
Promise.all(arrayPromise).then(()=>{
|
|
321
|
+
resolve(true)
|
|
322
|
+
}).catch((error)=>{
|
|
323
|
+
reject(error)
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
public async deleteAsset(userId: string, path: string): Promise<any>{
|
|
329
|
+
return this.deleteProfile(userId, path);
|
|
330
|
+
}
|
|
331
|
+
|
|
195
332
|
public async deleteProfile(userId: string, path: string): Promise<any>{
|
|
196
333
|
const that = this;
|
|
197
334
|
const file_name_photo = 'photo.jpg';
|
|
@@ -219,13 +356,5 @@ export class FirebaseUploadService extends UploadService {
|
|
|
219
356
|
})
|
|
220
357
|
}
|
|
221
358
|
|
|
222
|
-
// // ------------------------------------
|
|
223
|
-
// // Delete the file photo
|
|
224
|
-
// // ------------------------------------
|
|
225
|
-
private deleteFile(pathToFile, fileName){
|
|
226
|
-
const ref = this.firebase.storage().ref(pathToFile);
|
|
227
|
-
const childRef = ref.child(fileName);
|
|
228
|
-
return childRef.delete()
|
|
229
|
-
}
|
|
230
359
|
|
|
231
360
|
}
|
|
@@ -16,7 +16,7 @@ export class NativeImageRepoService extends ImageRepoService {
|
|
|
16
16
|
* @param uid
|
|
17
17
|
*/
|
|
18
18
|
getImagePhotoUrl(uid: string): string {
|
|
19
|
-
this.baseImageURL = this.getImageBaseUrl() + '
|
|
19
|
+
this.baseImageURL = this.getImageBaseUrl() + 'files'
|
|
20
20
|
let sender_id = '';
|
|
21
21
|
if (uid.includes('bot_')) {
|
|
22
22
|
sender_id = uid.slice(4)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UploadService } from '../abstract/upload.service';
|
|
2
2
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
3
3
|
import { Injectable } from '@angular/core';
|
|
4
|
-
import { BehaviorSubject } from 'rxjs';
|
|
4
|
+
import { BehaviorSubject, first } from 'rxjs';
|
|
5
5
|
import { UploadModel } from '../../models/upload';
|
|
6
6
|
import { AppStorageService } from '../abstract/app-storage.service';
|
|
7
7
|
import { LoggerService } from '../abstract/logger.service';
|
|
@@ -14,8 +14,8 @@ export class NativeUploadService extends UploadService {
|
|
|
14
14
|
BSStateUpload: BehaviorSubject<any> = new BehaviorSubject<any>(null)
|
|
15
15
|
|
|
16
16
|
private tiledeskToken: string;
|
|
17
|
-
private URL_TILEDESK_IMAGES: string;
|
|
18
17
|
private URL_TILEDESK_FILE: string;
|
|
18
|
+
private URL_TILEDESK_UPLOAD: string;
|
|
19
19
|
private logger: LoggerService = LoggerInstance.getInstance()
|
|
20
20
|
|
|
21
21
|
constructor(
|
|
@@ -25,16 +25,18 @@ export class NativeUploadService extends UploadService {
|
|
|
25
25
|
super();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
initialize(): void {
|
|
29
|
-
this.logger.
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
initialize(projectId?: string): void {
|
|
29
|
+
this.logger.info('[NATIVE UPLOAD] initialize', this.getBaseUrl())
|
|
30
|
+
if (projectId) {
|
|
31
|
+
this.URL_TILEDESK_FILE = this.getBaseUrl() + projectId + '/files'
|
|
32
|
+
}
|
|
33
|
+
this.URL_TILEDESK_UPLOAD = this.getBaseUrl();
|
|
32
34
|
this.tiledeskToken = this.appStorage.getItem('tiledeskToken')
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
upload(userId: string, upload: UploadModel): Promise<{downloadURL: string, src: string}> {
|
|
37
|
-
this.logger.
|
|
39
|
+
this.logger.log('[NATIVE UPLOAD] - upload new image/file ... upload', upload)
|
|
38
40
|
const headers = new HttpHeaders({
|
|
39
41
|
Authorization: this.tiledeskToken,
|
|
40
42
|
//'Content-Type': 'multipart/form-data',
|
|
@@ -45,37 +47,93 @@ export class NativeUploadService extends UploadService {
|
|
|
45
47
|
|
|
46
48
|
const that = this;
|
|
47
49
|
if ((upload.file.type.startsWith('image') && (!upload.file.type.includes('svg')))) {
|
|
48
|
-
this.logger.debug('[NATIVE UPLOAD] - upload new image')
|
|
49
50
|
//USE IMAGE API
|
|
50
|
-
const url = this.
|
|
51
|
+
const url = this.URL_TILEDESK_UPLOAD + 'images/users'
|
|
51
52
|
return new Promise((resolve, reject) => {
|
|
52
|
-
that.http.post(url, formData, requestOptions).subscribe(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
that.http.post(url, formData, requestOptions).pipe(first()).subscribe({
|
|
54
|
+
next: (data) => {
|
|
55
|
+
const downloadURL = this.URL_TILEDESK_UPLOAD + 'images?path=' + encodeURIComponent(data?.['filename']);
|
|
56
|
+
resolve({downloadURL : downloadURL, src: downloadURL})
|
|
57
|
+
// that.BSStateUpload.next({upload: upload});
|
|
58
|
+
},
|
|
59
|
+
error: (error) => {
|
|
60
|
+
reject(error)
|
|
61
|
+
}
|
|
58
62
|
});
|
|
59
63
|
});
|
|
60
64
|
} else {
|
|
61
|
-
this.logger.debug('[NATIVE UPLOAD] - upload new file')
|
|
62
65
|
//USE FILE API
|
|
63
|
-
const url = this.
|
|
66
|
+
const url = this.URL_TILEDESK_UPLOAD + 'files/users'
|
|
64
67
|
return new Promise((resolve, reject) => {
|
|
65
|
-
that.http.post(url, formData, requestOptions).subscribe(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
that.http.post(url, formData, requestOptions).pipe(first()).subscribe({
|
|
69
|
+
next: (data) => {
|
|
70
|
+
const src = this.URL_TILEDESK_UPLOAD + 'files?path=' + encodeURIComponent(data['filename']);
|
|
71
|
+
const downloadURL = this.URL_TILEDESK_UPLOAD + 'files/download' + '?path=' + encodeURIComponent(data['filename']);
|
|
72
|
+
resolve({downloadURL : downloadURL, src: src})
|
|
73
|
+
// that.BSStateUpload.next({upload: upload});
|
|
74
|
+
},
|
|
75
|
+
error: (error) => {
|
|
76
|
+
this.logger.error('[NATIVE UPLOAD] - ERROR upload new file ', error)
|
|
77
|
+
reject(error)
|
|
78
|
+
}
|
|
73
79
|
});
|
|
74
80
|
});
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
}
|
|
78
84
|
|
|
85
|
+
uploadFile(userId: string, upload: UploadModel): Promise<{downloadURL: string, src: string}> {
|
|
86
|
+
this.logger.log('[NATIVE UPLOAD] - upload new image/file ... upload', upload)
|
|
87
|
+
const headers = new HttpHeaders({
|
|
88
|
+
Authorization: this.tiledeskToken,
|
|
89
|
+
//'Content-Type': 'multipart/form-data',
|
|
90
|
+
});
|
|
91
|
+
const requestOptions = { headers: headers };
|
|
92
|
+
const formData = new FormData();
|
|
93
|
+
formData.append('file', upload.file);
|
|
94
|
+
|
|
95
|
+
const that = this;
|
|
96
|
+
const url = this.URL_TILEDESK_FILE + '/chat'
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
that.http.post(url, formData, requestOptions).pipe(first()).subscribe({
|
|
99
|
+
next: (data) => {
|
|
100
|
+
const src = this.URL_TILEDESK_UPLOAD + 'files?path=' + encodeURIComponent(data['filename']);
|
|
101
|
+
const downloadURL = this.URL_TILEDESK_UPLOAD + 'files/download?path=' + encodeURIComponent(data['filename']);
|
|
102
|
+
resolve({downloadURL : downloadURL, src: src})
|
|
103
|
+
},
|
|
104
|
+
error: (error) => {
|
|
105
|
+
reject(error)
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
uploadAsset(userId: string, upload: UploadModel, expiration: number = 60): Promise<{downloadURL: string, src: string}> {
|
|
112
|
+
this.logger.log('[NATIVE UPLOAD] - upload new asset ... upload', upload, 'expiration:', expiration)
|
|
113
|
+
const headers = new HttpHeaders({
|
|
114
|
+
Authorization: this.tiledeskToken,
|
|
115
|
+
});
|
|
116
|
+
const requestOptions = { headers: headers };
|
|
117
|
+
const formData = new FormData();
|
|
118
|
+
formData.append('file', upload.file);
|
|
119
|
+
|
|
120
|
+
const that = this;
|
|
121
|
+
const queryString = expiration !== undefined ? `?expiration=${encodeURIComponent(String(expiration))}` : ''
|
|
122
|
+
const url = this.URL_TILEDESK_FILE + `/assets${queryString}`
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
that.http.post(url, formData, requestOptions).pipe(first()).subscribe({
|
|
125
|
+
next: (data) => {
|
|
126
|
+
const src = this.URL_TILEDESK_UPLOAD + 'files?path=' + data['filename'];
|
|
127
|
+
const downloadURL = this.URL_TILEDESK_UPLOAD + 'files/download?path=' + encodeURIComponent(data['filename']);
|
|
128
|
+
resolve({downloadURL : downloadURL, src: src})
|
|
129
|
+
},
|
|
130
|
+
error: (error) => {
|
|
131
|
+
reject(error)
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
79
137
|
uploadProfile(userId: string, upload: UploadModel): Promise<any> {
|
|
80
138
|
this.logger.log('[NATIVE UPLOAD] - upload new photo profile ... upload', upload)
|
|
81
139
|
const headers = new HttpHeaders({
|
|
@@ -88,14 +146,18 @@ export class NativeUploadService extends UploadService {
|
|
|
88
146
|
|
|
89
147
|
// USE IMAGE API
|
|
90
148
|
const that = this;
|
|
91
|
-
const
|
|
149
|
+
const queryString = userId?.startsWith('bot_') ? `?bot_id=${encodeURIComponent(userId.substring('bot_'.length))}` : ''
|
|
150
|
+
const url = this.URL_TILEDESK_FILE + `/users/photo${queryString}`
|
|
92
151
|
return new Promise((resolve, reject) => {
|
|
93
|
-
that.http.put(url, formData, requestOptions).subscribe(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
152
|
+
that.http.put(url, formData, requestOptions).pipe(first()).subscribe({
|
|
153
|
+
next: (data) => {
|
|
154
|
+
const downloadURL = this.getBaseUrl() + 'files?path=' + data['thumbnail'];
|
|
155
|
+
resolve(downloadURL)
|
|
156
|
+
// that.BSStateUpload.next({upload: upload});
|
|
157
|
+
},
|
|
158
|
+
error: (error) => {
|
|
159
|
+
reject(error)
|
|
160
|
+
}
|
|
99
161
|
});
|
|
100
162
|
});
|
|
101
163
|
}
|
|
@@ -110,18 +172,50 @@ export class NativeUploadService extends UploadService {
|
|
|
110
172
|
|
|
111
173
|
//USE IMAGE API
|
|
112
174
|
const that = this;
|
|
113
|
-
const url = this.
|
|
175
|
+
const url = this.URL_TILEDESK_UPLOAD + 'images/users' + '?path=' + path.split('path=')[1]
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
that.http.delete(url, requestOptions).pipe(first()).subscribe({
|
|
178
|
+
next: (data) => {
|
|
179
|
+
// const downloadURL = this.URL_TILEDESK_IMAGES + '?path=' + data['filename'];
|
|
180
|
+
resolve(true)
|
|
181
|
+
// that.BSStateUpload.next({upload: upload});
|
|
182
|
+
},
|
|
183
|
+
error: (error) => {
|
|
184
|
+
reject(error)
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
deleteFile(userId: string, path: string): Promise<any>{
|
|
191
|
+
this.logger.log('[NATIVE UPLOAD] - delete image ... upload', userId)
|
|
192
|
+
const headers = new HttpHeaders({
|
|
193
|
+
Authorization: this.tiledeskToken,
|
|
194
|
+
//'Content-Type': 'multipart/form-data',
|
|
195
|
+
});
|
|
196
|
+
const requestOptions = { headers: headers };
|
|
197
|
+
|
|
198
|
+
//USE IMAGE API
|
|
199
|
+
const that = this;
|
|
200
|
+
const url = this.URL_TILEDESK_FILE + '?path=' + path.split('path=')[1]
|
|
114
201
|
return new Promise((resolve, reject) => {
|
|
115
|
-
that.http.delete(url, requestOptions).subscribe(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
202
|
+
that.http.delete(url, requestOptions).pipe(first()).subscribe({
|
|
203
|
+
next: (data) => {
|
|
204
|
+
// const downloadURL = this.URL_TILEDESK_IMAGES + '?path=' + data['filename'];
|
|
205
|
+
resolve(true)
|
|
206
|
+
// that.BSStateUpload.next({upload: upload});
|
|
207
|
+
},
|
|
208
|
+
error: (error) => {
|
|
209
|
+
reject(error)
|
|
210
|
+
}
|
|
121
211
|
});
|
|
122
212
|
});
|
|
123
213
|
}
|
|
124
214
|
|
|
215
|
+
deleteAsset(userId: string, path: string): Promise<any>{
|
|
216
|
+
return this.deleteFile(userId, path);
|
|
217
|
+
}
|
|
218
|
+
|
|
125
219
|
deleteProfile(userId: string, path: string): Promise<any>{
|
|
126
220
|
this.logger.log('[NATIVE UPLOAD] - delete image ... upload', userId)
|
|
127
221
|
const headers = new HttpHeaders({
|
|
@@ -132,14 +226,17 @@ export class NativeUploadService extends UploadService {
|
|
|
132
226
|
|
|
133
227
|
//USE IMAGE API
|
|
134
228
|
const that = this;
|
|
135
|
-
const url = this.
|
|
229
|
+
const url = this.URL_TILEDESK_FILE + '?path=' + "uploads/users/"+ userId + "/images/photo.jpg"
|
|
136
230
|
return new Promise((resolve, reject) => {
|
|
137
|
-
that.http.delete(url, requestOptions).subscribe(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
231
|
+
that.http.delete(url, requestOptions).pipe(first()).subscribe({
|
|
232
|
+
next: (data) => {
|
|
233
|
+
// const downloadURL = this.URL_TILEDESK_IMAGES + '?path=' + data['filename'];
|
|
234
|
+
resolve(true)
|
|
235
|
+
// that.BSStateUpload.next({upload: upload});
|
|
236
|
+
},
|
|
237
|
+
error: (error) => {
|
|
238
|
+
reject(error)
|
|
239
|
+
}
|
|
143
240
|
});
|
|
144
241
|
});
|
|
145
242
|
}
|
package/src/launch.js
CHANGED
|
@@ -217,13 +217,68 @@ function loadIframe(tiledeskScriptBaseLocation) {
|
|
|
217
217
|
|
|
218
218
|
iDiv.appendChild(ifrm);
|
|
219
219
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
// Funzione helper per caricare iframe con fallback per compatibilità CSP (Wix, etc.)
|
|
221
|
+
// Usa Blob URL come metodo principale (più compatibile con CSP) con fallback a srcdoc e document.write
|
|
222
|
+
function loadIframeContent(iframe, htmlContent, baseLocation) {
|
|
223
|
+
var isLocalhost = baseLocation.includes('localhost');
|
|
224
|
+
var blobUrl = null;
|
|
225
|
+
|
|
226
|
+
// Metodo 1: Blob URL (più compatibile con CSP di Wix e altre piattaforme)
|
|
227
|
+
// Usa Blob URL come metodo principale perché è meno spesso bloccato da CSP rispetto a srcdoc
|
|
228
|
+
if (typeof Blob !== 'undefined' && typeof URL !== 'undefined' && URL.createObjectURL) {
|
|
229
|
+
try {
|
|
230
|
+
var blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
|
|
231
|
+
blobUrl = URL.createObjectURL(blob);
|
|
232
|
+
iframe.src = blobUrl;
|
|
233
|
+
|
|
234
|
+
// Cleanup del blob URL dopo il caricamento per liberare memoria
|
|
235
|
+
var originalOnload = iframe.onload;
|
|
236
|
+
iframe.onload = function() {
|
|
237
|
+
// Revoca il blob URL dopo un delay per assicurarsi che tutto sia caricato
|
|
238
|
+
setTimeout(function() {
|
|
239
|
+
if (blobUrl) {
|
|
240
|
+
try {
|
|
241
|
+
URL.revokeObjectURL(blobUrl);
|
|
242
|
+
blobUrl = null;
|
|
243
|
+
} catch(e) {
|
|
244
|
+
console.warn('Error revoking blob URL:', e);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}, 1000);
|
|
248
|
+
if (originalOnload) originalOnload.call(this);
|
|
249
|
+
};
|
|
250
|
+
return; // Blob URL impostato con successo
|
|
251
|
+
} catch(e) {
|
|
252
|
+
console.warn('Blob URL not available, trying srcdoc:', e);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Metodo 2: srcdoc (fallback se Blob URL non disponibile)
|
|
257
|
+
// Skip per localhost (usa document.write per compatibilità sviluppo)
|
|
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)) {
|
|
269
|
+
try {
|
|
270
|
+
iframe.contentWindow.document.open();
|
|
271
|
+
iframe.contentWindow.document.write(htmlContent);
|
|
272
|
+
iframe.contentWindow.document.close();
|
|
273
|
+
return; // document.write completato
|
|
274
|
+
} catch(e) {
|
|
275
|
+
console.error('All iframe loading methods failed:', e);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
226
278
|
}
|
|
279
|
+
|
|
280
|
+
// Carica il contenuto dell'iframe con fallback automatico
|
|
281
|
+
loadIframeContent(ifrm, srcTileDesk, tiledeskScriptBaseLocation);
|
|
227
282
|
|
|
228
283
|
|
|
229
284
|
}
|
package/src/launch_template.js
CHANGED
|
@@ -218,13 +218,68 @@ function loadIframe(tiledeskScriptBaseLocation) {
|
|
|
218
218
|
|
|
219
219
|
iDiv.appendChild(ifrm);
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
221
|
+
// Funzione helper per caricare iframe con fallback per compatibilità CSP (Wix, etc.)
|
|
222
|
+
// Usa Blob URL come metodo principale (più compatibile con CSP) con fallback a srcdoc e document.write
|
|
223
|
+
function loadIframeContent(iframe, htmlContent, baseLocation) {
|
|
224
|
+
var isLocalhost = baseLocation.includes('localhost');
|
|
225
|
+
var blobUrl = null;
|
|
226
|
+
|
|
227
|
+
// Metodo 1: Blob URL (più compatibile con CSP di Wix e altre piattaforme)
|
|
228
|
+
// Usa Blob URL come metodo principale perché è meno spesso bloccato da CSP rispetto a srcdoc
|
|
229
|
+
if (typeof Blob !== 'undefined' && typeof URL !== 'undefined' && URL.createObjectURL) {
|
|
230
|
+
try {
|
|
231
|
+
var blob = new Blob([htmlContent], { type: 'text/html;charset=utf-8' });
|
|
232
|
+
blobUrl = URL.createObjectURL(blob);
|
|
233
|
+
iframe.src = blobUrl;
|
|
234
|
+
|
|
235
|
+
// Cleanup del blob URL dopo il caricamento per liberare memoria
|
|
236
|
+
var originalOnload = iframe.onload;
|
|
237
|
+
iframe.onload = function() {
|
|
238
|
+
// Revoca il blob URL dopo un delay per assicurarsi che tutto sia caricato
|
|
239
|
+
setTimeout(function() {
|
|
240
|
+
if (blobUrl) {
|
|
241
|
+
try {
|
|
242
|
+
URL.revokeObjectURL(blobUrl);
|
|
243
|
+
blobUrl = null;
|
|
244
|
+
} catch(e) {
|
|
245
|
+
console.warn('Error revoking blob URL:', e);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}, 1000);
|
|
249
|
+
if (originalOnload) originalOnload.call(this);
|
|
250
|
+
};
|
|
251
|
+
return; // Blob URL impostato con successo
|
|
252
|
+
} catch(e) {
|
|
253
|
+
console.warn('Blob URL not available, trying srcdoc:', e);
|
|
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);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Metodo 3: document.write (fallback finale, funziona su localhost e browser vecchi)
|
|
269
|
+
if (isLocalhost || (iframe.contentWindow && iframe.contentWindow.document)) {
|
|
270
|
+
try {
|
|
271
|
+
iframe.contentWindow.document.open();
|
|
272
|
+
iframe.contentWindow.document.write(htmlContent);
|
|
273
|
+
iframe.contentWindow.document.close();
|
|
274
|
+
return; // document.write completato
|
|
275
|
+
} catch(e) {
|
|
276
|
+
console.error('All iframe loading methods failed:', e);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
227
279
|
}
|
|
280
|
+
|
|
281
|
+
// Carica il contenuto dell'iframe con fallback automatico
|
|
282
|
+
loadIframeContent(ifrm, srcTileDesk, tiledeskScriptBaseLocation);
|
|
228
283
|
|
|
229
284
|
|
|
230
285
|
}
|