@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.
- package/CHANGELOG.md +43 -2
- package/Dockerfile +3 -3
- package/angular.json +21 -17
- package/deploy_amazon_beta.sh +6 -11
- package/deploy_amazon_prod.sh +4 -11
- package/deploy_beta.sh +11 -0
- package/deploy_prod.sh +12 -3
- package/package.json +18 -20
- package/src/app/app.component.spec.ts +13 -13
- package/src/app/app.module.ts +11 -13
- package/src/app/component/conversation-detail/conversation/conversation.component.html +1 -2
- package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +11 -13
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +4 -2
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.scss +1 -0
- 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 +60 -8
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +2 -2
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +10 -11
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.ts +0 -1
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.ts +5 -1
- package/src/app/component/home-conversations/home-conversations.component.spec.ts +9 -11
- package/src/app/component/message/audio/audio.component.ts +30 -16
- package/src/app/component/star-rating-widget/star-rating-widget.component.spec.ts +10 -11
- package/src/app/pipe/date-ago.pipe.ts +2 -2
- package/src/app/providers/app-config.service.spec.ts +5 -6
- package/src/app/providers/global-settings.service.spec.ts +7 -8
- package/src/app/providers/global-settings.service.ts +1 -14
- package/src/app/providers/star-rating-widget.service.spec.ts +7 -8
- package/src/app/providers/translator.service.spec.ts +7 -9
- package/src/app/providers/waiting.service.spec.ts +7 -8
- package/src/app/sass/_variables.scss +6 -1
- package/src/app/sass/animations.scss +1 -0
- package/src/app/utils/globals.ts +1 -4
- package/src/app/utils/utils.ts +0 -9
- 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 -18
- package/src/assets/twp/index.html +13 -3
- package/src/build_launch.js +9 -8
- package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +7 -1
- package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +8 -1
- package/src/chat21-core/providers/tiledesk/tiledesk-auth.service.spec.ts +8 -7
- package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.spec.ts +10 -6
- package/src/chat21-core/utils/constants.ts +2 -0
- package/src/chat21-core/utils/utils-message.ts +32 -1
- package/src/chat21-core/utils/utils.ts +85 -2
- package/src/launch.js +11 -4
- package/src/launch_template.js +11 -4
- package/src/models/project.ts +4 -1
- package/tsconfig.json +1 -2
package/src/app/utils/globals.ts
CHANGED
|
@@ -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,
|
|
528
|
+
'hideCloseConversationOptionMenu': this.hideCloseConversationOptionMenu,
|
|
532
529
|
'hideSettings': this.hideSettings,
|
|
533
530
|
'isLogEnabled': this.isLogEnabled,
|
|
534
531
|
'isOpen': this.isOpen, 'isShown': this.isShown,
|
package/src/app/utils/utils.ts
CHANGED
|
@@ -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'];
|
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":"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.
|
|
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
|
}
|
|
@@ -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
|
|
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
|
}
|
package/src/build_launch.js
CHANGED
|
@@ -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
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
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 + "
|
|
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 {
|
|
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
|
-
|
|
20
|
+
imports: [],
|
|
21
|
+
providers: [
|
|
21
22
|
TiledeskAuthService,
|
|
22
23
|
AppStorageService,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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 {
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
2
|
-
import
|
|
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
|
package/src/launch_template.js
CHANGED
|
@@ -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
|
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
|
|
package/tsconfig.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"outDir": "./dist/out-tsc",
|
|
6
6
|
"sourceMap": true,
|
|
7
7
|
"declaration": false,
|
|
8
|
-
"
|
|
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": {
|