@chat21/chat21-web-widget 5.1.12 → 5.1.14
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 +11 -0
- package/package.json +1 -1
- package/src/app/app.component.html +3 -3
- package/src/app/app.component.scss +1 -1
- package/src/app/app.component.ts +28 -8
- package/src/app/app.module.ts +18 -9
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +1 -1
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +40 -21
- package/src/app/component/{network-offline/network-offline.component.html → error-alert/error-alert.component.html} +4 -2
- package/src/app/component/{network-offline/network-offline.component.scss → error-alert/error-alert.component.scss} +3 -1
- package/src/app/component/{network-offline/network-offline.component.spec.ts → error-alert/error-alert.component.spec.ts} +8 -6
- package/src/app/component/error-alert/error-alert.component.ts +47 -0
- package/src/app/component/message/buttons/action-button/action-button.component.ts +3 -1
- package/src/app/providers/global-settings.service.ts +15 -3
- package/src/app/utils/constants.ts +3 -0
- package/src/assets/i18n/en.json +1 -1
- package/src/assets/i18n/es.json +1 -1
- package/src/assets/i18n/fr.json +1 -1
- package/src/assets/i18n/it.json +1 -1
- package/src/chat21-core/utils/utils.ts +109 -0
- package/src/app/component/network-offline/network-offline.component.ts +0 -24
package/CHANGELOG.md
CHANGED
|
@@ -6,10 +6,21 @@
|
|
|
6
6
|
### **Copyrigth**:
|
|
7
7
|
*Tiledesk SRL*
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
# 5.1.14
|
|
11
|
+
- **bug-fixed**: stopped loading local language json file
|
|
12
|
+
|
|
13
|
+
# 5.1.13
|
|
14
|
+
- **bug-fixed**: set default widget size
|
|
15
|
+
- **changed**: Updated the translations of the tooltips in the footer-component
|
|
16
|
+
- **changed**: Refactored the network-offline component and made it generic for displaying errors (now error-alert.component)
|
|
17
|
+
- **bug-fixed**: set the color of the buttons with visibility control to the font color (setButtonColors function)
|
|
18
|
+
|
|
9
19
|
# 5.1.12
|
|
10
20
|
- **bug-fixed**: check showEmojiFooterButton to enable/disable emojii
|
|
11
21
|
- **bug-fixed**: markdown is fired as an emojii and blocked by isEmojii check fn
|
|
12
22
|
|
|
23
|
+
|
|
13
24
|
# 5.1.7-rc5
|
|
14
25
|
- **bug-fixed**: bug fixed BUTTON STYLES
|
|
15
26
|
|
package/package.json
CHANGED
|
@@ -105,11 +105,11 @@
|
|
|
105
105
|
|
|
106
106
|
<!--
|
|
107
107
|
****************************************
|
|
108
|
-
|
|
108
|
+
********* MODALE ERROR ALERT ***********
|
|
109
109
|
****************************************
|
|
110
110
|
-->
|
|
111
|
-
<div *ngIf="g.isOpen &&
|
|
112
|
-
<chat-
|
|
111
|
+
<div *ngIf="g.isOpen && isShowErrorMessage" class="modal-page star-rating-widget active">
|
|
112
|
+
<chat-error-alert [errorMessage]="errorMessage" [errorKeyMessage]="errorKeyMessage" [errorParams]="errorParams"></chat-error-alert>
|
|
113
113
|
</div>
|
|
114
114
|
|
|
115
115
|
</div>
|
package/src/app/app.component.ts
CHANGED
|
@@ -106,9 +106,12 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
106
106
|
|
|
107
107
|
forceDisconnect: boolean = false;
|
|
108
108
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
// alert error message
|
|
110
|
+
isShowErrorMessage: boolean = false;
|
|
111
|
+
errorMessage: string = '';
|
|
112
|
+
errorKeyMessage: string = null;
|
|
113
|
+
errorParams: Record<string, any> = {};
|
|
114
|
+
|
|
112
115
|
private logger: LoggerService = LoggerInstance.getInstance();
|
|
113
116
|
constructor(
|
|
114
117
|
private el: ElementRef,
|
|
@@ -398,9 +401,11 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
398
401
|
this.triggerLoadParamsEvent(); // first trigger
|
|
399
402
|
//this.setAvailableAgentsStatus();
|
|
400
403
|
|
|
401
|
-
|
|
402
404
|
/** NETWORK STATUS */
|
|
403
|
-
this.listenToNetworkStatus()
|
|
405
|
+
this.listenToNetworkStatus();
|
|
406
|
+
|
|
407
|
+
/** SET WIDGET SIZE */
|
|
408
|
+
this.onWidgetSizeChange(this.g.size);
|
|
404
409
|
|
|
405
410
|
}
|
|
406
411
|
|
|
@@ -724,7 +729,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
724
729
|
// visualizzo l'iframe!!!
|
|
725
730
|
this.triggerOnViewInit();
|
|
726
731
|
this.g.setParentBodyStyleMobile(this.g.isOpen, this.g.isMobile);
|
|
727
|
-
this.g.setElementStyle(this.g.isOpen)
|
|
732
|
+
this.g.setElementStyle(this.g.isOpen);
|
|
728
733
|
// this.triggerOnAuthStateChanged(true)
|
|
729
734
|
// mostro il widget
|
|
730
735
|
// setTimeout(() => {
|
|
@@ -2215,8 +2220,23 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
|
2215
2220
|
}
|
|
2216
2221
|
|
|
2217
2222
|
private listenToNetworkStatus(){
|
|
2218
|
-
window.addEventListener('online', () =>
|
|
2219
|
-
|
|
2223
|
+
window.addEventListener('online', () => {
|
|
2224
|
+
this.isShowErrorMessage = false;
|
|
2225
|
+
this.errorMessage = null;
|
|
2226
|
+
this.errorKeyMessage = null;
|
|
2227
|
+
});
|
|
2228
|
+
window.addEventListener('offline', () => {
|
|
2229
|
+
this.isShowErrorMessage = true;
|
|
2230
|
+
this.errorMessage = null;
|
|
2231
|
+
this.errorKeyMessage = 'CONNECTION_NETWORK_ERROR';
|
|
2232
|
+
});
|
|
2233
|
+
window.addEventListener('tooltipErrorMessage', (event: CustomEvent) => {
|
|
2234
|
+
// console.log('event-------------------> tooltipErrorMessage', event);
|
|
2235
|
+
this.isShowErrorMessage = event.detail?.error;
|
|
2236
|
+
this.errorKeyMessage = event.detail?.keyMessage || null;
|
|
2237
|
+
this.errorMessage = event.detail?.message || null;
|
|
2238
|
+
this.errorParams = event.detail?.params || {};
|
|
2239
|
+
});
|
|
2220
2240
|
}
|
|
2221
2241
|
|
|
2222
2242
|
// ========= begin:: DESTROY ALL SUBSCRIPTIONS ============//
|
package/src/app/app.module.ts
CHANGED
|
@@ -60,6 +60,8 @@ import { environment } from 'src/environments/environment';
|
|
|
60
60
|
|
|
61
61
|
//THIRD-PART MODULES
|
|
62
62
|
import { TranslateModule } from '@ngx-translate/core';
|
|
63
|
+
// import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
|
64
|
+
// import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
|
63
65
|
// import { MomentModule } from 'ngx-moment';
|
|
64
66
|
import { PickerModule } from '@ctrl/ngx-emoji-mart';
|
|
65
67
|
import { LoggerModule, NGXLogger, NgxLoggerLevel } from "ngx-logger";
|
|
@@ -134,7 +136,7 @@ import { Rules } from './utils/rules';
|
|
|
134
136
|
import { ScriptService } from 'src/chat21-core/providers/scripts/script.service';
|
|
135
137
|
import { CarouselComponent } from './component/message/carousel/carousel.component';
|
|
136
138
|
import { BrandService } from './providers/brand.service';
|
|
137
|
-
import {
|
|
139
|
+
import { ErrorAlertComponent } from './component/error-alert/error-alert.component';
|
|
138
140
|
import { ConfirmCloseComponent } from './modals/confirm-close/confirm-close.component';
|
|
139
141
|
|
|
140
142
|
|
|
@@ -204,6 +206,13 @@ export function conversationHandlerFactory(chat21Service: Chat21Service, appConf
|
|
|
204
206
|
}
|
|
205
207
|
}
|
|
206
208
|
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
// ngx-translate Http loader factory
|
|
212
|
+
// export function createTranslateLoader(http: HttpClient) {
|
|
213
|
+
// return new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
|
214
|
+
// }
|
|
215
|
+
|
|
207
216
|
export function typingFactory(chat21Service: Chat21Service, appConfig: AppConfigService) {
|
|
208
217
|
const config = appConfig.getConfig()
|
|
209
218
|
if (config.chatEngine === CHAT_ENGINE_MQTT) {
|
|
@@ -300,7 +309,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
|
|
|
300
309
|
LikeUnlikeComponent,
|
|
301
310
|
TooltipDirective,
|
|
302
311
|
CarouselComponent,
|
|
303
|
-
|
|
312
|
+
ErrorAlertComponent,
|
|
304
313
|
ConfirmCloseComponent
|
|
305
314
|
],
|
|
306
315
|
imports: [BrowserModule,
|
|
@@ -308,13 +317,13 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
|
|
|
308
317
|
FormsModule,
|
|
309
318
|
ReactiveFormsModule,
|
|
310
319
|
PickerModule,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
//
|
|
320
|
+
TranslateModule.forRoot({
|
|
321
|
+
// defaultLanguage: 'en',
|
|
322
|
+
// loader: {
|
|
323
|
+
// provide: TranslateLoader,
|
|
324
|
+
// useFactory: (createTranslateLoader),
|
|
325
|
+
// deps: [HttpClient]
|
|
326
|
+
// }
|
|
318
327
|
}),
|
|
319
328
|
LoggerModule.forRoot({
|
|
320
329
|
level: NgxLoggerLevel.DEBUG,
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<span class="v-align-center">
|
|
24
24
|
<svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" width="24px" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
25
25
|
<path d="M9.9,22.7c0,0-.1,0-.2,0-1.9.3-3.7-.2-5.2-1.4-3-2.3-3.6-6.4-1.4-9.5L9.5,2.5c.4-.5,1.1-.6,1.6-.3.5.4.6,1.1.3,1.6l-6.5,9.4c-1.4,2-1,4.8.9,6.3,1,.8,2.2,1.1,3.5.9,1.3-.2,2.4-.9,3.1-1.9l6-8.7c.9-1.2.6-3-.6-3.9-.6-.5-1.4-.6-2.1-.5-.8.1-1.4.5-1.9,1.1l-5.8,8.2c-.3.5-.2,1.1.2,1.5.2.2.5.3.8.2.3,0,.6-.2.7-.4l4.7-6.2c.4-.5,1.1-.6,1.6-.2.5.4.6,1.1.2,1.6l-4.7,6.2c-.5.7-1.4,1.2-2.3,1.3-.9.1-1.8-.2-2.5-.7-1.4-1.1-1.6-3.1-.6-4.6l5.8-8.2c.8-1.1,2-1.9,3.4-2.1,1.4-.2,2.7.1,3.8,1,2.2,1.7,2.7,4.8,1.1,7.1l-6,8.7c-1.1,1.5-2.6,2.5-4.4,2.8h0Z"/>
|
|
26
|
-
<title id="altIconTitle">{{
|
|
26
|
+
<title id="altIconTitle">{{ 'MAX_ATTACHMENT' | translate: { FILE_SIZE_LIMIT: file_size_limit } }}</title>
|
|
27
27
|
</svg>
|
|
28
28
|
|
|
29
29
|
</span>
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Component, ComponentFactoryResolver, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core';
|
|
2
|
+
import { error } from 'console';
|
|
3
|
+
import { FILE_SIZE_LIMIT } from 'src/app/utils/constants';
|
|
2
4
|
import { Globals } from 'src/app/utils/globals';
|
|
3
5
|
import { checkAcceptedFile } from 'src/app/utils/utils';
|
|
4
6
|
import { MessageModel } from 'src/chat21-core/models/message';
|
|
@@ -83,9 +85,10 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
83
85
|
|
|
84
86
|
showAlertEmoji: boolean = false
|
|
85
87
|
|
|
86
|
-
file_size_limit
|
|
88
|
+
file_size_limit = FILE_SIZE_LIMIT;
|
|
87
89
|
attachmentTooltip: string = '';
|
|
88
90
|
|
|
91
|
+
|
|
89
92
|
convertColorToRGBA = convertColorToRGBA;
|
|
90
93
|
private logger: LoggerService = LoggerInstance.getInstance()
|
|
91
94
|
constructor(private chatManager: ChatManager,
|
|
@@ -93,7 +96,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
93
96
|
private uploadService: UploadService) { }
|
|
94
97
|
|
|
95
98
|
ngOnInit() {
|
|
96
|
-
this.updateAttachmentTooltip();
|
|
99
|
+
// this.updateAttachmentTooltip();
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
|
|
@@ -109,9 +112,9 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
109
112
|
this.onDrop(this.dropEvent)
|
|
110
113
|
}
|
|
111
114
|
|
|
112
|
-
if(changes['translationMap'] && changes['translationMap'].currentValue !== undefined){
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
+
// if(changes['translationMap'] && changes['translationMap'].currentValue !== undefined){
|
|
116
|
+
// this.updateAttachmentTooltip();
|
|
117
|
+
// }
|
|
115
118
|
|
|
116
119
|
}
|
|
117
120
|
|
|
@@ -120,24 +123,24 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
120
123
|
// setTimeout(() => {
|
|
121
124
|
this.showEmojiPicker = true
|
|
122
125
|
// }, 500);
|
|
123
|
-
this.updateAttachmentTooltip();
|
|
126
|
+
// this.updateAttachmentTooltip();
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
|
|
127
|
-
updateAttachmentTooltip() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
130
|
+
// updateAttachmentTooltip() {
|
|
131
|
+
// // Use setTimeout to wait for the async translation map to be populated
|
|
132
|
+
// setTimeout(() => {
|
|
133
|
+
// this.logger.log('[CONV-FOOTER] updateAttachmentTooltip - translationMap:', this.translationMap);
|
|
134
|
+
// if (this.translationMap && this.translationMap.has('MAX_ATTACHMENT')) {
|
|
135
|
+
// const template = this.translationMap.get('MAX_ATTACHMENT');
|
|
136
|
+
// this.logger.log('[CONV-FOOTER] MAX_ATTACHMENT template:', template);
|
|
137
|
+
// // this.attachmentTooltip = template.replace('{{file_size_limit}}', this.file_size_limit.toString());
|
|
138
|
+
// this.logger.log('[CONV-FOOTER] attachmentTooltip:', this.attachmentTooltip);
|
|
139
|
+
// } else {
|
|
140
|
+
// this.logger.log('[CONV-FOOTER] MAX_ATTACHMENT not found in translationMap');
|
|
141
|
+
// }
|
|
142
|
+
// }, 500);
|
|
143
|
+
// }
|
|
141
144
|
|
|
142
145
|
// ========= begin:: functions send image ======= //
|
|
143
146
|
// START LOAD IMAGE //
|
|
@@ -227,7 +230,12 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
227
230
|
const fileXLoad = this.arrayFilesLoad[0].file;
|
|
228
231
|
const uid = this.arrayFilesLoad[0].uid;
|
|
229
232
|
const type = this.arrayFilesLoad[0].type;
|
|
230
|
-
const size = this.arrayFilesLoad[0].size
|
|
233
|
+
const size = this.arrayFilesLoad[0].size;
|
|
234
|
+
if(size > this.file_size_limit * 1024 * 1024){
|
|
235
|
+
this.logger.error('[CONV-FOOTER] file size is greater than the limit: ', size, this.file_size_limit * 1024 * 1024);
|
|
236
|
+
this.showErrorNetwork();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
231
239
|
this.logger.log('[CONV-FOOTER] that.fileXLoad: ', type);
|
|
232
240
|
let metadata;
|
|
233
241
|
if (type.startsWith('image') && !type.includes('svg')) {
|
|
@@ -264,6 +272,17 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
264
272
|
}
|
|
265
273
|
|
|
266
274
|
|
|
275
|
+
private showErrorNetwork() {
|
|
276
|
+
// posso anche passare solo keyMessage, nel caso non voglio passare un messaggio custom posso passare message e params(se il messaggio possiede dei parametri),
|
|
277
|
+
//window.dispatchEvent(new CustomEvent('tooltipErrorMessage', { detail: { error: true, message: 'File size is greater than the limit {{file_size_limit}}', keyMessage: null, params: { file_size_limit: this.file_size_limit } } }));
|
|
278
|
+
window.dispatchEvent(new CustomEvent('tooltipErrorMessage', { detail: { error: true, keyMessage: 'MAX_ATTACHMENT' } }));
|
|
279
|
+
setTimeout(() => {
|
|
280
|
+
this.isFilePendingToUpload = false;
|
|
281
|
+
this.hideTextReply = false;
|
|
282
|
+
window.dispatchEvent(new CustomEvent('tooltipErrorMessage', { detail: { error: false, message: '', keyMessage: null } }));
|
|
283
|
+
}, 5000);
|
|
284
|
+
}
|
|
285
|
+
|
|
267
286
|
uploadSingle(metadata, file, messageText?: string) {
|
|
268
287
|
const that = this;
|
|
269
288
|
try {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
<path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>
|
|
4
4
|
</svg>
|
|
5
5
|
<div class="alert-content">
|
|
6
|
-
{{
|
|
6
|
+
{{ errorMessage }}
|
|
7
7
|
</div>
|
|
8
|
-
</div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ErrorAlertComponent } from './error-alert.component';
|
|
4
4
|
|
|
5
|
-
describe('
|
|
6
|
-
let component:
|
|
7
|
-
let fixture: ComponentFixture<
|
|
5
|
+
describe('ErrorAlertComponent', () => {
|
|
6
|
+
let component: ErrorAlertComponent;
|
|
7
|
+
let fixture: ComponentFixture<ErrorAlertComponent>;
|
|
8
8
|
|
|
9
9
|
beforeEach(async () => {
|
|
10
10
|
await TestBed.configureTestingModule({
|
|
11
|
-
declarations: [
|
|
11
|
+
declarations: [ ErrorAlertComponent ]
|
|
12
12
|
})
|
|
13
13
|
.compileComponents();
|
|
14
14
|
|
|
15
|
-
fixture = TestBed.createComponent(
|
|
15
|
+
fixture = TestBed.createComponent(ErrorAlertComponent);
|
|
16
16
|
component = fixture.componentInstance;
|
|
17
17
|
fixture.detectChanges();
|
|
18
18
|
});
|
|
@@ -21,3 +21,5 @@ describe('NetworkOfflineComponent', () => {
|
|
|
21
21
|
expect(component).toBeTruthy();
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Component, Input, OnInit } from '@angular/core';
|
|
2
|
+
import { CustomTranslateService } from 'src/chat21-core/providers/custom-translate.service';
|
|
3
|
+
import * as CONSTANTS from 'src/app/utils/constants';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'chat-error-alert',
|
|
7
|
+
templateUrl: './error-alert.component.html',
|
|
8
|
+
styleUrls: ['./error-alert.component.scss']
|
|
9
|
+
})
|
|
10
|
+
export class ErrorAlertComponent implements OnInit {
|
|
11
|
+
|
|
12
|
+
@Input() errorMessage: string = '';
|
|
13
|
+
@Input() errorKeyMessage: string = '';
|
|
14
|
+
@Input() errorParams: Record<string, any> = {};
|
|
15
|
+
|
|
16
|
+
translationMap: Map<string, string>;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private customTranslateService: CustomTranslateService,
|
|
20
|
+
){}
|
|
21
|
+
|
|
22
|
+
ngOnInit(): void {
|
|
23
|
+
let rawMessage: string = '';
|
|
24
|
+
// Combina costanti globali + parametri passati come input
|
|
25
|
+
const replacements = { ...CONSTANTS, ...this.errorParams };
|
|
26
|
+
if (this.errorKeyMessage) {
|
|
27
|
+
// Traduci il messaggio e sostituisci i placeholder
|
|
28
|
+
rawMessage = this.customTranslateService
|
|
29
|
+
.translateLanguage([this.errorKeyMessage])
|
|
30
|
+
.get(this.errorKeyMessage);
|
|
31
|
+
} else if (this.errorMessage) {
|
|
32
|
+
rawMessage = this.errorMessage;
|
|
33
|
+
}
|
|
34
|
+
this.errorMessage = this.interpolate(rawMessage, replacements);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Sostituisce {{placeholders}} con i valori corrispondenti */
|
|
38
|
+
private interpolate(template: string, variables: Record<string, any>): string {
|
|
39
|
+
return template.replace(/\{\{(.*?)\}\}/g, (_, key) => {
|
|
40
|
+
const trimmedKey = key.trim();
|
|
41
|
+
return variables[trimmedKey] ?? `{{${trimmedKey}}}`;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, SimpleChanges } from '@angular/core';
|
|
2
|
+
import { getColorBck } from 'src/chat21-core/utils/utils-user';
|
|
2
3
|
|
|
3
4
|
@Component({
|
|
4
5
|
selector: 'chat-action-button-attachment',
|
|
@@ -26,9 +27,10 @@ export class ActionButtonComponent implements OnInit {
|
|
|
26
27
|
//decomment if element should have same color of themeColor and fregroundColor
|
|
27
28
|
if(this.fontSize) this.elementRef.nativeElement.querySelector('.action').style.setProperty('--buttonFontSize', this.fontSize);
|
|
28
29
|
if(this.backgroundColor) this.elementRef.nativeElement.querySelector('.action').style.setProperty('--buttonBackgroundColor', this.backgroundColor);
|
|
29
|
-
if(this.textColor) this.elementRef.nativeElement.querySelector('.action').style.setProperty('--
|
|
30
|
+
if(this.textColor) this.elementRef.nativeElement.querySelector('.action').style.setProperty('--buttonTextColor', this.textColor);
|
|
30
31
|
if(this.hoverBackgroundColor) this.elementRef.nativeElement.querySelector('.action').style.setProperty('--hoverBackgroundColor', this.hoverBackgroundColor);
|
|
31
32
|
if(this.hoverTextColor) this.elementRef.nativeElement.querySelector('.action').style.setProperty('--hoverTextColor', this.hoverTextColor);
|
|
33
|
+
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
onMouseOver(event){
|
|
@@ -10,7 +10,7 @@ import { TemplateBindingParseResult } from '@angular/compiler';
|
|
|
10
10
|
import { AppStorageService } from '../../chat21-core/providers/abstract/app-storage.service';
|
|
11
11
|
import { LoggerService } from '../../chat21-core/providers/abstract/logger.service';
|
|
12
12
|
import { LoggerInstance } from '../../chat21-core/providers/logger/loggerInstance';
|
|
13
|
-
import { invertColor, isAllowedUrlInText, isJsonArray } from '../../chat21-core/utils/utils';
|
|
13
|
+
import { ensureAccessibleTextColor, invertColor, isAllowedUrlInText, isJsonArray, normalizeColorToHex } from '../../chat21-core/utils/utils';
|
|
14
14
|
import { AppConfigService } from './app-config.service';
|
|
15
15
|
|
|
16
16
|
|
|
@@ -322,10 +322,14 @@ export class GlobalSettingsService {
|
|
|
322
322
|
if (response !== null) {
|
|
323
323
|
this.setVariablesFromService(this.globals, response);
|
|
324
324
|
}
|
|
325
|
+
/** set button colors */
|
|
326
|
+
this.setButtonColors();
|
|
327
|
+
|
|
325
328
|
this.setVariableFromStorage(this.globals);
|
|
326
329
|
this.setVariablesFromSettings(this.globals);
|
|
327
330
|
this.setVariablesFromAttributeHtml(this.globals, this.el);
|
|
328
331
|
this.setVariablesFromUrlParameters(this.globals);
|
|
332
|
+
|
|
329
333
|
this.setDepartmentFromExternal();
|
|
330
334
|
/** set color with gradient from theme's colors */
|
|
331
335
|
this.globals.setColorWithGradient();
|
|
@@ -333,11 +337,19 @@ export class GlobalSettingsService {
|
|
|
333
337
|
this.setCssIframe();
|
|
334
338
|
/** set main style */
|
|
335
339
|
this.setStyle();
|
|
336
|
-
|
|
337
|
-
this.logger.debug('[GLOBAL-SET] ***** END SET PARAMETERS *****');
|
|
338
340
|
this.obsSettingsService.next(true);
|
|
339
341
|
}
|
|
340
342
|
|
|
343
|
+
private setButtonColors() {
|
|
344
|
+
this.logger.debug('[GLOBAL-SET] ***** END SET PARAMETERS *****', this.globals);
|
|
345
|
+
const bubbleSentBackground = this.globals?.bubbleSentBackground;
|
|
346
|
+
const buttonBackgroundColor = this.globals?.buttonBackgroundColor;
|
|
347
|
+
|
|
348
|
+
this.globals.buttonTextColor = ensureAccessibleTextColor(buttonBackgroundColor, bubbleSentBackground);
|
|
349
|
+
this.globals.buttonHoverTextColor = ensureAccessibleTextColor(bubbleSentBackground, buttonBackgroundColor);
|
|
350
|
+
|
|
351
|
+
}
|
|
352
|
+
|
|
341
353
|
/**
|
|
342
354
|
*
|
|
343
355
|
*/
|
package/src/assets/i18n/en.json
CHANGED
|
@@ -95,6 +95,6 @@
|
|
|
95
95
|
"CONNECTION_NETWORK_ERROR": "Our apologies. There was some trouble connecting to network",
|
|
96
96
|
"EMOJI_NOT_ELLOWED":"Emoji not allowed",
|
|
97
97
|
"DOMAIN_NOT_ALLOWED":"URL contains a non-allowed domain",
|
|
98
|
-
"MAX_ATTACHMENT": "Max allowed size {{
|
|
98
|
+
"MAX_ATTACHMENT": "Max allowed size {{FILE_SIZE_LIMIT}}Mb",
|
|
99
99
|
"EMOJI": "Emoji"
|
|
100
100
|
}
|
package/src/assets/i18n/es.json
CHANGED
|
@@ -95,6 +95,6 @@
|
|
|
95
95
|
"CONNECTION_NETWORK_ERROR": "Nuestras disculpas. Hubo algunos problemas para conectarse a la red",
|
|
96
96
|
"EMOJI_NOT_ELLOWED":"Emoji no permitido",
|
|
97
97
|
"DOMAIN_NOT_ALLOWED":"La URL contiene un dominio no permitido",
|
|
98
|
-
"MAX_ATTACHMENT": "Tamaño máximo permitido {{
|
|
98
|
+
"MAX_ATTACHMENT": "Tamaño máximo permitido {{FILE_SIZE_LIMIT}}Mb",
|
|
99
99
|
"EMOJI": "Emoji"
|
|
100
100
|
}
|
package/src/assets/i18n/fr.json
CHANGED
|
@@ -95,6 +95,6 @@
|
|
|
95
95
|
"CONNECTION_NETWORK_ERROR": "Nos excuses. Il y a eu des problèmes de connexion au réseau",
|
|
96
96
|
"EMOJI_NOT_ELLOWED":"Emoji non autorisé",
|
|
97
97
|
"DOMAIN_NOT_ALLOWED":"L'URL contient un domaine non autorisé",
|
|
98
|
-
"MAX_ATTACHMENT": "Taille maximale autorisée {{
|
|
98
|
+
"MAX_ATTACHMENT": "Taille maximale autorisée {{FILE_SIZE_LIMIT}}Mo",
|
|
99
99
|
"EMOJI": "Emoji"
|
|
100
100
|
}
|
package/src/assets/i18n/it.json
CHANGED
|
@@ -93,6 +93,6 @@
|
|
|
93
93
|
"CONNECTION_NETWORK_ERROR": "Ci scusiamo. Si sono verificati problemi di connessione di rete",
|
|
94
94
|
"EMOJI_NOT_ELLOWED":"Emoji non consentiti",
|
|
95
95
|
"DOMAIN_NOT_ALLOWED":"L'URL contiene un dominio non consentito",
|
|
96
|
-
"MAX_ATTACHMENT": "Dimensione massima consentita {{
|
|
96
|
+
"MAX_ATTACHMENT": "Dimensione massima consentita {{FILE_SIZE_LIMIT}}Mb",
|
|
97
97
|
"EMOJI": "Emoji"
|
|
98
98
|
}
|
|
@@ -389,6 +389,115 @@ export function convertColorToRGBA(color, opacity) {
|
|
|
389
389
|
return result;
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
+
export function normalizeColorToHex(input?: string): string | null {
|
|
393
|
+
if (!input) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
let color = input.trim();
|
|
397
|
+
if (color.toLowerCase().includes('gradient')) {
|
|
398
|
+
const match = color.match(/(#[0-9a-fA-F]{3,8}|rgba?\([^)]*\))/);
|
|
399
|
+
if (!match) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
color = match[0];
|
|
403
|
+
}
|
|
404
|
+
if (color.startsWith('#')) {
|
|
405
|
+
const hex = color.slice(1);
|
|
406
|
+
if (hex.length === 3) {
|
|
407
|
+
return (
|
|
408
|
+
'#' +
|
|
409
|
+
hex
|
|
410
|
+
.split('')
|
|
411
|
+
.map((char) => char + char)
|
|
412
|
+
.join('')
|
|
413
|
+
.toLowerCase()
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
if (hex.length === 4) {
|
|
417
|
+
return (
|
|
418
|
+
'#' +
|
|
419
|
+
hex
|
|
420
|
+
.slice(0, 3)
|
|
421
|
+
.split('')
|
|
422
|
+
.map((char) => char + char)
|
|
423
|
+
.join('')
|
|
424
|
+
.toLowerCase()
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
if (hex.length === 6 || hex.length === 8) {
|
|
428
|
+
return '#' + hex.slice(0, 6).toLowerCase();
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
const rgbaMatch = color.match(/rgba?\s*\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)/i);
|
|
433
|
+
if (rgbaMatch) {
|
|
434
|
+
const [, r, g, b] = rgbaMatch;
|
|
435
|
+
const toHex = (value: number) => {
|
|
436
|
+
const clamped = Math.max(0, Math.min(255, Math.round(value)));
|
|
437
|
+
return clamped.toString(16).padStart(2, '0');
|
|
438
|
+
};
|
|
439
|
+
return `#${toHex(parseFloat(r))}${toHex(parseFloat(g))}${toHex(parseFloat(b))}`;
|
|
440
|
+
}
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function ensureAccessibleTextColor(backgroundColor?: string, fontColor?: string, minContrast = 4.5): string | null {
|
|
445
|
+
const bgHex = normalizeColorToHex(backgroundColor);
|
|
446
|
+
if (!bgHex) {
|
|
447
|
+
return normalizeColorToHex(fontColor);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const fontHex = normalizeColorToHex(fontColor);
|
|
451
|
+
if (fontHex && getContrastRatio(bgHex, fontHex) >= minContrast) {
|
|
452
|
+
return fontHex;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const blackContrast = getContrastRatio(bgHex, '#000000');
|
|
456
|
+
const whiteContrast = getContrastRatio(bgHex, '#ffffff');
|
|
457
|
+
return blackContrast >= whiteContrast ? '#000000' : '#ffffff';
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function getContrastRatio(backgroundHex: string, foregroundHex: string): number {
|
|
461
|
+
const bg = hexToRgb(backgroundHex);
|
|
462
|
+
const fg = hexToRgb(foregroundHex);
|
|
463
|
+
if (!bg || !fg) {
|
|
464
|
+
return 1;
|
|
465
|
+
}
|
|
466
|
+
const l1 = relativeLuminance(bg);
|
|
467
|
+
const l2 = relativeLuminance(fg);
|
|
468
|
+
const lighter = Math.max(l1, l2);
|
|
469
|
+
const darker = Math.min(l1, l2);
|
|
470
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function relativeLuminance({ r, g, b }: { r: number; g: number; b: number }): number {
|
|
474
|
+
const srgb = [r, g, b].map((value) => {
|
|
475
|
+
const channel = value / 255;
|
|
476
|
+
return channel <= 0.03928 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);
|
|
477
|
+
});
|
|
478
|
+
return 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
482
|
+
let value = hex.replace('#', '');
|
|
483
|
+
if (value.length === 3) {
|
|
484
|
+
value = value
|
|
485
|
+
.split('')
|
|
486
|
+
.map((char) => char + char)
|
|
487
|
+
.join('');
|
|
488
|
+
}
|
|
489
|
+
if (value.length !== 6) {
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
const r = parseInt(value.slice(0, 2), 16);
|
|
493
|
+
const g = parseInt(value.slice(2, 4), 16);
|
|
494
|
+
const b = parseInt(value.slice(4, 6), 16);
|
|
495
|
+
if ([r, g, b].some((channel) => Number.isNaN(channel))) {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
return { r, g, b };
|
|
499
|
+
}
|
|
500
|
+
|
|
392
501
|
|
|
393
502
|
// export function setLanguage(windowContext, translatorService) {
|
|
394
503
|
// if (translatorService.getBrowserLanguage(windowContext)) {
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Component, OnInit } from '@angular/core';
|
|
2
|
-
import { CustomTranslateService } from 'src/chat21-core/providers/custom-translate.service';
|
|
3
|
-
|
|
4
|
-
@Component({
|
|
5
|
-
selector: 'chat-network-offline',
|
|
6
|
-
templateUrl: './network-offline.component.html',
|
|
7
|
-
styleUrls: ['./network-offline.component.scss']
|
|
8
|
-
})
|
|
9
|
-
export class NetworkOfflineComponent implements OnInit {
|
|
10
|
-
|
|
11
|
-
translationMap: Map< string, string>;
|
|
12
|
-
|
|
13
|
-
constructor(
|
|
14
|
-
private customTranslateService: CustomTranslateService
|
|
15
|
-
){}
|
|
16
|
-
|
|
17
|
-
ngOnInit(): void {
|
|
18
|
-
let keys = [
|
|
19
|
-
'CONNECTION_NETWORK_ERROR'
|
|
20
|
-
]
|
|
21
|
-
this.translationMap = this.customTranslateService.translateLanguage(keys)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
}
|