@chat21/chat21-web-widget 5.1.0-rc.4 → 5.1.0-rc10

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 CHANGED
@@ -6,13 +6,29 @@
6
6
  ### **Copyrigth**:
7
7
  *Tiledesk SRL*
8
8
 
9
- # 5.1.0-rc.4
9
+ # 5.1.0-rc10
10
+ - **bug-fixed**: minor fix allowed urls
11
+
12
+ # 5.1.0-rc9
13
+ - **bug-fixed**: minor fix allowed urls
14
+
15
+ # 5.1.0-rc8
16
+ - **added**: ability to filter on urls attached to message textarea
17
+
18
+ # 5.1.0-rc7
19
+ - **added**: ability to allows emoji after message is sent
20
+
21
+ # 5.1.0-rc6
22
+ - **removed**: hideRestartConversationOptionsMenu
23
+
24
+ # 5.1.0-rc5
25
+ # 5.1.0-rc4
10
26
  - **bug-fixed**: Dockerfile for angular 18
11
27
 
12
- # 5.1.0-rc.3
28
+ # 5.1.0-rc3
13
29
  - **added**: angular 18
14
30
 
15
- # 5.1.0-rc.2
31
+ # 5.1.0-rc2
16
32
  - **added**: angular 17
17
33
 
18
34
  # 5.0.96
package/Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
1
  ### STAGE 1: Build ###
2
2
 
3
3
  # We label our stage as ‘builder’
4
- FROM node:18.19.1-alpine3.18 as builder
4
+ FROM node:20.12.2-alpine3.19 as builder
5
5
 
6
6
  COPY package.json package-lock.json ./
7
7
 
@@ -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 npm run ng build -- --configuration="prod" --output-path=dist --base-href="./" --output-hashing=none
18
+ RUN npx ng build --configuration="prod" --output-path=dist --base-href=./ --output-hashing=none
19
19
 
20
20
 
21
21
  ### STAGE 2: Setup ###
@@ -29,7 +29,7 @@ COPY nginx.conf /etc/nginx/nginx.conf
29
29
  RUN rm -rf /usr/share/nginx/html/*
30
30
 
31
31
  ## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder
32
- COPY --from=builder /ng-app/dist /usr/share/nginx/html
32
+ COPY --from=builder /ng-app/dist/browser /usr/share/nginx/html
33
33
 
34
34
  RUN echo "Chat21 Web Widget Started!!"
35
35
 
package/deploy_beta.sh CHANGED
@@ -1,10 +1,21 @@
1
1
  # npm version prerelease --preid=beta
2
2
  version=`node -e 'console.log(require("./package.json").version)'`
3
3
  echo "version $version"
4
+ echo "____________WIDGET-V5______________"
5
+ echo "CREATING TAG ON GIT FOR version: $version"
6
+
7
+ # Get curent branch name
8
+ current_branch=$(git rev-parse --abbrev-ref HEAD)
9
+ remote_name=$(git config --get branch.$current_branch.remote)
10
+
11
+ ## Push commit to git
12
+ git add .
13
+ git commit -m "version added: ### $version"
14
+ git push "$remote_name" "$current_branch"
4
15
 
5
16
  if [ "$version" != "" ]; then
6
17
  git tag -a "$version" -m "`git log -1 --format=%s`"
7
18
  echo "Created a new tag, $version"
8
- git push remoteTiledesk --tags
19
+ git push --tags
9
20
  npm publish
10
21
  fi
package/deploy_prod.sh CHANGED
@@ -1,12 +1,21 @@
1
- # npm version patch
1
+ # npm version prerelease --preid=beta
2
2
  version=`node -e 'console.log(require("./package.json").version)'`
3
- ECHO "____________WIDGET-V5______________"
3
+ echo "version $version"
4
+ echo "____________WIDGET-V5______________"
4
5
  echo "CREATING TAG ON GIT FOR version: $version"
5
- # echo "version $version"
6
+
7
+ # Get curent branch name
8
+ current_branch=$(git rev-parse --abbrev-ref HEAD)
9
+ remote_name=$(git config --get branch.$current_branch.remote)
10
+
11
+ ## Push commit to git
12
+ git add .
13
+ git commit -m "version added: ### $version"
14
+ git push "$remote_name" "$current_branch"
6
15
 
7
16
  if [ "$version" != "" ]; then
8
17
  git tag -a "$version" -m "`git log -1 --format=%s`"
9
18
  echo "Created a new tag, $version"
10
- git push remoteTiledesk --tags
19
+ git push --tags
11
20
  npm publish
12
21
  fi
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-web-widget",
3
3
  "author": "Tiledesk SRL",
4
- "version": "5.1.0-rc.4",
4
+ "version": "5.1.0-rc10",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.tiledesk.com",
7
7
  "repository": {
@@ -15,7 +15,6 @@
15
15
  [hideHeaderCloseButton]="g?.hideHeaderCloseButton"
16
16
  [hideHeaderBackButton]="g?.singleConversation"
17
17
  [hideCloseConversationOptionMenu]="(isConversationArchived || g?.hideCloseConversationOptionMenu)"
18
- [hideRestartConversationOptionsMenu]="(isConversationArchived || (!g?.singleConversation && !hideTextAreaContent) || g?.hideRestartConversationOptionsMenu)"
19
18
  [hideHeaderConversationOptionsMenu]="g?.hideHeaderConversationOptionsMenu"
20
19
  [hideSignOutOptionMenu]="(!g?.singleConversation || !g?.showLogoutOption)"
21
20
  [hideChatDetailOptionMenu]="(!g?.isDevMode)"
@@ -113,7 +112,7 @@
113
112
  [attributes]="g?.attributes"
114
113
  [senderId]="senderId"
115
114
  [tenant]="g?.tenant"
116
- [projectid]="g?.projectid"
115
+ [project]="g?.project"
117
116
  [channelType]="g?.channelType"
118
117
  [userFullname]="g?.userFullname"
119
118
  [userEmail]="g?.userEmail"
@@ -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 = [
@@ -1,7 +1,23 @@
1
- <!-- LOGO-->
2
- <div id="hiddenFooter" *ngIf="!hideTextAreaContent && poweredBy" class="fade-in-bottom"
3
- [class.hideTextReply]="hideTextReply">
4
- <div tabindex="-1" class="c21-powered-by" [innerHTML]="poweredBy" (click)="managePoweredBy($event)"></div>
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" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
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"/>
@@ -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
- // position: absolute;
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
+ }
@@ -10,8 +10,9 @@ import { UploadService } from 'src/chat21-core/providers/abstract/upload.service
10
10
  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
- import { convertColorToRGBA } from 'src/chat21-core/utils/utils';
14
- import { isImage } from 'src/chat21-core/utils/utils-message';
13
+ import { convertColorToRGBA, isAllowedUrlInText, isEmoji } from 'src/chat21-core/utils/utils';
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() projectid: string;
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(public g: Globals,
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,13 @@ 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
+
328
+ let checkUrlDomain = this.checkForUrlDomain(this.textInputTextArea)
329
+ if(!checkUrlDomain){
330
+ return
331
+ }
332
+
324
333
  if (msg && msg.trim() !== '' || type === TYPE_MSG_IMAGE || type === TYPE_MSG_FILE ) {
325
334
 
326
335
  // msg = htmlEntities(msg);
@@ -345,7 +354,7 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
345
354
  // fine-sponziello
346
355
  // this.conversationHandlerService = this.chatManager.getConversationHandlerByConversationId(this.conversationWith)
347
356
  const senderId = this.senderId;
348
- const projectid = this.projectid;
357
+ const projectid = this.project.id;
349
358
  const channelType = this.channelType;
350
359
  const userFullname = this.userFullname;
351
360
  const userEmail = this.userEmail;
@@ -514,11 +523,49 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
514
523
  //}, false);
515
524
  }
516
525
 
526
+
527
+ checkForEmojii(text){
528
+ //remove emojii only if "emojii" exist and is set to false
529
+ if(this.project && this.project.settings?.allow_send_emoji === false){
530
+ this.showAlertEmoji = isEmoji(text);
531
+ if(this.showAlertEmoji){
532
+ return false
533
+ }
534
+ this.showAlertEmoji = false;
535
+ return true
536
+ }
537
+ this.showAlertEmoji = false;
538
+ return true
539
+ }
540
+
541
+ checkForUrlDomain(text){
542
+ if(this.project && this.project.settings?.allowed_urls === true){
543
+ this.showAlertUrl = !isAllowedUrlInText(text, this.project.settings?.allowed_urls_list);
544
+ if(this.showAlertUrl){
545
+ return false
546
+ }
547
+ this.showAlertUrl = false
548
+ return true
549
+ }
550
+ this.showAlertUrl = false
551
+ return true
552
+
553
+
554
+ }
555
+
517
556
 
518
557
 
519
558
  onTextAreaChange(){
520
559
  this.resizeInputField()
521
560
  this.setWritingMessages(this.textInputTextArea)
561
+
562
+ //reset alert to defalt values before checking again
563
+ this.showAlertEmoji= false;
564
+ this.showAlertUrl = false;
565
+ let check = this.checkForEmojii(this.textInputTextArea)
566
+ if(!check){
567
+ return;
568
+ }
522
569
  }
523
570
 
524
571
  onSendPressed(event) {
@@ -587,6 +634,12 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
587
634
 
588
635
  addEmoji(event){
589
636
  this.onEmojiiPickerShow.emit(false); //de-activate emojii picker on select
637
+
638
+ let check = this.checkForEmojii(this.textInputTextArea)
639
+ if(!check){
640
+ return;
641
+ }
642
+
590
643
  this.textInputTextArea = this.textInputTextArea.trimStart() + event.emoji.native + " "
591
644
  this.setFocusOnId('chat21-main-message-context')
592
645
  }
@@ -632,7 +685,6 @@ export class ConversationFooterComponent implements OnInit, OnChanges {
632
685
  * @param str
633
686
  */
634
687
  setWritingMessages(str) {
635
- //this.messagingService.setWritingMessages(str, this.g.channelType);
636
688
  this.typingService.setTyping(this.conversationWith, str, this.senderId, this.userFullname )
637
689
  }
638
690
 
@@ -112,7 +112,7 @@
112
112
 
113
113
 
114
114
  <!-- ICON RESTART CONVERSATION -->
115
- <div class="c21-header-button c21-right" *ngIf="!hideRestartConversationOptionsMenu" (click)="restartChat()">
115
+ <!-- <div class="c21-header-button c21-right" *ngIf="!hideRestartConversationOptionsMenu" (click)="restartChat()">
116
116
  <svg role="img" id="refresh" aria-labelledby="altIconTitle" class="icon-menu" xmlns="http://www.w3.org/2000/svg"
117
117
  width="20px" height="20px" viewBox="0 0 20 20">
118
118
  <path d="M9.6,18.2c0.6,0,1-0.7,1-1.5s-0.5-1.5-1-1.5H6c-0.6,0-1-0.7-1-1.5V7.8h1c0.4,0,0.8-0.4,1-0.9c0.2-0.6,0.1-1.2-0.2-1.6 l-2.1-3c-0.4-0.6-1.1-0.6-1.5,0l-2.1,3C0.9,5.7,0.8,6.3,1,6.9s0.5,0.9,1,0.9h1v5.9c0,2.5,1.4,4.4,3.1,4.4H9.6z"/>
@@ -120,7 +120,7 @@
120
120
  <title id="altIconTitle">{{ translationMap?.get('RESTART') }}</title>
121
121
  </svg>
122
122
  <span class="label-menu-item">{{ translationMap?.get('RESTART') }}</span>
123
- </div>
123
+ </div> -->
124
124
 
125
125
  <!-- ICON MAXIMIZE -->
126
126
  <!-- <div class="c21-header-button c21-right" *ngIf="heightStatus==='min'" (click)="maximizeMinimize('max')">
@@ -23,7 +23,6 @@ export class ConversationHeaderComponent implements OnInit, OnChanges {
23
23
  @Input() typingLocation: string;
24
24
  @Input() isTrascriptDownloadEnabled: boolean;
25
25
  @Input() hideCloseConversationOptionMenu: boolean;
26
- @Input() hideRestartConversationOptionsMenu: boolean;
27
26
  @Input() hideHeaderCloseButton: boolean;
28
27
  @Input() hideHeaderBackButton: boolean;
29
28
  @Input() hideHeaderConversationOptionsMenu: boolean;
@@ -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
- if (audio.duration === Infinity) {
144
- audio.currentTime = Number.MAX_SAFE_INTEGER;
145
- audio.ontimeupdate = () => {
146
- audio.ontimeupdate = null;
147
- audio.currentTime = 0;
148
- this.audioDuration = audio.duration;
149
- };
150
- } else {
151
- this.audioDuration = audio.duration;
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 {
@@ -93,6 +93,7 @@ export class GlobalSettingsService {
93
93
  project['trialDaysLeft'],
94
94
  project['trialExpired'],
95
95
  project['updatedAt'],
96
+ project['settings'],
96
97
  project['versions']
97
98
  );
98
99
  }
@@ -865,11 +866,6 @@ export class GlobalSettingsService {
865
866
  if (TEMP !== undefined) {
866
867
  globals.hideCloseConversationOptionMenu = (TEMP === true) ? true : false;;
867
868
  }
868
- TEMP = tiledeskSettings['hideRestartConversationOptionsMenu'];
869
- // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > hideHeaderConversationOptionsMenu:: ', TEMP]);
870
- if (TEMP !== undefined) {
871
- globals.hideRestartConversationOptionsMenu = (TEMP === true) ? true : false;;
872
- }
873
869
  TEMP = tiledeskSettings['hideHeaderConversationOptionsMenu'];
874
870
  // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > hideHeaderConversationOptionsMenu:: ', TEMP]);
875
871
  if (TEMP !== undefined) {
@@ -1240,10 +1236,6 @@ export class GlobalSettingsService {
1240
1236
  if (TEMP !== null) {
1241
1237
  this.globals.hideCloseConversationOptionMenu = TEMP;
1242
1238
  }
1243
- TEMP = el.nativeElement.getAttribute('hideRestartConversationOptionsMenu');
1244
- if (TEMP !== null) {
1245
- this.globals.hideRestartConversationOptionsMenu = TEMP;
1246
- }
1247
1239
  TEMP = el.nativeElement.getAttribute('hideSettings');
1248
1240
  if (TEMP !== null) {
1249
1241
  this.globals.hideSettings = TEMP;
@@ -1637,11 +1629,6 @@ export class GlobalSettingsService {
1637
1629
  globals.hideCloseConversationOptionMenu = stringToBoolean(TEMP);
1638
1630
  }
1639
1631
 
1640
- TEMP = getParameterByName(windowContext, 'tiledesk_hideRestartConversationOptionsMenu');
1641
- if (TEMP) {
1642
- globals.hideRestartConversationOptionsMenu = stringToBoolean(TEMP);
1643
- }
1644
-
1645
1632
  TEMP = getParameterByName(windowContext, 'tiledesk_hideSettings');
1646
1633
  if (TEMP) {
1647
1634
  globals.hideSettings = stringToBoolean(TEMP);
@@ -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);
@@ -163,7 +163,6 @@ export class Globals {
163
163
  openExternalLinkButton: boolean;
164
164
  hideHeaderConversationOptionsMenu: boolean;
165
165
  hideCloseConversationOptionMenu: boolean;
166
- hideRestartConversationOptionsMenu: boolean;
167
166
  hideSettings: boolean;
168
167
  filterByRequester: boolean;
169
168
  persistence;
@@ -337,8 +336,6 @@ export class Globals {
337
336
  /** enable to close a conversation from upper-right header menu */
338
337
  this.hideCloseConversationOptionMenu = false;
339
338
  /** enable to hide/show options menu in conversation detail header */
340
- this.hideRestartConversationOptionsMenu = false;
341
- /** enable to hide/show options menu in conversation detail header */
342
339
  this.hideSettings = false;
343
340
  /** enable to hide/show options menu in home component */
344
341
  this.filterByRequester = false;
@@ -528,7 +525,7 @@ export class Globals {
528
525
  'fullscreenMode': this.fullscreenMode,
529
526
  'filterByRequester': this.filterByRequester,
530
527
  'hideHeaderConversationOptionsMenu': this.hideHeaderConversationOptionsMenu, 'hideHeaderCloseButton': this.hideHeaderCloseButton,
531
- 'hideCloseConversationOptionMenu': this.hideCloseConversationOptionMenu, 'hideRestartConversationOptionsMenu': this.hideRestartConversationOptionsMenu,
528
+ 'hideCloseConversationOptionMenu': this.hideCloseConversationOptionMenu,
532
529
  'hideSettings': this.hideSettings,
533
530
  'isLogEnabled': this.isLogEnabled,
534
531
  'isOpen': this.isOpen, 'isShown': this.isShown,
@@ -200,15 +200,6 @@ export function avatarPlaceholder(name: string) {
200
200
  return initials;
201
201
  }
202
202
 
203
- export function isEmoji(str: string) {
204
- // tslint:disable-next-line:max-line-length
205
- const ranges = ['(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c[\ude32-\ude3a]|[\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])'];
206
- if (str.match(ranges.join('|'))) {
207
- return true;
208
- } else {
209
- return false;
210
- }
211
- }
212
203
 
213
204
  export function setColorFromString(str: string) {
214
205
  const arrayBckColor = ['#fba76f', '#80d066', '#73cdd0', '#ecd074', '#6fb1e4', '#f98bae'];
@@ -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":"URL contains a non-allowed domain"
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.body.title = brandJson['WIDGET'].META_TITLE : null;
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.body.title = brandJson['WIDGET'].META_TITLE : null;
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
  }
@@ -1263,13 +1270,6 @@
1263
1270
  window.Tiledesk('restart')
1264
1271
  }
1265
1272
 
1266
- function onClickHideRestartConversationOptionsMenu(){
1267
- let status = document.querySelector('input[name="hideRestartConversationOptionsMenu"]:checked').value
1268
- window.tiledeskSettings['hideRestartConversationOptionsMenu'] = stringToBoolean(status)
1269
- console.log('onClickHideRestartConversationOptionsMenu:', window.tiledeskSettings)
1270
- window.Tiledesk('restart')
1271
- }
1272
-
1273
1273
  function onClickAllowTranscriptDownload(){
1274
1274
  let status = document.querySelector('input[name="allowTranscriptDownload"]:checked').value
1275
1275
  window.tiledeskSettings['allowTranscriptDownload'] = stringToBoolean(status)
@@ -2277,16 +2277,6 @@
2277
2277
  <button class="btn btn-light" onclick="onClickHideCloseConversationOptionMenu()">Test this setting <i class="fa fa-magic" aria-hidden="true"></i></button>
2278
2278
  </div>
2279
2279
  </div>
2280
- <div class="row">
2281
- <div class="col-md-5 formElement"><span><em><strong>hideRestartConversationOptionsMenu</strong></em></span></div>
2282
- <div class="col-md-5">
2283
- <input class="form-check-input" type="radio" name="hideRestartConversationOptionsMenu" value="true"><label>True</label>
2284
- <input class="form-check-input" type="radio" name="hideRestartConversationOptionsMenu" value="false" checked><label >False</label>
2285
- </div>
2286
- <div class="col-md-2">
2287
- <button class="btn btn-light" onclick="onClickHideRestartConversationOptionsMenu()">Test this setting <i class="fa fa-magic" aria-hidden="true"></i></button>
2288
- </div>
2289
- </div>
2290
2280
  <div class="row">
2291
2281
  <div class="col-md-5 formElement"><span><em><strong>allowTranscriptDownload</strong></em></span></div>
2292
2282
  <div class="col-md-5">
@@ -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 </title>
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
- var brandSrc = event_data.detail.appConfigs.brandSrc? getBrandResources(event_data.detail.appConfigs.brandSrc) : null;
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.body.title = brandJson['WIDGET'].META_TITLE : null;
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;
@@ -579,4 +579,87 @@ function componentFromStr(numStr, percent) {
579
579
 
580
580
 
581
581
 
582
+ // export function isAllowedUrlInText(text: string, allowedUrls: string[]): boolean {
583
+ // // Regex per trovare URL o domini nudi nel testo
584
+ // const urlRegex = /https?:\/\/[^\s]+|www\.[^\s]+|(?:\b[\w-]+\.)+[a-z]{2,}(\/[^\s]*)?/gi;
585
+ // const foundUrls = text.match(urlRegex);
586
+
587
+ // if (!foundUrls) {
588
+ // return true; // Nessun URL => testo ammesso
589
+ // }
590
+
591
+ // // Normalizza dominio: rimuove schema, www., slash finali
592
+ // const normalize = (url: string) =>
593
+ // url
594
+ // .replace(/^https?:\/\//i, '')
595
+ // .replace(/^www\./i, '')
596
+ // .replace(/\/$/, '')
597
+ // .toLowerCase();
598
+
599
+ // // Normalizza tutti gli allowed pattern per confronto
600
+ // const normalizedAllowedPatterns = allowedUrls.map(pattern =>
601
+ // pattern
602
+ // .replace(/^https?:\/\//i, '')
603
+ // .replace(/^www\./i, '')
604
+ // .replace(/\/$/, '')
605
+ // .toLowerCase()
606
+ // .replace(/\./g, '\\.')
607
+ // .replace(/\//g, '\\/')
608
+ // .replace(/\*/g, '.*')
609
+ // );
610
+
611
+ // return foundUrls.every(rawUrl => {
612
+ // const url = normalize(rawUrl);
613
+ // return normalizedAllowedPatterns.some(pattern => {
614
+ // const regex = new RegExp(`^${pattern}$`, 'i');
615
+ // return regex.test(url);
616
+ // });
617
+ // });
618
+ // }
619
+
620
+ export function isAllowedUrlInText(text: string, allowedUrls: string[]) {
621
+ const urlsInMessage = extractUrls(text);
622
+ console.log('urlsInMessage ++++ :', urlsInMessage);
623
+
624
+ const allowedPatterns = allowedUrls.map((url) => {
625
+ try {
626
+ // Prova a estrarre il dominio da una URL completa
627
+ const hostname = new URL(url).hostname.toLowerCase();
628
+ return hostname;
629
+ } catch {
630
+ // Lascia il dominio nudo (es: "*.tiledesk.com" o "tiledesk.com")
631
+ return url.toLowerCase();
632
+ }
633
+ });
634
+
635
+ const matchesAllowed = (domain: string) => {
636
+ return allowedPatterns.some((pattern) => {
637
+ if (pattern.startsWith('*.')) {
638
+ const base = pattern.replace(/^\*\./, '');
639
+ return domain === base || domain.endsWith('.' + base);
640
+ } else {
641
+ return domain === pattern;
642
+ }
643
+ });
644
+ };
645
+
646
+ const nonWhitelistedDomains = urlsInMessage.filter((url) => {
647
+ try {
648
+ const domain = new URL(url).hostname.toLowerCase();
649
+ return !matchesAllowed(domain);
650
+ } catch (e) {
651
+ return true; // Considera URL non valido come non ammesso
652
+ }
653
+ });
654
+
655
+ return nonWhitelistedDomains.length === 0;
656
+ }
657
+
658
+ function extractUrls(text: string): string[] {
659
+ const urlRegex = /https?:\/\/[^\s]+/g;
660
+ return text.match(urlRegex) || [];
661
+ }
662
+
663
+
664
+
582
665
 
@@ -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