@chat21/chat21-web-widget 5.1.0-rc7 → 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 +3 -0
- package/package.json +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 +46 -4
- 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/CHANGELOG.md
CHANGED
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';
|
|
@@ -82,6 +82,9 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
82
82
|
include: [ 'recent', 'people', 'nature', 'activity', 'flags']
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
showAlertEmoji: boolean = false
|
|
86
|
+
showAlertUrl: boolean = false;
|
|
87
|
+
|
|
85
88
|
convertColorToRGBA = convertColorToRGBA;
|
|
86
89
|
private logger: LoggerService = LoggerInstance.getInstance()
|
|
87
90
|
constructor(private chatManager: ChatManager,
|
|
@@ -321,7 +324,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
321
324
|
this.onEmojiiPickerShow.emit(false)
|
|
322
325
|
this.logger.log('[CONV-FOOTER] SEND MESSAGE: ', msg, type, metadata, additional_attributes);
|
|
323
326
|
|
|
324
|
-
|
|
327
|
+
|
|
325
328
|
if (msg && msg.trim() !== '' || type === TYPE_MSG_IMAGE || type === TYPE_MSG_FILE ) {
|
|
326
329
|
|
|
327
330
|
// msg = htmlEntities(msg);
|
|
@@ -519,9 +522,30 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
519
522
|
checkForEmojii(text){
|
|
520
523
|
//remove emojii only if "emojii" exist and is set to false
|
|
521
524
|
if(this.project && this.project.settings?.allow_send_emoji === false){
|
|
522
|
-
|
|
525
|
+
this.showAlertEmoji = isEmoji(text);
|
|
526
|
+
if(this.showAlertEmoji){
|
|
527
|
+
return false
|
|
528
|
+
}
|
|
529
|
+
this.showAlertEmoji = false;
|
|
530
|
+
return true
|
|
523
531
|
}
|
|
524
|
-
|
|
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
|
+
|
|
525
549
|
}
|
|
526
550
|
|
|
527
551
|
|
|
@@ -529,6 +553,17 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
529
553
|
onTextAreaChange(){
|
|
530
554
|
this.resizeInputField()
|
|
531
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
|
+
|
|
532
567
|
}
|
|
533
568
|
|
|
534
569
|
onSendPressed(event) {
|
|
@@ -597,6 +632,13 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
|
|
|
597
632
|
|
|
598
633
|
addEmoji(event){
|
|
599
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
|
+
|
|
600
642
|
this.textInputTextArea = this.textInputTextArea.trimStart() + event.emoji.native + " "
|
|
601
643
|
this.setFocusOnId('chat21-main-message-context')
|
|
602
644
|
}
|
|
@@ -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
|
}
|