@chat21/chat21-web-widget 5.1.0-rc1 → 5.1.0-rc11

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +43 -2
  2. package/Dockerfile +3 -3
  3. package/angular.json +21 -17
  4. package/deploy_amazon_beta.sh +6 -11
  5. package/deploy_amazon_prod.sh +4 -11
  6. package/deploy_beta.sh +11 -0
  7. package/deploy_prod.sh +12 -3
  8. package/package.json +18 -20
  9. package/src/app/app.component.spec.ts +13 -13
  10. package/src/app/app.module.ts +11 -13
  11. package/src/app/component/conversation-detail/conversation/conversation.component.html +1 -2
  12. package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +11 -13
  13. package/src/app/component/conversation-detail/conversation/conversation.component.ts +4 -2
  14. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.scss +1 -0
  15. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +22 -6
  16. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +45 -3
  17. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +60 -8
  18. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +2 -2
  19. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +10 -11
  20. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.ts +0 -1
  21. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.ts +5 -1
  22. package/src/app/component/home-conversations/home-conversations.component.spec.ts +9 -11
  23. package/src/app/component/message/audio/audio.component.ts +30 -16
  24. package/src/app/component/star-rating-widget/star-rating-widget.component.spec.ts +10 -11
  25. package/src/app/pipe/date-ago.pipe.ts +2 -2
  26. package/src/app/providers/app-config.service.spec.ts +5 -6
  27. package/src/app/providers/global-settings.service.spec.ts +7 -8
  28. package/src/app/providers/global-settings.service.ts +1 -14
  29. package/src/app/providers/star-rating-widget.service.spec.ts +7 -8
  30. package/src/app/providers/translator.service.spec.ts +7 -9
  31. package/src/app/providers/waiting.service.spec.ts +7 -8
  32. package/src/app/sass/_variables.scss +6 -1
  33. package/src/app/sass/animations.scss +1 -0
  34. package/src/app/utils/globals.ts +1 -4
  35. package/src/app/utils/utils.ts +0 -9
  36. package/src/assets/i18n/en.json +3 -1
  37. package/src/assets/twp/chatbot-panel.html +8 -1
  38. package/src/assets/twp/index-dev.html +8 -18
  39. package/src/assets/twp/index.html +13 -3
  40. package/src/build_launch.js +9 -8
  41. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +7 -1
  42. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +8 -1
  43. package/src/chat21-core/providers/tiledesk/tiledesk-auth.service.spec.ts +8 -7
  44. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.spec.ts +10 -6
  45. package/src/chat21-core/utils/constants.ts +2 -0
  46. package/src/chat21-core/utils/utils-message.ts +32 -1
  47. package/src/chat21-core/utils/utils.ts +85 -2
  48. package/src/launch.js +11 -4
  49. package/src/launch_template.js +11 -4
  50. package/src/models/project.ts +4 -1
  51. package/tsconfig.json +1 -2
@@ -1,3 +1,4 @@
1
+ @import 'variables';
1
2
 
2
3
  /**
3
4
  * ----------------------------------------
@@ -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
  }
@@ -7,11 +7,12 @@ initReplacement()
7
7
  function initReplacement(){
8
8
  console.log('init replacement HEREEE')
9
9
  fileToBeReplaced = {
10
- 'main' : {name: 'main', extension: '.js', regex: /(?<=\/main\.)(.+?)(?=\.js|$)/},
11
- 'runtime' : {name: 'runtime', extension: '.js', regex: /(?<=\/runtime\.)(.+?)(?=\.js|$)/},
12
- 'polyfills' : {name: 'polyfills', extension: '.js', regex: /(?<=\/polyfills\.)(.+?)(?=\.js|$)/},
13
- 'vendor' : {name: 'vendor', extension: '.js', regex: /(?<=\/vendor\.)(.+?)(?=\.js|$)/},
14
- 'styles' : {name: 'styles', extension: '.css', regex: /(?<=\/styles\.)(.+?)(?=\.css|$)/},
10
+ 'main' : {name: 'main', extension: '.js', regex: /(?<=\/main-)(.+?)(?=\.js|$)/},
11
+ 'scripts' : {name: 'scripts', extension: '.js', regex: /(?<=\/scripts-)(.+?)(?=\.js|$)/},
12
+ // 'runtime' : {name: 'runtime', extension: '.js', regex: /(?<=\/runtime\.)(.+?)(?=\.js|$)/},
13
+ 'polyfills' : {name: 'polyfills', extension: '.js', regex: /(?<=\/polyfills-)(.+?)(?=\.js|$)/},
14
+ // 'vendor' : {name: 'vendor', extension: '.js', regex: /(?<=\/vendor\.)(.+?)(?=\.js|$)/},
15
+ 'styles' : {name: 'styles', extension: '.css', regex: /(?<=\/styles-)(.+?)(?=\.css|$)/},
15
16
  }
16
17
 
17
18
  Object.keys(fileToBeReplaced).forEach(key => {
@@ -46,7 +47,7 @@ function initReplacement(){
46
47
 
47
48
  function replaceFile(name, element){
48
49
  let hashCode = ''
49
- glob("./dist/"+name+"*", function (er, files) {
50
+ glob("./dist/browser/"+name+"*", function (er, files) {
50
51
  // files is an array of filenames.
51
52
  // If the `nonull` option is set, and nothing
52
53
  // was found, then files is ["**/*.js"]
@@ -56,8 +57,8 @@ function replaceFile(name, element){
56
57
  console.log('hashhh',hashCode ,name )
57
58
  replace({
58
59
  regex: '{{'+ name + '}}'+ element.extension,
59
- replacement: name + "." + hashCode + element.extension ,
60
- paths: [ './dist/launch.js' ],
60
+ replacement: name + "-" + hashCode + element.extension ,
61
+ paths: [ './dist/browser/launch.js' ],
61
62
  recursive: true,
62
63
  silent: false,
63
64
  }, (error,changedFiles)=>{
@@ -15,7 +15,7 @@ import { LoggerService } from '../abstract/logger.service';
15
15
  import { LoggerInstance } from '../logger/loggerInstance';
16
16
 
17
17
  // utils
18
- import { MSG_STATUS_RECEIVED, TYPE_DIRECT, MESSAGE_TYPE_INFO, INFO_MESSAGE_TYPE } from '../../utils/constants';
18
+ import { MSG_STATUS_RECEIVED, TYPE_DIRECT, MESSAGE_TYPE_INFO, INFO_MESSAGE_TYPE, MESSAGE_TYPE_PRIVATE } from '../../utils/constants';
19
19
  import { compareValues, searchIndexInArrayForUid, conversationMessagesRef } from '../../utils/utils';
20
20
  import { v4 as uuidv4 } from 'uuid';
21
21
  import { messageType, checkIfIsMemberJoinedGroup, hideInfoMessage, isJustRecived, isSender, infoMessageType } from '../../utils/utils-message';
@@ -230,6 +230,12 @@ export class FirebaseConversationHandler extends ConversationHandlerService {
230
230
  private addedNew(message:MessageModel){
231
231
  const msg = this.messageCommandGenerate(message);
232
232
  if(this.isValidMessage(msg)){
233
+
234
+ // do not add 'private' msg in widget array messages
235
+ let isPrivateMessage = messageType(MESSAGE_TYPE_PRIVATE, msg)
236
+ if(isPrivateMessage){
237
+ return;
238
+ }
233
239
  // msg.attributes && msg.attributes['subtype'] === 'info'
234
240
  let isInfoMessage = messageType(MESSAGE_TYPE_INFO, msg)
235
241
  if(isInfoMessage){
@@ -15,7 +15,7 @@ import { UserModel } from '../../models/user';
15
15
  import { ConversationHandlerService } from '../abstract/conversation-handler.service';
16
16
 
17
17
  // utils
18
- import { MSG_STATUS_RECEIVED, TYPE_DIRECT, MESSAGE_TYPE_INFO, INFO_MESSAGE_TYPE } from '../../utils/constants';
18
+ import { MSG_STATUS_RECEIVED, TYPE_DIRECT, MESSAGE_TYPE_INFO, INFO_MESSAGE_TYPE, MESSAGE_TYPE_PRIVATE } from '../../utils/constants';
19
19
  import { compareValues, searchIndexInArrayForUid } from '../../utils/utils';
20
20
  import { messageType, checkIfIsMemberJoinedGroup, hideInfoMessage, isJustRecived, isSender, infoMessageType } from '../../utils/utils-message';
21
21
  import { v4 as uuidv4 } from 'uuid';
@@ -262,6 +262,13 @@ export class MQTTConversationHandler extends ConversationHandlerService {
262
262
  /** */
263
263
  private addedMessage(messageSnapshot: MessageModel): Promise<boolean> {
264
264
  const msg = this.messageGenerate(messageSnapshot);
265
+
266
+ // do not add 'private' msg in widget array messages
267
+ let isPrivateMessage = messageType(MESSAGE_TYPE_PRIVATE, msg)
268
+ if(isPrivateMessage){
269
+ return;
270
+ }
271
+
265
272
  let isInfoMessage = messageType(MESSAGE_TYPE_INFO, msg)
266
273
  if(isInfoMessage){
267
274
  this.messageInfo.next(msg)
@@ -1,7 +1,7 @@
1
- import { HttpClient, HttpHeaders } from '@angular/common/http';
1
+ import { HttpClient, HttpHeaders, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
2
2
  import { TestBed } from '@angular/core/testing';
3
3
  import { AppStorageService } from '../abstract/app-storage.service';
4
- import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
4
+ import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
5
5
  import { TiledeskAuthService } from './tiledesk-auth.service';
6
6
  import { NGXLogger } from 'ngx-logger';
7
7
  import { CustomLogger } from '../logger/customLogger';
@@ -17,13 +17,14 @@ describe('TiledeskAuthService', () => {
17
17
 
18
18
  beforeEach(() => {
19
19
  TestBed.configureTestingModule({
20
- providers: [
20
+ imports: [],
21
+ providers: [
21
22
  TiledeskAuthService,
22
23
  AppStorageService,
23
- // {provide: HttpClient, useValue: httpClientMock}
24
- ],
25
- imports: [HttpClientTestingModule]
26
- })
24
+ provideHttpClient(withInterceptorsFromDi()),
25
+ provideHttpClientTesting(),
26
+ ]
27
+ })
27
28
 
28
29
  // httpClientMock = jasmine.createSpyObj(['getAllObjects']);
29
30
  httpMock = TestBed.inject(HttpTestingController);
@@ -1,17 +1,21 @@
1
- import { HttpClientTestingModule } from '@angular/common/http/testing';
1
+ import { provideHttpClientTesting } from '@angular/common/http/testing';
2
2
  import { TestBed, inject } from '@angular/core/testing';
3
3
  import { AppStorageService } from '../abstract/app-storage.service';
4
4
 
5
5
  import { TiledeskRequestsService } from './tiledesk-requests.service';
6
+ import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
6
7
 
7
8
  describe('TiledeskRequestsService', () => {
8
9
  beforeEach(() => {
9
10
  TestBed.configureTestingModule({
10
- providers: [
11
- TiledeskRequestsService,
12
- AppStorageService],
13
- imports: [HttpClientTestingModule]
14
- });
11
+ imports: [],
12
+ providers: [
13
+ TiledeskRequestsService,
14
+ AppStorageService,
15
+ provideHttpClient(withInterceptorsFromDi()),
16
+ provideHttpClientTesting()
17
+ ]
18
+ });
15
19
  });
16
20
 
17
21
  it('should be created', inject([TiledeskRequestsService], (service: TiledeskRequestsService) => {
@@ -71,6 +71,8 @@ export const TYPE_POPUP_DETAIL_MESSAGE = 'DETAIL_MESSAGE';
71
71
  export const MESSAGE_TYPE_INFO = 'INFO';
72
72
  export const MESSAGE_TYPE_MINE = 'MINE';
73
73
  export const MESSAGE_TYPE_OTHERS = 'OTHERS';
74
+ export const MESSAGE_TYPE_PRIVATE = 'PRIVATE';
75
+
74
76
 
75
77
  export enum INFO_MESSAGE_TYPE {
76
78
  CHAT_REOPENED = 'CHAT_REOPENED',
@@ -7,7 +7,8 @@ import {
7
7
  MESSAGE_TYPE_OTHERS,
8
8
  MAX_WIDTH_IMAGES,
9
9
  INFO_MESSAGE_TYPE,
10
- CHANNEL_TYPE
10
+ CHANNEL_TYPE,
11
+ MESSAGE_TYPE_PRIVATE
11
12
  } from '../../chat21-core/utils/constants';
12
13
  /** */
13
14
  export function isCarousel(message: any) {
@@ -103,10 +104,20 @@ export function isFirstMessage(messages, senderId, index):boolean{
103
104
  return false;
104
105
  }
105
106
 
107
+ export function isPrivate(message: any) {
108
+ if (message && message.attributes && message.attributes.subtype === 'private') {
109
+ return true;
110
+ }
111
+ return false;
112
+ }
113
+
106
114
 
107
115
  /** */
108
116
  export function messageType(msgType: string, message: any) {
109
117
 
118
+ if (msgType === MESSAGE_TYPE_PRIVATE) {
119
+ return isPrivate(message);
120
+ }
110
121
  if (msgType === MESSAGE_TYPE_INFO) {
111
122
  return isInfo(message);
112
123
  }
@@ -194,9 +205,13 @@ export function isEmojii(message: any){
194
205
  // https://localcoder.org/javascript-detect-if-a-string-contains-only-unicode-emojis
195
206
  try {
196
207
  if(!message) return false;
208
+ // Removes all characters that are NOT emojis (unicode > \u1eeff)
197
209
  const onlyEmojis = message.replace(new RegExp('[\u0000-\u1eeff]', 'g'), '')
210
+ // Removes spaces, line breaks, and carriage returns from the string
198
211
  const visibleChars = message.replace(new RegExp('[\n\r\s]+|( )+', 'g'), '')
212
+ // Removes Chinese characters from the string
199
213
  const chineseChars = message.replace(new RegExp('[\u4e00-\u9fa5]', 'g'), '')
214
+
200
215
  if(onlyEmojis === '' || visibleChars == '' || chineseChars=='') return false
201
216
  return (onlyEmojis.length === visibleChars.length && onlyEmojis.length <= 2)
202
217
  } catch(e) {
@@ -204,6 +219,22 @@ export function isEmojii(message: any){
204
219
  }
205
220
  }
206
221
 
222
+ export function findAndRemoveEmoji(text): string{
223
+
224
+ if (!text) return text;
225
+
226
+ // Regex to find most unicode emojis
227
+ // Source: https://thekevinscott.com/emojis-in-javascript/
228
+ const emojiRegex = /([\u2700-\u27BF]|[\uE000-\uF8FF]|[\uD83C-\uDBFF][\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83D[\uDE00-\uDE4F])/g;
229
+
230
+ // Removes all found emojis
231
+ text = text.replace(emojiRegex, '');
232
+
233
+ // Removes any multiple spaces left by the removal of emojis
234
+ text = text.replace(/\s{2,}/g, ' ').trim();
235
+ return text
236
+ }
237
+
207
238
  export function isJustRecived(startedAt, time) {
208
239
  if (time > startedAt) {
209
240
  return true;
@@ -1,5 +1,5 @@
1
- import * as dayjs from 'dayjs'
2
- import * as duration from 'dayjs/plugin/duration'
1
+ import dayjs from 'dayjs';
2
+ import duration from 'dayjs/plugin/duration'
3
3
  dayjs.extend(duration)
4
4
  // tslint:disable-next-line:max-line-length
5
5
 
@@ -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
 
package/src/launch.js CHANGED
@@ -79,10 +79,11 @@ function loadIframe(tiledeskScriptBaseLocation) {
79
79
  srcTileDesk += '</head>';
80
80
  srcTileDesk += '<body>';
81
81
  srcTileDesk += '<chat-root></chat-root>';
82
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/runtime.js"></script>';
83
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/polyfills.js"></script>';
84
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/vendor.js"></script>';
85
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/main.js"></script>';
82
+ // srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/runtime.js"></script>';
83
+ srcTileDesk += '<script type="module" async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/polyfills.js"></script>';
84
+ // srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/vendor.js"></script>';
85
+ srcTileDesk += '<script type="module" async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/main.js"></script>';
86
+ srcTileDesk += '<script type="module" async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/scripts.js"></script>';
86
87
  srcTileDesk += '<link type="text/css" rel="stylesheet" href="'+tiledeskScriptBaseLocation+'/styles.css" media="all"></link>';
87
88
  srcTileDesk += '</body>';
88
89
  srcTileDesk += '</html>';
@@ -154,6 +155,12 @@ function loadIframe(tiledeskScriptBaseLocation) {
154
155
  window.tiledesk.on('onNewConversation', function(event_data) {
155
156
  // console.log("test-custom-auth.html onNewConversation >>>",event_data);
156
157
  const tiledeskToken = window.tiledesk.angularcomponent.component.g.tiledeskToken;
158
+
159
+ // if hiddenMessage is present, do not call /events endpoint because conversation is created by /messages endpoint
160
+ const hiddenMessage = window.tiledesk.angularcomponent.component.g.hiddenMessage;
161
+ if(hiddenMessage){
162
+ return;
163
+ }
157
164
  // console.log(">>>> tiledeskToken >>>> ",event_data.detail.appConfigs.apiUrl+event_data.detail.default_settings.projectid);
158
165
  if(tiledeskToken) {
159
166
  var httpRequest = createCORSRequest('POST', event_data.detail.appConfigs.apiUrl+event_data.detail.default_settings.projectid+'/events',true); //set async to false because loadParams must return when the get is complete
@@ -80,10 +80,11 @@ function loadIframe(tiledeskScriptBaseLocation) {
80
80
  srcTileDesk += '</head>';
81
81
  srcTileDesk += '<body>';
82
82
  srcTileDesk += '<chat-root></chat-root>';
83
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{runtime}}.js"></script>';
84
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{polyfills}}.js"></script>';
85
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{vendor}}.js"></script>';
86
- srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{main}}.js"></script>';
83
+ // srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{runtime}}.js"></script>';
84
+ srcTileDesk += '<script type="module" async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{polyfills}}.js"></script>';
85
+ // srcTileDesk += '<script async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{vendor}}.js"></script>';
86
+ srcTileDesk += '<script type="module" async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{main}}.js"></script>';
87
+ srcTileDesk += '<script type="module" async type="text/javascript" src="'+tiledeskScriptBaseLocation+'/{{scripts}}.js"></script>';
87
88
  srcTileDesk += '<link type="text/css" rel="stylesheet" href="'+tiledeskScriptBaseLocation+'/{{styles}}.css" media="all"></link>';
88
89
  srcTileDesk += '</body>';
89
90
  srcTileDesk += '</html>';
@@ -155,6 +156,12 @@ function loadIframe(tiledeskScriptBaseLocation) {
155
156
  window.tiledesk.on('onNewConversation', function(event_data) {
156
157
  // console.log("test-custom-auth.html onNewConversation >>>",event_data);
157
158
  const tiledeskToken = window.tiledesk.angularcomponent.component.g.tiledeskToken;
159
+
160
+ // if hiddenMessage is present, do not call /events endpoint because conversation is created by /messages endpoint
161
+ const hiddenMessage = window.tiledesk.angularcomponent.component.g.hiddenMessage;
162
+ if(hiddenMessage){
163
+ return;
164
+ }
158
165
  // console.log(">>>> tiledeskToken >>>> ",event_data.detail.appConfigs.apiUrl+event_data.detail.default_settings.projectid);
159
166
  if(tiledeskToken) {
160
167
  var httpRequest = createCORSRequest('POST', event_data.detail.appConfigs.apiUrl+event_data.detail.default_settings.projectid+'/events',true); //set async to false because loadParams must return when the get is complete
@@ -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
 
package/tsconfig.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "outDir": "./dist/out-tsc",
6
6
  "sourceMap": true,
7
7
  "declaration": false,
8
- "downlevelIteration": true,
8
+ "esModuleInterop": true,
9
9
  "experimentalDecorators": true,
10
10
  "module": "es2020",
11
11
  "moduleResolution": "node",
@@ -23,7 +23,6 @@
23
23
  "dom"
24
24
  ],
25
25
  "resolveJsonModule": true,
26
- "allowSyntheticDefaultImports": true,
27
26
  },
28
27
  "skipLibCheck": true,
29
28
  "angularCompilerOptions": {