@chat21/chat21-web-widget 5.1.0-rc6 → 5.1.0-rc8
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 +6 -0
- package/Dockerfile +1 -1
- package/package.json +1 -1
- package/src/app/component/conversation-detail/conversation/conversation.component.html +1 -1
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +3 -1
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +22 -6
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +45 -3
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +59 -8
- package/src/app/component/message/audio/audio.component.ts +30 -16
- package/src/app/providers/global-settings.service.ts +1 -0
- package/src/app/sass/_variables.scss +6 -1
- package/src/app/utils/utils.ts +38 -0
- package/src/assets/i18n/en.json +3 -1
- package/src/assets/twp/chatbot-panel.html +8 -1
- package/src/assets/twp/index-dev.html +8 -1
- package/src/assets/twp/index.html +13 -3
- package/src/chat21-core/utils/utils-message.ts +20 -0
- package/src/models/project.ts +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
### **Copyrigth**:
|
|
7
7
|
*Tiledesk SRL*
|
|
8
8
|
|
|
9
|
+
# 5.1.0-rc.8
|
|
10
|
+
- **added**: ability to filter on urls attached to message textarea
|
|
11
|
+
|
|
12
|
+
# 5.1.0-rc.7
|
|
13
|
+
- **added**: ability to allows emoji after message is sent
|
|
14
|
+
|
|
9
15
|
# 5.1.0-rc.6
|
|
10
16
|
- **removed**: hideRestartConversationOptionsMenu
|
|
11
17
|
|
package/Dockerfile
CHANGED
|
@@ -15,7 +15,7 @@ COPY . .
|
|
|
15
15
|
|
|
16
16
|
## Build the angular app in production mode and store the artifacts in dist folder
|
|
17
17
|
|
|
18
|
-
RUN
|
|
18
|
+
RUN npx ng build --configuration="prod" --output-path=dist --base-href=./ --output-hashing=none
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
### STAGE 2: Setup ###
|
package/package.json
CHANGED
|
@@ -231,7 +231,9 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
231
231
|
'LABEL_PLACEHOLDER',
|
|
232
232
|
'GUEST_LABEL',
|
|
233
233
|
'LABEL_START_NW_CONV',
|
|
234
|
-
'CONTINUE'
|
|
234
|
+
'CONTINUE',
|
|
235
|
+
'EMOJI_NOT_ELLOWED',
|
|
236
|
+
'DOMAIN_NOT_ALLOWED'
|
|
235
237
|
];
|
|
236
238
|
|
|
237
239
|
const keysContent = [
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
[class.hideTextReply]="hideTextReply">
|
|
4
|
-
|
|
1
|
+
<div class="footerContainerAlert">
|
|
2
|
+
<!-- LOGO-->
|
|
3
|
+
<div id="hiddenFooter" *ngIf="!hideTextAreaContent && poweredBy" class="fade-in-bottom" [class.hideTextReply]="hideTextReply">
|
|
4
|
+
<div tabindex="-1" class="c21-powered-by" [innerHTML]="poweredBy" (click)="managePoweredBy($event)"></div>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<!-- ALERT EMOJI & URLS -->
|
|
8
|
+
<div id="textAlert" *ngIf="!hideTextAreaContent && showAlertEmoji" class="fade-in-bottom" [class.hideTextReply]="hideTextReply">
|
|
9
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" version="1.1" viewBox="0 0 110 135">
|
|
10
|
+
<path d="M55,25.8c-23,0-41.7,18.7-41.7,41.7s18.7,41.7,41.7,41.7,41.7-18.7,41.7-41.7-18.7-41.7-41.7-41.7ZM55,91.5c-3.4,0-6.2-2.8-6.2-6.2s2.8-6.2,6.2-6.2,6.2,2.8,6.2,6.2-2.8,6.2-6.2,6.2ZM60.3,70.1c-.2,2.8-2.5,4.9-5.3,4.9s-5.1-2.2-5.3-4.9l-1.6-22.3c-.3-4,2.9-7.4,6.9-7.4s7.2,3.4,6.9,7.4l-1.6,22.3Z"/>
|
|
11
|
+
</svg>
|
|
12
|
+
<div tabindex="-1" class="alertText">{{translationMap.get('EMOJI_NOT_ELLOWED')}}</div>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div id="textAlert" *ngIf="!hideTextAreaContent && showAlertUrl" class="fade-in-bottom" [class.hideTextReply]="hideTextReply">
|
|
16
|
+
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" version="1.1" viewBox="0 0 110 135">
|
|
17
|
+
<path d="M55,25.8c-23,0-41.7,18.7-41.7,41.7s18.7,41.7,41.7,41.7,41.7-18.7,41.7-41.7-18.7-41.7-41.7-41.7ZM55,91.5c-3.4,0-6.2-2.8-6.2-6.2s2.8-6.2,6.2-6.2,6.2,2.8,6.2,6.2-2.8,6.2-6.2,6.2ZM60.3,70.1c-.2,2.8-2.5,4.9-5.3,4.9s-5.1-2.2-5.3-4.9l-1.6-22.3c-.3-4,2.9-7.4,6.9-7.4s7.2,3.4,6.9,7.4l-1.6,22.3Z"/>
|
|
18
|
+
</svg>
|
|
19
|
+
<div tabindex="-1" class="alertText">{{translationMap.get('DOMAIN_NOT_ALLOWED')}}</div>
|
|
20
|
+
</div>
|
|
5
21
|
</div>
|
|
6
22
|
|
|
7
23
|
<!-- TEXTAREA + ICONS: conv active-->
|
|
@@ -44,7 +60,7 @@
|
|
|
44
60
|
|
|
45
61
|
|
|
46
62
|
|
|
47
|
-
<div *ngIf="!isStopRec" class="visible-text-area"
|
|
63
|
+
<div *ngIf="!isStopRec" class="visible-text-area" [class.hasError]="showAlertEmoji || showAlertUrl" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
|
|
48
64
|
<!-- isFilePendingToUpload || -->
|
|
49
65
|
<textarea
|
|
50
66
|
[attr.disabled] = "(hideTextReply)? true : null"
|
|
@@ -67,7 +83,7 @@
|
|
|
67
83
|
</div>
|
|
68
84
|
|
|
69
85
|
<!-- ICON SEND -->
|
|
70
|
-
<div *ngIf="(textInputTextArea !== '' && !isStopRec) || !showAudioRecorderFooterButton" tabindex="-1" class="chat21-textarea-button" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
|
|
86
|
+
<div *ngIf="(textInputTextArea !== '' && !isStopRec) || !showAudioRecorderFooterButton" tabindex="-1" class="chat21-textarea-button" [class.disabled]="showAlertEmoji || showAlertUrl" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
|
|
71
87
|
<span class="v-align-center">
|
|
72
88
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="24" width="24" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
|
|
73
89
|
<path d="M1.8,18.9V1.7L22,10.3L1.8,18.9z M3.9,15.6l12.6-5.4L3.9,4.9v3.7l6.4,1.6l-6.4,1.6V15.6z M3.9,15.6V4.9v7V15.6z"/>
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss
CHANGED
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
&.disabled {
|
|
40
40
|
background-color: rgb(232, 233, 237);
|
|
41
41
|
}
|
|
42
|
+
&.hasError{
|
|
43
|
+
box-shadow: 0 0 0 1px var(--chat-footer-border-color-error) inset;
|
|
44
|
+
}
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
.chat21-textarea-button {
|
|
@@ -52,6 +55,12 @@
|
|
|
52
55
|
opacity: 1;
|
|
53
56
|
cursor: pointer;
|
|
54
57
|
}
|
|
58
|
+
&.disabled{
|
|
59
|
+
opacity: 0.3 !important;
|
|
60
|
+
pointer-events: none !important;
|
|
61
|
+
cursor: not-allowed !important;
|
|
62
|
+
|
|
63
|
+
}
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
.chat21-textarea-button span svg:hover {
|
|
@@ -273,16 +282,18 @@ textarea:active{
|
|
|
273
282
|
|
|
274
283
|
}
|
|
275
284
|
|
|
285
|
+
.footerContainerAlert{
|
|
286
|
+
position: relative;
|
|
287
|
+
}
|
|
276
288
|
|
|
277
289
|
#hiddenFooter{
|
|
278
|
-
|
|
290
|
+
position: absolute;
|
|
279
291
|
bottom: 100%;
|
|
280
292
|
width: 100%;
|
|
281
293
|
height: var(--chat-footer-logo-height);
|
|
282
294
|
display: flex;
|
|
283
295
|
align-items: center;
|
|
284
296
|
justify-content: center;
|
|
285
|
-
// position: absolute;
|
|
286
297
|
// box-shadow: inset 0px -22px 16px -15px rgba(0,0,0,0.1);
|
|
287
298
|
&.hideTextReply{
|
|
288
299
|
height: var(--chat-footer-height);
|
|
@@ -344,6 +355,37 @@ textarea:active{
|
|
|
344
355
|
}
|
|
345
356
|
}
|
|
346
357
|
|
|
358
|
+
#textAlert{
|
|
359
|
+
bottom: 100%;
|
|
360
|
+
width: 100%;
|
|
361
|
+
height: var(--chat-footer-logo-height);
|
|
362
|
+
display: flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
justify-content: center;
|
|
365
|
+
background-color: var(--content-background-color);
|
|
366
|
+
position: absolute;
|
|
367
|
+
svg{
|
|
368
|
+
fill: var(--chat-footer-border-color-error);
|
|
369
|
+
}
|
|
370
|
+
// box-shadow: inset 0px -22px 16px -15px rgba(0,0,0,0.1);
|
|
371
|
+
div.alertText{
|
|
372
|
+
color: var(--dark-gray);
|
|
373
|
+
font-size: 1.2em;
|
|
374
|
+
font-weight: 500;
|
|
375
|
+
line-height: 22px;
|
|
376
|
+
font-family: Mulish, sans-serif;
|
|
377
|
+
letter-spacing: 0.24px;
|
|
378
|
+
-webkit-font-smoothing: antialiased;
|
|
379
|
+
padding: 4px 12px;
|
|
380
|
+
color: var(--chat-footer-border-color-error);
|
|
381
|
+
}
|
|
382
|
+
&.hideTextReply{
|
|
383
|
+
height: var(--chat-footer-height);
|
|
384
|
+
position: unset;
|
|
385
|
+
box-shadow: none;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
347
389
|
.fade-in-bottom {
|
|
348
390
|
-webkit-animation: fade-in-bottom 0.5s cubic-bezier(0.600, -0.280, 0.735, 0.045) 0.0s;
|
|
349
391
|
animation: fade-in-bottom 0.5s cubic-bezier(0.600, -0.280, 0.735, 0.045) 0.0s;
|
|
@@ -367,4 +409,4 @@ textarea:active{
|
|
|
367
409
|
// left: 10px;
|
|
368
410
|
border: none;
|
|
369
411
|
margin: -2px -2px 0px;
|
|
370
|
-
}
|
|
412
|
+
}
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Component, ComponentFactoryResolver, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core';
|
|
2
2
|
import { Globals } from 'src/app/utils/globals';
|
|
3
|
-
import { checkAcceptedFile } from 'src/app/utils/utils';
|
|
3
|
+
import { checkAcceptedFile, isEmoji, isAllowedUrlInText } from 'src/app/utils/utils';
|
|
4
4
|
import { MessageModel } from 'src/chat21-core/models/message';
|
|
5
5
|
import { UploadModel } from 'src/chat21-core/models/upload';
|
|
6
6
|
import { ConversationHandlerService } from 'src/chat21-core/providers/abstract/conversation-handler.service';
|
|
@@ -11,7 +11,8 @@ import { ChatManager } from 'src/chat21-core/providers/chat-manager';
|
|
|
11
11
|
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
12
12
|
import { TYPE_MSG_FILE, TYPE_MSG_IMAGE, TYPE_MSG_TEXT } from 'src/chat21-core/utils/constants';
|
|
13
13
|
import { convertColorToRGBA } from 'src/chat21-core/utils/utils';
|
|
14
|
-
import { isImage } from 'src/chat21-core/utils/utils-message';
|
|
14
|
+
import { findAndRemoveEmoji, isImage } from 'src/chat21-core/utils/utils-message';
|
|
15
|
+
import { ProjectModel } from 'src/models/project';
|
|
15
16
|
|
|
16
17
|
@Component({
|
|
17
18
|
selector: 'chat-conversation-footer',
|
|
@@ -24,7 +25,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
24
25
|
@Input() attributes: string;
|
|
25
26
|
@Input() senderId: string;
|
|
26
27
|
@Input() tenant: string;
|
|
27
|
-
@Input()
|
|
28
|
+
@Input() project: ProjectModel;
|
|
28
29
|
@Input() channelType: string;
|
|
29
30
|
@Input() userFullname: string;
|
|
30
31
|
@Input() userEmail: string;
|
|
@@ -81,10 +82,12 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
81
82
|
include: [ 'recent', 'people', 'nature', 'activity', 'flags']
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
showAlertEmoji: boolean = false
|
|
86
|
+
showAlertUrl: boolean = false;
|
|
87
|
+
|
|
84
88
|
convertColorToRGBA = convertColorToRGBA;
|
|
85
89
|
private logger: LoggerService = LoggerInstance.getInstance()
|
|
86
|
-
constructor(
|
|
87
|
-
private chatManager: ChatManager,
|
|
90
|
+
constructor(private chatManager: ChatManager,
|
|
88
91
|
private typingService: TypingService,
|
|
89
92
|
private uploadService: UploadService) { }
|
|
90
93
|
|
|
@@ -253,7 +256,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
253
256
|
// return snapshot.ref.getDownloadURL(); // Will return a promise with the download link
|
|
254
257
|
// }).then(downloadURL => {
|
|
255
258
|
// that.logger.log('[CONV-FOOTER] AppComponent::uploadSingle:: downloadURL', downloadURL]);
|
|
256
|
-
// that.g.wdLog([`Successfully uploaded file and got download link - ${downloadURL}`]);
|
|
257
259
|
|
|
258
260
|
// metadata.src = downloadURL;
|
|
259
261
|
// let type_message = TYPE_MSG_TEXT;
|
|
@@ -321,6 +323,8 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
321
323
|
(metadata) ? metadata = metadata : metadata = '';
|
|
322
324
|
this.onEmojiiPickerShow.emit(false)
|
|
323
325
|
this.logger.log('[CONV-FOOTER] SEND MESSAGE: ', msg, type, metadata, additional_attributes);
|
|
326
|
+
|
|
327
|
+
|
|
324
328
|
if (msg && msg.trim() !== '' || type === TYPE_MSG_IMAGE || type === TYPE_MSG_FILE ) {
|
|
325
329
|
|
|
326
330
|
// msg = htmlEntities(msg);
|
|
@@ -345,7 +349,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
345
349
|
// fine-sponziello
|
|
346
350
|
// this.conversationHandlerService = this.chatManager.getConversationHandlerByConversationId(this.conversationWith)
|
|
347
351
|
const senderId = this.senderId;
|
|
348
|
-
const projectid = this.
|
|
352
|
+
const projectid = this.project.id;
|
|
349
353
|
const channelType = this.channelType;
|
|
350
354
|
const userFullname = this.userFullname;
|
|
351
355
|
const userEmail = this.userEmail;
|
|
@@ -514,11 +518,52 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
514
518
|
//}, false);
|
|
515
519
|
}
|
|
516
520
|
|
|
521
|
+
|
|
522
|
+
checkForEmojii(text){
|
|
523
|
+
//remove emojii only if "emojii" exist and is set to false
|
|
524
|
+
if(this.project && this.project.settings?.allow_send_emoji === false){
|
|
525
|
+
this.showAlertEmoji = isEmoji(text);
|
|
526
|
+
if(this.showAlertEmoji){
|
|
527
|
+
return false
|
|
528
|
+
}
|
|
529
|
+
this.showAlertEmoji = false;
|
|
530
|
+
return true
|
|
531
|
+
}
|
|
532
|
+
this.showAlertEmoji = false;
|
|
533
|
+
return true
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
checkForUrlDomain(text){
|
|
537
|
+
if(this.project && this.project.settings?.allowed_urls === true){
|
|
538
|
+
this.showAlertUrl = !isAllowedUrlInText(text, this.project.settings?.allowed_urls_list);
|
|
539
|
+
if(this.showAlertUrl){
|
|
540
|
+
return false
|
|
541
|
+
}
|
|
542
|
+
this.showAlertUrl = false
|
|
543
|
+
return true
|
|
544
|
+
}
|
|
545
|
+
this.showAlertUrl = false
|
|
546
|
+
return true
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
}
|
|
550
|
+
|
|
517
551
|
|
|
518
552
|
|
|
519
553
|
onTextAreaChange(){
|
|
520
554
|
this.resizeInputField()
|
|
521
555
|
this.setWritingMessages(this.textInputTextArea)
|
|
556
|
+
|
|
557
|
+
let check = this.checkForEmojii(this.textInputTextArea)
|
|
558
|
+
if(!check){
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
let checkUrlDomain = this.checkForUrlDomain(this.textInputTextArea)
|
|
563
|
+
if(!checkUrlDomain){
|
|
564
|
+
return
|
|
565
|
+
}
|
|
566
|
+
|
|
522
567
|
}
|
|
523
568
|
|
|
524
569
|
onSendPressed(event) {
|
|
@@ -587,6 +632,13 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
587
632
|
|
|
588
633
|
addEmoji(event){
|
|
589
634
|
this.onEmojiiPickerShow.emit(false); //de-activate emojii picker on select
|
|
635
|
+
|
|
636
|
+
let check = this.checkForEmojii(this.textInputTextArea)
|
|
637
|
+
console.log('chekkkkkkk', check)
|
|
638
|
+
if(!check){
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
590
642
|
this.textInputTextArea = this.textInputTextArea.trimStart() + event.emoji.native + " "
|
|
591
643
|
this.setFocusOnId('chat21-main-message-context')
|
|
592
644
|
}
|
|
@@ -632,7 +684,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
632
684
|
* @param str
|
|
633
685
|
*/
|
|
634
686
|
setWritingMessages(str) {
|
|
635
|
-
//this.messagingService.setWritingMessages(str, this.g.channelType);
|
|
636
687
|
this.typingService.setTyping(this.conversationWith, str, this.senderId, this.userFullname )
|
|
637
688
|
}
|
|
638
689
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Component, ElementRef, AfterViewInit, Input, ViewChild } from '@angular/core';
|
|
2
2
|
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
|
3
|
+
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
4
|
+
import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
|
|
3
5
|
import { convertColorToRGBA } from 'src/chat21-core/utils/utils';
|
|
4
6
|
|
|
5
7
|
@Component({
|
|
@@ -26,13 +28,13 @@ export class AudioComponent implements AfterViewInit {
|
|
|
26
28
|
currentTime: number = 0;
|
|
27
29
|
isPlaying: boolean = false;
|
|
28
30
|
|
|
31
|
+
private logger: LoggerService = LoggerInstance.getInstance();
|
|
29
32
|
constructor(
|
|
30
33
|
private sanitizer: DomSanitizer,
|
|
31
34
|
private elementRef: ElementRef
|
|
32
35
|
) {}
|
|
33
36
|
|
|
34
37
|
ngAfterViewInit() {
|
|
35
|
-
console.log('stylesssss', this.stylesMap)
|
|
36
38
|
if (this.audioBlob) {
|
|
37
39
|
this.rawAudioUrl = URL.createObjectURL(this.audioBlob);
|
|
38
40
|
this.audioUrl = this.sanitizer.bypassSecurityTrustUrl(this.rawAudioUrl);
|
|
@@ -136,21 +138,33 @@ export class AudioComponent implements AfterViewInit {
|
|
|
136
138
|
return `${minutes}:${sec < 10 ? '0' + sec : sec}`;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
getAudioDuration() {
|
|
140
|
-
const audio = new Audio();
|
|
141
|
-
audio.src = this.rawAudioUrl!;
|
|
142
|
-
audio.addEventListener('loadedmetadata', () => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
141
|
+
async getAudioDuration() {
|
|
142
|
+
// const audio = new Audio();
|
|
143
|
+
// audio.src = this.rawAudioUrl!;
|
|
144
|
+
// audio.addEventListener('loadedmetadata', () => {
|
|
145
|
+
// if (audio.duration === Infinity) {
|
|
146
|
+
// audio.currentTime = Number.MAX_SAFE_INTEGER;
|
|
147
|
+
// audio.ontimeupdate = () => {
|
|
148
|
+
// audio.ontimeupdate = null;
|
|
149
|
+
// audio.currentTime = 0;
|
|
150
|
+
// this.audioDuration = audio.duration;
|
|
151
|
+
// };
|
|
152
|
+
// } else {
|
|
153
|
+
// this.audioDuration = audio.duration;
|
|
154
|
+
// }
|
|
155
|
+
// });
|
|
156
|
+
|
|
157
|
+
const response = await fetch(this.rawAudioUrl!);
|
|
158
|
+
this.logger.debug('getAudioDuration: response ---> ', response)
|
|
159
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
160
|
+
this.logger.debug('getAudioDuration: arrayBuffer ---> ', arrayBuffer)
|
|
161
|
+
const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
|
|
162
|
+
this.logger.debug('getAudioDuration: audioContext ---> ', audioContext)
|
|
163
|
+
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
164
|
+
this.logger.debug('getAudioDuration: audioBuffer ---> ', audioBuffer)
|
|
165
|
+
this.audioDuration = audioBuffer.duration;
|
|
166
|
+
this.logger.debug('getAudioDuration: audioDuration ---> ', this.audioDuration)
|
|
167
|
+
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
extractFirstColor(gradient: string): string | null {
|
|
@@ -31,8 +31,13 @@
|
|
|
31
31
|
--chat-footer-border-radius: 16px;
|
|
32
32
|
--chat-footer-background-color: #f6f7fb;
|
|
33
33
|
--chat-footer-color: #1a1a1a;
|
|
34
|
+
--chat-footer-border-color-error: #aa0404;
|
|
34
35
|
|
|
35
|
-
--icon-fill-color: #5f6368
|
|
36
|
+
--icon-fill-color: #5f6368;
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
--content-background-color: #fff;
|
|
40
|
+
--content-text-color: var(--black);
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
$trasp-black:rgba(0,0,0,0.8);
|
package/src/app/utils/utils.ts
CHANGED
|
@@ -210,6 +210,44 @@ export function isEmoji(str: string) {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
export function isAllowedUrlInText(text: string, allowedUrls: string[]): boolean {
|
|
214
|
+
// Regex per trovare URL o domini nudi nel testo
|
|
215
|
+
const urlRegex = /https?:\/\/[^\s]+|www\.[^\s]+|(?:\b[\w-]+\.)+[a-z]{2,}(\/[^\s]*)?/gi;
|
|
216
|
+
const foundUrls = text.match(urlRegex);
|
|
217
|
+
|
|
218
|
+
if (!foundUrls) {
|
|
219
|
+
return true; // Nessun URL => testo ammesso
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Normalizza dominio: rimuove schema, www., slash finali
|
|
223
|
+
const normalize = (url: string) =>
|
|
224
|
+
url
|
|
225
|
+
.replace(/^https?:\/\//i, '')
|
|
226
|
+
.replace(/^www\./i, '')
|
|
227
|
+
.replace(/\/$/, '')
|
|
228
|
+
.toLowerCase();
|
|
229
|
+
|
|
230
|
+
// Normalizza tutti gli allowed pattern per confronto
|
|
231
|
+
const normalizedAllowedPatterns = allowedUrls.map(pattern =>
|
|
232
|
+
pattern
|
|
233
|
+
.replace(/^https?:\/\//i, '')
|
|
234
|
+
.replace(/^www\./i, '')
|
|
235
|
+
.replace(/\/$/, '')
|
|
236
|
+
.toLowerCase()
|
|
237
|
+
.replace(/\./g, '\\.')
|
|
238
|
+
.replace(/\//g, '\\/')
|
|
239
|
+
.replace(/\*/g, '.*')
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return foundUrls.every(rawUrl => {
|
|
243
|
+
const url = normalize(rawUrl);
|
|
244
|
+
return normalizedAllowedPatterns.some(pattern => {
|
|
245
|
+
const regex = new RegExp(`^${pattern}$`, 'i');
|
|
246
|
+
return regex.test(url);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
213
251
|
export function setColorFromString(str: string) {
|
|
214
252
|
const arrayBckColor = ['#fba76f', '#80d066', '#73cdd0', '#ecd074', '#6fb1e4', '#f98bae'];
|
|
215
253
|
let num = 0;
|
package/src/assets/i18n/en.json
CHANGED
|
@@ -91,5 +91,7 @@
|
|
|
91
91
|
|
|
92
92
|
"LABEL_PREVIEW": "Preview",
|
|
93
93
|
"SWITCH_TO": "Or switch to:",
|
|
94
|
-
"CONNECTION_NETWORK_ERROR": "Our apologies. There was some trouble connecting to network"
|
|
94
|
+
"CONNECTION_NETWORK_ERROR": "Our apologies. There was some trouble connecting to network",
|
|
95
|
+
"EMOJI_NOT_ELLOWED":"Emoji not allowed",
|
|
96
|
+
"DOMAIN_NOT_ALLOWED":"Domain not allowed"
|
|
95
97
|
}
|
|
@@ -613,7 +613,7 @@
|
|
|
613
613
|
var brandJson = JSON.parse(xhr.response)
|
|
614
614
|
if(brandJson){
|
|
615
615
|
/** TITLE AND FAVICON **/
|
|
616
|
-
brandJson['WIDGET'].META_TITLE? document.
|
|
616
|
+
brandJson['WIDGET'].META_TITLE? document.title = brandJson['WIDGET'].META_TITLE : null;
|
|
617
617
|
brandJson['WIDGET'].FAVICON_URL? document.querySelector("link[rel~='icon']").setAttribute('href', brandJson['WIDGET'].FAVICON_URL) : null;
|
|
618
618
|
/** FOOTER-LOGO **/
|
|
619
619
|
brandJson['COMMON'].COMPANY_LOGO? document.getElementById('footer-logo').src = brandJson['COMMON'].COMPANY_LOGO : null;
|
|
@@ -624,6 +624,13 @@
|
|
|
624
624
|
brandJson['COMMON'].BRAND_PRIMARY_COLOR? document.body.style.setProperty('--base-company-logo', brandJson['COMMON'].BRAND_PRIMARY_COLOR): null;
|
|
625
625
|
/** IFRAME TITLE**/
|
|
626
626
|
brandJson['COMMON'].COMPANY_NAME? document.getElementById('tiledeskiframe').title = brandJson['COMMON'].COMPANY_NAME + ' Widget' : null;
|
|
627
|
+
/** META sharing ELEMENTS */
|
|
628
|
+
if(brandJson['WIDGET'].META_SHARE_INFO && Object.keys(brandJson['WIDGET'].META_SHARE_INFO).length > 0){
|
|
629
|
+
Object.keys(brandJson['WIDGET'].META_SHARE_INFO).forEach(key => {
|
|
630
|
+
var meta = document.querySelector("meta[property^='og:"+key.toLowerCase()+"']");
|
|
631
|
+
meta.setAttribute('content', brandJson['WIDGET'].META_SHARE_INFO[key])
|
|
632
|
+
})
|
|
633
|
+
}
|
|
627
634
|
}
|
|
628
635
|
}
|
|
629
636
|
|
|
@@ -719,7 +719,7 @@
|
|
|
719
719
|
var brandJson = JSON.parse(xhr.response)
|
|
720
720
|
if(brandJson){
|
|
721
721
|
/** TITLE AND FAVICON **/
|
|
722
|
-
brandJson['WIDGET'].META_TITLE? document.
|
|
722
|
+
brandJson['WIDGET'].META_TITLE? document.title = brandJson['WIDGET'].META_TITLE : null;
|
|
723
723
|
brandJson['WIDGET'].FAVICON_URL? document.querySelector("link[rel~='icon']").setAttribute('href', brandJson['WIDGET'].FAVICON_URL) : null;
|
|
724
724
|
/** FOOTER-LOGO **/
|
|
725
725
|
brandJson['COMMON'].COMPANY_LOGO? document.getElementById('footer-logo').src = brandJson['COMMON'].COMPANY_LOGO : null;
|
|
@@ -737,6 +737,13 @@
|
|
|
737
737
|
}
|
|
738
738
|
/** IFRAME TITLE**/
|
|
739
739
|
brandJson['COMMON'].COMPANY_LOGO? document.getElementById('tiledeskiframe').title = brandJson['COMMON'].COMPANY_NAME + ' Widget' : null;
|
|
740
|
+
/** META sharing ELEMENTS */
|
|
741
|
+
if(brandJson['WIDGET'].META_SHARE_INFO && Object.keys(brandJson['WIDGET'].META_SHARE_INFO).length > 0){
|
|
742
|
+
Object.keys(brandJson['WIDGET'].META_SHARE_INFO).forEach(key => {
|
|
743
|
+
var meta = document.querySelector("meta[property^='og:"+key.toLowerCase()+"']");
|
|
744
|
+
meta.setAttribute('content', brandJson['WIDGET'].META_SHARE_INFO[key])
|
|
745
|
+
})
|
|
746
|
+
}
|
|
740
747
|
}
|
|
741
748
|
}
|
|
742
749
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta charset="utf-8">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
8
8
|
|
|
9
|
-
<title>Widget test page
|
|
9
|
+
<title>Widget test page</title>
|
|
10
10
|
<style>
|
|
11
11
|
|
|
12
12
|
@-moz-keyframes blink {0%{opacity:1;} 50%{opacity:0;} 100%{opacity:1;}} /* Firefox */
|
|
@@ -295,7 +295,10 @@
|
|
|
295
295
|
window.Tiledesk('onBeforeInit', function(event_data) {
|
|
296
296
|
console.log("onBeforeInit Tiledesk FN", event_data);
|
|
297
297
|
|
|
298
|
-
|
|
298
|
+
if(event_data.detail.appConfigs.brandSrc){
|
|
299
|
+
getBrandResources(event_data.detail.appConfigs.brandSrc)
|
|
300
|
+
}
|
|
301
|
+
// var brandSrc = event_data.detail.appConfigs.brandSrc? : null;
|
|
299
302
|
|
|
300
303
|
setTimeout(() => {
|
|
301
304
|
if(event_data && event_data.detail && event_data.detail.appConfigs){
|
|
@@ -408,7 +411,7 @@
|
|
|
408
411
|
var brandJson = JSON.parse(xhr.response)
|
|
409
412
|
if(brandJson){
|
|
410
413
|
/** TITLE AND FAVICON **/
|
|
411
|
-
brandJson['WIDGET'].META_TITLE? document.
|
|
414
|
+
brandJson['WIDGET'].META_TITLE? document.title = brandJson['WIDGET'].META_TITLE : null;
|
|
412
415
|
brandJson['WIDGET'].FAVICON_URL? document.querySelector("link[rel~='icon']").setAttribute('href', brandJson['WIDGET'].FAVICON_URL) : null;
|
|
413
416
|
/** FOOTER-LOGO **/
|
|
414
417
|
brandJson['COMMON'].COMPANY_LOGO? document.getElementById('footer-logo').src = brandJson['COMMON'].COMPANY_LOGO : null;
|
|
@@ -421,6 +424,13 @@
|
|
|
421
424
|
brandJson['COMMON'].DOCS? null: document.getElementById('share').style.display = 'none'
|
|
422
425
|
/** IFRAME TITLE**/
|
|
423
426
|
brandJson['COMMON'].COMPANY_LOGO? document.getElementById('tiledeskiframe').title = brandJson['COMMON'].COMPANY_NAME + ' Widget' : null;
|
|
427
|
+
/** META sharing ELEMENTS */
|
|
428
|
+
if(brandJson['WIDGET'].META_SHARE_INFO && Object.keys(brandJson['WIDGET'].META_SHARE_INFO).length > 0){
|
|
429
|
+
Object.keys(brandJson['WIDGET'].META_SHARE_INFO).forEach(key => {
|
|
430
|
+
var meta = document.querySelector("meta[property^='og:"+key.toLowerCase()+"']");
|
|
431
|
+
meta.setAttribute('content', brandJson['WIDGET'].META_SHARE_INFO[key])
|
|
432
|
+
})
|
|
433
|
+
}
|
|
424
434
|
}
|
|
425
435
|
|
|
426
436
|
}
|
|
@@ -194,9 +194,13 @@ export function isEmojii(message: any){
|
|
|
194
194
|
// https://localcoder.org/javascript-detect-if-a-string-contains-only-unicode-emojis
|
|
195
195
|
try {
|
|
196
196
|
if(!message) return false;
|
|
197
|
+
// Removes all characters that are NOT emojis (unicode > \u1eeff)
|
|
197
198
|
const onlyEmojis = message.replace(new RegExp('[\u0000-\u1eeff]', 'g'), '')
|
|
199
|
+
// Removes spaces, line breaks, and carriage returns from the string
|
|
198
200
|
const visibleChars = message.replace(new RegExp('[\n\r\s]+|( )+', 'g'), '')
|
|
201
|
+
// Removes Chinese characters from the string
|
|
199
202
|
const chineseChars = message.replace(new RegExp('[\u4e00-\u9fa5]', 'g'), '')
|
|
203
|
+
|
|
200
204
|
if(onlyEmojis === '' || visibleChars == '' || chineseChars=='') return false
|
|
201
205
|
return (onlyEmojis.length === visibleChars.length && onlyEmojis.length <= 2)
|
|
202
206
|
} catch(e) {
|
|
@@ -204,6 +208,22 @@ export function isEmojii(message: any){
|
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
|
|
211
|
+
export function findAndRemoveEmoji(text): string{
|
|
212
|
+
|
|
213
|
+
if (!text) return text;
|
|
214
|
+
|
|
215
|
+
// Regex to find most unicode emojis
|
|
216
|
+
// Source: https://thekevinscott.com/emojis-in-javascript/
|
|
217
|
+
const emojiRegex = /([\u2700-\u27BF]|[\uE000-\uF8FF]|[\uD83C-\uDBFF][\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83D[\uDE00-\uDE4F])/g;
|
|
218
|
+
|
|
219
|
+
// Removes all found emojis
|
|
220
|
+
text = text.replace(emojiRegex, '');
|
|
221
|
+
|
|
222
|
+
// Removes any multiple spaces left by the removal of emojis
|
|
223
|
+
text = text.replace(/\s{2,}/g, ' ').trim();
|
|
224
|
+
return text
|
|
225
|
+
}
|
|
226
|
+
|
|
207
227
|
export function isJustRecived(startedAt, time) {
|
|
208
228
|
if (time > startedAt) {
|
|
209
229
|
return true;
|
package/src/models/project.ts
CHANGED
|
@@ -24,7 +24,8 @@ export class ProjectModel {
|
|
|
24
24
|
public color?: string,
|
|
25
25
|
public welcomeTitle?: string,
|
|
26
26
|
public welcomeMsg?: string,
|
|
27
|
-
public attributes?: IRules
|
|
27
|
+
public attributes?: IRules,
|
|
28
|
+
public settings?:{ [key: string]: any },
|
|
28
29
|
) { }
|
|
29
30
|
|
|
30
31
|
initialize (
|
|
@@ -43,6 +44,7 @@ export class ProjectModel {
|
|
|
43
44
|
trialDaysLeft?: number,
|
|
44
45
|
trialExpired?: boolean,
|
|
45
46
|
updatedAt?: string,
|
|
47
|
+
settings?: { [key: string]: any },
|
|
46
48
|
versions?: string,
|
|
47
49
|
) {
|
|
48
50
|
this.id = id;
|
|
@@ -60,6 +62,7 @@ export class ProjectModel {
|
|
|
60
62
|
this.trialDaysLeft = trialDaysLeft;
|
|
61
63
|
this.trialExpired = trialExpired;
|
|
62
64
|
this.updatedAt = updatedAt;
|
|
65
|
+
this.settings = settings;
|
|
63
66
|
this.versions = versions;
|
|
64
67
|
}
|
|
65
68
|
|