@chat21/chat21-ionic 3.4.29 → 3.4.30-rc2

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 (75) hide show
  1. package/CHANGELOG.md +120 -2
  2. package/angular.json +1 -0
  3. package/package.json +1 -1
  4. package/src/app/app.component.html +3 -1
  5. package/src/app/app.component.ts +71 -12
  6. package/src/app/chatlib/conversation-detail/message/image/image.component.html +1 -0
  7. package/src/app/chatlib/conversation-detail/message/image/image.component.ts +19 -0
  8. package/src/app/components/canned-response/canned-response.component.html +26 -23
  9. package/src/app/components/canned-response/canned-response.component.scss +0 -2
  10. package/src/app/components/canned-response/canned-response.component.ts +3 -1
  11. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.html +24 -1
  12. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.scss +30 -0
  13. package/src/app/components/conversation-detail/message-text-area/message-text-area.component.ts +39 -9
  14. package/src/app/components/conversation-info/info-content/info-content.component.ts +2 -2
  15. package/src/app/components/conversation-info/info-group/info-group.component.ts +23 -21
  16. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.html +1 -1
  17. package/src/app/components/conversations-list/header-conversations-list/header-conversations-list.component.ts +5 -1
  18. package/src/app/components/navbar/navbar.component.html +3 -3
  19. package/src/app/components/navbar/navbar.component.ts +29 -38
  20. package/src/app/components/project-item/project-item.component.ts +11 -11
  21. package/src/app/components/sidebar/sidebar.component.html +65 -45
  22. package/src/app/components/sidebar/sidebar.component.ts +110 -117
  23. package/src/app/components/sidebar-user-details/sidebar-user-details.component.html +2 -2
  24. package/src/app/components/sidebar-user-details/sidebar-user-details.component.ts +10 -7
  25. package/src/app/modals/create-ticket/create-ticket.page.ts +4 -2
  26. package/src/app/pages/conversation-detail/conversation-detail.page.html +7 -3
  27. package/src/app/pages/conversation-detail/conversation-detail.page.ts +95 -7
  28. package/src/app/pages/conversations-list/conversations-list.page.html +2 -0
  29. package/src/app/pages/conversations-list/conversations-list.page.ts +40 -2
  30. package/src/app/services/global-settings/global-settings.service.ts +11 -3
  31. package/src/app/services/nav-proxy.service.ts +0 -1
  32. package/src/app/services/project_users/project-users.service.spec.ts +16 -0
  33. package/src/app/services/project_users/project-users.service.ts +63 -0
  34. package/src/app/services/tiledesk/tiledesk.service.ts +0 -16
  35. package/src/app/services/triggerEvents/triggerEvents.ts +28 -0
  36. package/src/app/services/websocket/websocket-js.ts +59 -534
  37. package/src/app/services/websocket/websocket-js_old.ts +578 -0
  38. package/src/app/services/websocket/websocket.service.ts +9 -10
  39. package/src/app/services/websocket/websocket.worker.ts +242 -0
  40. package/src/app/shared/shared.module.ts +11 -2
  41. package/src/app/utils/globals.ts +2 -0
  42. package/src/app/utils/permissions.constants.ts +138 -0
  43. package/src/app/utils/project-utils.ts +2 -2
  44. package/src/app/utils/utils.ts +18 -1
  45. package/src/assets/i18n/ar.json +11 -1
  46. package/src/assets/i18n/az.json +11 -1
  47. package/src/assets/i18n/de.json +11 -1
  48. package/src/assets/i18n/en.json +11 -1
  49. package/src/assets/i18n/es.json +11 -1
  50. package/src/assets/i18n/fr.json +11 -1
  51. package/src/assets/i18n/it.json +13 -3
  52. package/src/assets/i18n/kk.json +11 -1
  53. package/src/assets/i18n/pt.json +11 -1
  54. package/src/assets/i18n/ru.json +11 -1
  55. package/src/assets/i18n/sr.json +11 -1
  56. package/src/assets/i18n/sv.json +11 -1
  57. package/src/assets/i18n/tr.json +11 -1
  58. package/src/assets/i18n/uk.json +11 -1
  59. package/src/assets/i18n/uz.json +12 -1
  60. package/src/assets/img/no_data_found.png +0 -0
  61. package/src/assets/js/agentDesktop-sdk.js +55 -0
  62. package/src/assets/js/chat21client.js +36 -0
  63. package/src/assets/js/mqtt-keepalive-worker.js +53 -0
  64. package/src/assets/test.html +5 -2
  65. package/src/chat-config-template.json +1 -0
  66. package/src/chat-config.json +1 -0
  67. package/src/chat21-core/models/projectUsers.ts +19 -0
  68. package/src/chat21-core/providers/abstract/upload.service.ts +5 -1
  69. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +1 -1
  70. package/src/chat21-core/providers/firebase/firebase-upload.service.ts +136 -9
  71. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +1 -1
  72. package/src/chat21-core/providers/native/native-image-repo.ts +1 -1
  73. package/src/chat21-core/providers/native/native-upload-service.ts +143 -45
  74. package/src/chat21-core/providers/tiledesk/tiledesk-auth.service.ts +3 -0
  75. package/src/chat21-core/utils/utils.ts +16 -2
package/CHANGELOG.md CHANGED
@@ -8,6 +8,12 @@
8
8
  ### **Copyrigth**:
9
9
  *Tiledesk SRL*
10
10
 
11
+ # 3.4.29-rc2
12
+ - **changed**: minor updates on API to upload file/image into chat
13
+
14
+ # 3.4.29-rc1
15
+ - **changed**: API for upload a file/image into chat
16
+
11
17
  # 3.4.29 in PROD
12
18
  - **bug-fixed**: web (Chrome >= 144) `ion-content` stopped scrolling on some pages (conversation list / contacts directory / unassigned); removed the forced `--overflow: hidden` and handled scrolling on Ionic’s internal scroll container via `ion-content::part(scroll)`
13
19
 
@@ -17,6 +23,99 @@
17
23
  # 3.4.27 in PROD
18
24
  - **bug-fixed**: cannot find route if userFullname contains /
19
25
 
26
+ # 3.4.29-rc1
27
+ - **bug-fixed**: web (Chrome >= 144) `ion-content` stopped scrolling on some pages (conversation list / contacts directory / unassigned); removed the forced `--overflow: hidden` and handled scrolling on Ionic’s internal scroll container via `ion-content::part(scroll)`
28
+
29
+ # 3.4.28-rc1
30
+ - **bug-fixed**: cannot do project subscription if last_project object is not a project_user obj
31
+
32
+ # 3.4.27-rc26
33
+ - **bug-fixed**: wss push requests twice
34
+
35
+ # 3.4.27-rc25
36
+ - **bug-fixed**: projectUserService is not initialized
37
+
38
+ # 3.4.27-rc24
39
+ - **added**: implementation of multiple message in wss onmessage
40
+
41
+ # 3.4.27-rc23
42
+ - **added**: keepAlive worker for MQTT connection
43
+
44
+ # 3.4.27-rc22
45
+ - **added**: new WsWorker to manage iframe chrome throttling while tab is in background or hidden
46
+
47
+ # 3.4.27-rc21
48
+ - **changed**: new wss reconnect and timeout keepalive
49
+ - **bug-fixed**: cannot route if senderFullaname contains /
50
+
51
+ # 3.4.27-rc20
52
+ - **added**: onOpenTicketExternally event in triggerEvents service
53
+
54
+ # 3.4.27-rc19
55
+ - **added**: window.parent['openTicketOnHDA']
56
+
57
+ # 3.4.27-rc18
58
+ - **added**: triggerOnUpdateNewConversationBadge to update conversation badge count in parent component
59
+
60
+ # 3.4.27-rc17
61
+ - **bug-fixed**: setNotification not called when click on a conversation
62
+
63
+ # 3.4.27-rc16
64
+ - **bug-fixed**: setNotification not called when resolve a conversation
65
+
66
+ # 3.4.27-rc15
67
+ - **changed**: /images with /img in assets folder
68
+
69
+ # 3.4.27-rc14
70
+ - **added**: DISPLAY_EDIT_PROFILE brand variable
71
+ - **bug-fixed**: emojii is sent also if is not allowed
72
+
73
+ # 3.4.27-rc13
74
+ - **added**: ability to mantain logout parameter when redirect to dashboard urls from sidebar component
75
+
76
+ # 3.4.27-rc12
77
+ - **added**: ability to manage logOut option in sidebar-user-detail with tiledesk_logOut url query params
78
+
79
+ # 3.4.27-rc11
80
+ - **bug-fixed**: fixed infinite loading in contact list
81
+
82
+ # 3.4.27-rc10
83
+ - **added**: ability to manage header-conversation-list with roles
84
+ - **bug-fixed**: members in group list not loaded
85
+
86
+ # 3.4.27-rc9
87
+ - **bug-fixed**: Scrolling to the last message when opening a conversation
88
+ - **bug-fixed**: Loading in the conversation list disabled when removing the last conversation
89
+
90
+ # 3.4.27-rc8
91
+ - **added**: ability to open ticket to external service
92
+ - **added**: ticketSection env var
93
+
94
+ # 3.4.27-rc7
95
+ - **bug-fixed**: Scrolling to the last message when opening a conversation
96
+ - **bug-fixed**: Loading in the conversation list disabled when removing the last conversation
97
+
98
+ # 3.4.27-rc6
99
+ - **bug-fixed**: user for dashboard app is incorrect
100
+
101
+ # 3.4.27-rc5
102
+ - **added**: managed roles in sidebar e navbar
103
+ - **bug-fixed**: projectId and supportMode url is not saved in localstorage
104
+
105
+ # 3.4.27-rc4
106
+ - **bug-fixed**: extractUrls function is not able to detect url start with www or without https/http
107
+ - **bug-fixed**: if message is sent with keydown, error on domain check is not showed
108
+
109
+ # 3.4.27-rc3
110
+ - **bug-fixed**: cannot set user availability if supportMode is enabled and tiledesk_projectID url params is set
111
+
112
+ # 3.4.27-rc2
113
+ - **bug-fixed**: cannede responses role
114
+
115
+ # 3.4.27-rc1
116
+ - **added**: managed canned responses with roles
117
+ - **changed**: name in info mesage
118
+
20
119
  # 3.4.26 in PROD
21
120
 
22
121
  # 3.4.26-rc2
@@ -25,6 +124,7 @@
25
124
  # 3.4.26-rc1
26
125
  - **added**: tiledesk_projectID query param to manage user status
27
126
  - **added**: token to managane ticket feature
127
+ - **added**: getOsCode login into utils.ts
28
128
 
29
129
  # 3.4.25 in PROD
30
130
  - **changed**: pipe marked to support malicious text input
@@ -47,6 +147,24 @@
47
147
  # 3.4.22 in PROD
48
148
  - **added**: managed allowed_upload_extentions from project settings
49
149
 
150
+ # 3.4.21-rc6
151
+ - **added**: managed allowed_upload_extentions from project settings
152
+
153
+ # 3.4.21-rc5
154
+ - **added**: setConversation as read when agent click on it
155
+
156
+ # 3.4.21-rc4
157
+ - **added**: ability to init and decrement new conversation count badge
158
+
159
+ # 3.4.21-rc3
160
+ - **changed**: badge notification for agentDesktop
161
+
162
+ # 3.4.21-rc2
163
+ - **added**: count in newConversation handler event
164
+
165
+ # 3.4.21-rc1
166
+ - **added**: implement badge notification for agentDesktop sw when new conversation is assigned to logged agent
167
+
50
168
  # 3.4.21 in PROD
51
169
 
52
170
  # 3.4.20 in PROD
@@ -60,8 +178,8 @@
60
178
  - **bug-fixed**: minor fix on ion-texarea element with allowed url domain
61
179
 
62
180
  # 3.4.19-rc1
63
- - added: ability to check for if emoji is allowd to be sent in message textarea
64
- - added: ability to check for if url domain is allowd to be sent in message textarea
181
+ - **added**: ability to check for if emoji is allowd to be sent in message textarea
182
+ - **added**: ability to check for if url domain is allowd to be sent in message textarea
65
183
 
66
184
  # 3.4.18 in PROD
67
185
 
package/angular.json CHANGED
@@ -32,6 +32,7 @@
32
32
  "src/chat-config-template.json",
33
33
  "src/chat-config.json",
34
34
  "src/chat-config-dev.json",
35
+ "src/chat-config-native-ar.json",
35
36
  {
36
37
  "glob": "**/*",
37
38
  "input": "src/assets",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-ionic",
3
3
  "author": "Tiledesk SRL",
4
- "version": "3.4.29",
4
+ "version": "3.4.30-rc2",
5
5
  "license": "MIT License",
6
6
  "homepage": "https://tiledesk.com/",
7
7
  "repository": {
@@ -21,7 +21,9 @@
21
21
  </div> -->
22
22
 
23
23
  <div class="user-details-sidebar" [ngClass]="{'hide-sidebar': IS_ONLINE === false || IS_ON_MOBILE_DEVICE || SUPPORT_MODE === false}">
24
- <app-sidebar-user-details> </app-sidebar-user-details>
24
+ <app-sidebar-user-details
25
+ [logOut]="g?.logOut">
26
+ </app-sidebar-user-details>
25
27
  </div>
26
28
 
27
29
  <ion-split-pane when="md" contentId="main" [ngClass]="{'mobile': IS_ON_MOBILE_DEVICE, 'sidebar-hidden': IS_ON_MOBILE_DEVICE || SUPPORT_MODE === false}">
@@ -44,6 +44,9 @@ import { conversationToMessage } from 'src/chat21-core/utils/utils-message';
44
44
  import { ProjectService } from './services/projects/project.service';
45
45
  import { ContactsService } from './services/contacts/contacts.service';
46
46
  import { TiledeskService } from './services/tiledesk/tiledesk.service';
47
+ import { Project } from 'src/chat21-core/models/projects';
48
+ import { BRAND_BASE_INFO } from './utils/utils-resources';
49
+ import { ProjectUsersService } from './services/project_users/project-users.service';
47
50
 
48
51
  @Component({
49
52
  selector: 'app-root',
@@ -119,7 +122,6 @@ export class AppComponent implements OnInit {
119
122
  private navService: NavProxyService,
120
123
  // public chatPresenceHandler: ChatPresenceHandler,
121
124
  public typingService: TypingService,
122
- public uploadService: UploadService,
123
125
  public appStorageService: AppStorageService,
124
126
 
125
127
  // public chatConversationsHandler: ChatConversationsHandler,
@@ -142,6 +144,7 @@ export class AppComponent implements OnInit {
142
144
  /**TILEDESK SERVICES */
143
145
  private tiledeskService: TiledeskService,
144
146
  private projectService: ProjectService,
147
+ private projectUsersService: ProjectUsersService,
145
148
  private contactsService: ContactsService
146
149
  ) {
147
150
 
@@ -168,6 +171,7 @@ export class AppComponent implements OnInit {
168
171
  }, { capture: true });
169
172
  }
170
173
 
174
+
171
175
  listenChatAlreadyOpenWithoutParamsInMobileMode() {
172
176
  this.events.subscribe('noparams:mobile', (isAlreadyOpenInMobileMode) => {
173
177
  // console.log('[APP-COMP] Chat is Already Open In Mobile Mode ', isAlreadyOpenInMobileMode)
@@ -296,6 +300,10 @@ export class AppComponent implements OnInit {
296
300
  this.zone = new NgZone({}); // a cosa serve?
297
301
 
298
302
  this.SUPPORT_MODE = this.g.supportMode
303
+ this.logger.info('[APP-COMP] this.SUPPORT_MODE', this.SUPPORT_MODE)
304
+
305
+ BRAND_BASE_INFO['LOGOUT_ENABLED'] = this.g.logOut
306
+ this.logger.info('[APP-COMP] this.logOut', BRAND_BASE_INFO['LOGOUT_ENABLED'])
299
307
  }
300
308
 
301
309
  });
@@ -332,7 +340,7 @@ export class AppComponent implements OnInit {
332
340
 
333
341
  listenToPostMsgs() {
334
342
  window.addEventListener("message", (event) => {
335
- this.logger.log("[APP-COMP] message event ", event);
343
+ // this.logger.log("[APP-COMP] message event ", event);
336
344
 
337
345
  if (event && event.data && event.data.action && event.data.parameter) {
338
346
  if (event.data.action === 'openJoinConversationModal') {
@@ -534,10 +542,10 @@ export class AppComponent implements OnInit {
534
542
  this.statusBar.styleLightContent();
535
543
  this.navService.init(this.sidebarNav, this.detailNav);
536
544
  this.tiledeskAuthService.initialize(this.appConfigProvider.getConfig().apiUrl);
537
- this.messagingAuthService.initialize();
538
-
545
+
539
546
  // this.currentUserService.initialize();
540
547
  this.chatManager.initialize();
548
+ this.messagingAuthService.initialize();
541
549
  this.presenceService.initialize(this.tenant);
542
550
  this.typingService.initialize(this.tenant);
543
551
 
@@ -547,7 +555,6 @@ export class AppComponent implements OnInit {
547
555
  if (pushEngine && pushEngine !== 'none') {
548
556
  this.notificationsService.initialize(this.tenant, vap_id_Key, platform)
549
557
  }
550
- this.uploadService.initialize();
551
558
 
552
559
  this.setLanguage(null)
553
560
  this.initAuthentication();
@@ -864,7 +871,8 @@ export class AppComponent implements OnInit {
864
871
  // console.log('[APP-COMP] PLATFORM', PLATFORM_MOBILE, 'route.snapshot', this.route.snapshot);
865
872
  if (!IDConv) {
866
873
  this.logger.log('[APP-COMP] navigateByUrl -- conversations-list');
867
- this.router.navigateByUrl('conversations-list')
874
+ const queryString = window.location.search; // restituisce ad es. "?jwt=...&tiledesk_supportMode=false"
875
+ this.router.navigateByUrl('conversations-list' + queryString);
868
876
  }
869
877
  // this.router.navigateByUrl(pageUrl);
870
878
  // this.navService.setRoot(ConversationListPage, {});
@@ -1115,13 +1123,19 @@ export class AppComponent implements OnInit {
1115
1123
  if (conversation && conversation.is_new === true && this.isInitialized) {
1116
1124
  this.manageTabNotification('conv_added', conversation.sound)
1117
1125
  this.manageEventNewConversation(conversation)
1126
+ //UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
1127
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1118
1128
  }
1119
1129
  if(conversation) this.updateConversationsOnStorage()
1120
1130
  });
1121
1131
 
1122
1132
  this.conversationsHandlerService.conversationChanged.subscribe((conversation: ConversationModel) => {
1123
1133
  // console.log('[APP-COMP] ***** subscribeConversationChanged conversation: ', conversation);
1124
- if(conversation) this.updateConversationsOnStorage();
1134
+ if(conversation){
1135
+ this.updateConversationsOnStorage();
1136
+ //UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
1137
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1138
+ }
1125
1139
  });
1126
1140
 
1127
1141
  this.conversationsHandlerService.conversationChangedDetailed.subscribe((changes: {value: ConversationModel, previousValue: ConversationModel}) => {
@@ -1145,6 +1159,8 @@ export class AppComponent implements OnInit {
1145
1159
  if(conversation) {
1146
1160
  this.updateConversationsOnStorage();
1147
1161
  this.segmentResolved(conversation);
1162
+ //UPDATE NOTIFICATION FOR NEW CONVERSATION COUNT
1163
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1148
1164
  this.router.navigateByUrl('conversation-detail/'); //redirect to basePage
1149
1165
  }
1150
1166
  });
@@ -1178,6 +1194,7 @@ export class AppComponent implements OnInit {
1178
1194
  this.chatManager.setCurrentUser(currentUser);
1179
1195
 
1180
1196
  this.tiledeskService.initialize(serverBaseURL)
1197
+ this.projectUsersService.initialize(serverBaseURL)
1181
1198
  this.projectService.initialize(serverBaseURL)
1182
1199
  this.contactsService.initialize(serverBaseURL)
1183
1200
 
@@ -1185,11 +1202,14 @@ export class AppComponent implements OnInit {
1185
1202
  this.events.publish('go:online', true);
1186
1203
  // this.chatManager.startApp();
1187
1204
 
1205
+
1206
+ //INIT WEBSOCKET
1207
+ this.connetWebsocket(tiledeskToken)
1208
+
1188
1209
  // ----------------------------------------------
1189
1210
  // PUSH NOTIFICATIONS
1190
1211
  // ----------------------------------------------
1191
1212
  const pushEngine = this.appConfigProvider.getConfig().pushEngine
1192
-
1193
1213
  if (currentUser) {
1194
1214
  if (pushEngine && pushEngine !== 'none') {
1195
1215
  this.notificationsService.getNotificationPermissionAndSaveToken(currentUser.uid);
@@ -1211,6 +1231,24 @@ export class AppComponent implements OnInit {
1211
1231
  } catch (err) {
1212
1232
  this.logger.error('[APP-COMP] -> error:', err);
1213
1233
  }
1234
+
1235
+ // ----------------------------------------------
1236
+ // LAST PROJECT FROM URL
1237
+ // ----------------------------------------------
1238
+ if(this.g.projectID){
1239
+ this.projectService.getProjects().subscribe({ next: (projects: Project[]) => {
1240
+ const project = projects.find(prjct => prjct.id_project._id === this.g.projectID)
1241
+ if(project){
1242
+ this.logger.log('[APP-COMP] - GET PROJECT - project found with this.projectID', project);
1243
+ localStorage.setItem('last_project', JSON.stringify(project))
1244
+ this.events.publish('storage:last_project', project)
1245
+ }
1246
+ }, error: (error) => {
1247
+ this.logger.log('[APP-COMP] - GET PROJECT - project NOT found with this.projectID', this.g.projectID, error);
1248
+ }, complete: () => {
1249
+
1250
+ }});
1251
+ }
1214
1252
  }
1215
1253
 
1216
1254
 
@@ -1251,9 +1289,21 @@ export class AppComponent implements OnInit {
1251
1289
  }
1252
1290
 
1253
1291
  goToDashboardLogin(){
1254
- let DASHBOARD_URL = this.appConfigProvider.getConfig().dashboardUrl + '#/login'
1255
- const myWindow = window.open(DASHBOARD_URL, '_self');
1256
- myWindow.focus();
1292
+ // let DASHBOARD_URL = this.appConfigProvider.getConfig().dashboardUrl + '#/login'
1293
+ // const myWindow = window.open(DASHBOARD_URL, '_self');
1294
+ // myWindow.focus();
1295
+ }
1296
+
1297
+ connetWebsocket(tiledeskToken) {
1298
+
1299
+ this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] tiledeskToken ', tiledeskToken)
1300
+ const appconfig = this.appConfigProvider.getConfig();
1301
+ this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', appconfig.wsUrl)
1302
+ const WS_URL = appconfig.wsUrl + '?token=' + tiledeskToken
1303
+ this.logger.log('[WEBSOCKET-JS] connetWebsocket called in [PROJECT-ITEM] wsUrl ', WS_URL)
1304
+ this.webSocketJs.init(
1305
+ WS_URL
1306
+ );
1257
1307
  }
1258
1308
 
1259
1309
 
@@ -1352,7 +1402,8 @@ export class AppComponent implements OnInit {
1352
1402
 
1353
1403
  subscribeConversationSelected= (conversation: ConversationModel) => {
1354
1404
  if(conversation && conversation.is_new){
1355
- this.audio_NewConv.pause()
1405
+ this.audio_NewConv.pause();
1406
+ this.conversationsHandlerService.setConversationRead(conversation.uid)
1356
1407
  }
1357
1408
  }
1358
1409
 
@@ -1428,6 +1479,9 @@ export class AppComponent implements OnInit {
1428
1479
  this.logger.debug('[APP-COMP]-CONVS - INIT CONV CONVS 2', conversations)
1429
1480
  this.events.publish('appcompSubscribeToConvs:loadingIsActive', false);
1430
1481
  }
1482
+
1483
+ //INIT NOTIFICATION FOR NEW CONVERSATION COUNT
1484
+ this.triggerOnUpdateNewConversationBadge(this.conversationsHandlerService.countIsNew());
1431
1485
  });
1432
1486
 
1433
1487
  }
@@ -1710,6 +1764,11 @@ export class AppComponent implements OnInit {
1710
1764
  this.triggerEvents.triggerOnInit(detailOBJ)
1711
1765
  }
1712
1766
 
1767
+ private triggerOnUpdateNewConversationBadge(count: number){
1768
+ const detailOBJ = { event: 'onUpdateNewConversationBadge', count: count.toString() }
1769
+ this.triggerEvents.triggerOnUpdateNewConversationBadge(detailOBJ)
1770
+ }
1771
+
1713
1772
 
1714
1773
  // @HostListener('mouseenter', ['$event'])
1715
1774
  // onMouseEnter(event: any) {
@@ -8,5 +8,6 @@
8
8
  [height]="height"
9
9
  [src]="metadata.src"
10
10
  (load)="onLoaded($event)"
11
+ (error)="onError($event)"
11
12
  (click)="openImageViewerModal(metadata.src, metadata.name)" />
12
13
  </div>
@@ -15,6 +15,7 @@ export class ImageComponent implements OnInit {
15
15
  loading: boolean = true
16
16
  modal: any
17
17
  span: any
18
+ private readonly fallbackSrc = 'assets/img/no_data_found.png'
18
19
 
19
20
  constructor() { }
20
21
 
@@ -26,6 +27,24 @@ export class ImageComponent implements OnInit {
26
27
  this.onElementRendered.emit({element: "image", status:true})
27
28
  }
28
29
 
30
+ onError(event: Event) {
31
+ this.loading = false
32
+ const img = event?.target as HTMLImageElement | null
33
+ if (!img) {
34
+ return
35
+ }
36
+ // avoid infinite loop if fallback image fails too
37
+ if (img.src && img.src.includes(this.fallbackSrc)) {
38
+ return
39
+ }
40
+ img.src = this.fallbackSrc
41
+ // also update metadata so click-to-open uses the fallback consistently
42
+ if (this.metadata) {
43
+ this.metadata.src = this.fallbackSrc
44
+ }
45
+ this.onElementRendered.emit({ element: 'image', status: true })
46
+ }
47
+
29
48
  _downloadImage(url: string, fileName: string) {
30
49
  // console.log('Image COMP - IMAGE URL ', url)
31
50
  // console.log('Image COMP - IMAGE FILENAME ', fileName)
@@ -1,20 +1,30 @@
1
1
  <div>
2
- <div class="canned-list" *ngIf="tagsCannedFilter.length > 0">
3
- <ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
4
- class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
5
- *ngFor="let canned of tagsCannedFilter; let i = index;"
6
- (click)="onClickCannedFN(canned, $event)">
7
- <div class="cannedContent">
8
- <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" #title></ion-input>
9
- <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.text" class="text truncate"></ion-input>
2
+ <div class="canned-list" *ngIf="!showLoading">
3
+ <span *ngIf="tagsCannedFilter.length > 0">
4
+ <ion-item button="true" [ngClass]="{'is_active_item': i == arrowkeyLocation}" lines="none"
5
+ class="canned-item no-ripple border" id="{{'canned-item_'+ i }}"
6
+ *ngFor="let canned of tagsCannedFilter; let i = index;"
7
+ (click)="onClickCannedFN(canned, $event)">
8
+ <div class="cannedContent">
9
+ <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.title" class="title" id="{{'titleCanned_'+canned._id}}" #title></ion-input>
10
+ <ion-input [class.readonly]="canned?.disabled" [readonly]="canned?.disabled" type="text" [(ngModel)]="canned.text" class="text truncate"></ion-input>
11
+ </div>
12
+ <!-- <ion-icon class="canned-item-icon" name="pin" src="assets/img/pin.svg" slot=end *ngIf="canned.pinned" (click)="onPinCanned(canned, $event)"></ion-icon>
13
+ <ion-icon class="canned-item-icon" name="pin" src="assets/img/pinned.svg" slot=end (click)="onUnPinCanned(canned, $event)"></ion-icon> -->
14
+ <ion-icon class="canned-item-icon" name="checkmark-sharp" slot=end *ngIf="(canned.createdBy === loggedUser.uid && !canned.disabled) && roles[PERMISSIONS.CANNED_RESPONSES_UPDATE]" (click)="onConfirmEditCanned(canned, $event)"></ion-icon>
15
+ <ion-icon class="canned-item-icon" name="pencil-sharp" slot=end *ngIf="(canned.createdBy === loggedUser.uid && canned.disabled) && roles[PERMISSIONS.CANNED_RESPONSES_UPDATE]" (click)="onEditCanned(canned, $event)"></ion-icon>
16
+ <ion-icon class="canned-item-icon" name="trash-bin-outline" slot=end *ngIf="(canned.createdBy === loggedUser.uid) && roles[PERMISSIONS.CANNED_RESPONSES_DELETE]" (click)="onDeleteCanned(canned, $event)"></ion-icon>
17
+ </ion-item>
18
+ </span>
19
+ <div class="no-data" *ngIf="tagsCannedFilter.length === 0">
20
+ <div class="container">
21
+ <ion-item button="false" lines="none" class="canned-item no-ripple border">
22
+ <ion-icon name="cloud-offline" slot="start"></ion-icon>
23
+ <ion-label>{{translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE')}}</ion-label>
24
+ </ion-item>
10
25
  </div>
11
- <!-- <ion-icon class="canned-item-icon" name="pin" src="assets/img/pin.svg" slot=end *ngIf="canned.pinned" (click)="onPinCanned(canned, $event)"></ion-icon>
12
- <ion-icon class="canned-item-icon" name="pin" src="assets/img/pinned.svg" slot=end (click)="onUnPinCanned(canned, $event)"></ion-icon> -->
13
- <ion-icon class="canned-item-icon" name="checkmark-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && !canned.disabled" (click)="onConfirmEditCanned(canned, $event)"></ion-icon>
14
- <ion-icon class="canned-item-icon" name="pencil-sharp" slot=end *ngIf="canned.createdBy === loggedUser.uid && canned.disabled" (click)="onEditCanned(canned, $event)"></ion-icon>
15
- <ion-icon class="canned-item-icon" name="trash-bin-outline" slot=end *ngIf="canned.createdBy === loggedUser.uid" (click)="onDeleteCanned(canned, $event)"></ion-icon>
16
- </ion-item>
17
- <ion-item class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
26
+ </div>
27
+ <ion-item *ngIf="roles[PERMISSIONS.CANNED_RESPONSES_CREATE]" class="canned-item add-canned-response-wpr" button="true" lines="none" (click)="onClickAddCannedResponseFN()">
18
28
  <ion-icon class="add-canned-response-icon" name="flash-outline"></ion-icon>
19
29
  <span class="add-canned-response-add-icon">+</span>
20
30
  <label class="add-canned-response-label" >{{translationMap?.get('AddNewCannedResponse')}}</label>
@@ -33,12 +43,5 @@
33
43
  <div class="label">{{translationMap.get('LABEL_LOADING')}}</div>
34
44
  </div>
35
45
  </div>
36
- <div class="no-data" *ngIf="tagsCannedFilter.length === 0 && !showLoading">
37
- <div class="container">
38
- <ion-item button="false" lines="none" class="canned-item no-ripple border">
39
- <ion-icon name="cloud-offline" slot="start"></ion-icon>
40
- <ion-label>{{translationMap.get('THERE_ARE_NO_CANNED_RESPONSES_AVAILABLE')}}</ion-label>
41
- </ion-item>
42
- </div>
43
- </div>
46
+
44
47
  </div>
@@ -278,7 +278,5 @@ ion-item {
278
278
  display: flex;
279
279
  justify-content: center;
280
280
  flex-direction: column;
281
- align-items: center;
282
-
283
281
  }
284
282
  }
@@ -9,6 +9,7 @@ import { TiledeskAuthService } from 'src/chat21-core/providers/tiledesk/tiledesk
9
9
  import { compareValues, htmlEntities } from 'src/chat21-core/utils/utils';
10
10
  import { getProjectIdSelectedConversation } from 'src/chat21-core/utils/utils-message';
11
11
  import { PLAN_NAME } from 'src/chat21-core/utils/constants';
12
+ import { PERMISSIONS } from 'src/app/utils/permissions.constants';
12
13
 
13
14
  @Component({
14
15
  selector: 'app-canned-response',
@@ -21,7 +22,7 @@ export class CannedResponseComponent implements OnInit {
21
22
  @Input() conversationWith: string;
22
23
  @Input() conversationWithFullname: string;
23
24
  @Input() currentString: string;
24
- @Input() canShowCanned: boolean = true;
25
+ @Input() roles: Array<string>;
25
26
  @Input() stylesMap: Map<string, string>;
26
27
  @Input() translationMap: Map<string, string>;
27
28
  @Output() onLoadedCannedResponses = new EventEmitter<[any]>();
@@ -36,6 +37,7 @@ export class CannedResponseComponent implements OnInit {
36
37
 
37
38
  public arrowkeyLocation = -1
38
39
 
40
+ PERMISSIONS = PERMISSIONS
39
41
 
40
42
  private logger: LoggerService = LoggerInstance.getInstance();
41
43
  constructor(
@@ -22,6 +22,13 @@
22
22
  {{translationMap?.get('WHATSAPP.LABEL_TEMPLATES')}}
23
23
  </ion-button>
24
24
  </div>
25
+ <!-- OPEN TICKET -->
26
+ <div *ngIf="ticketSection" class="section-option" id="template" tooltip="{{translationMap?.get('TICKET.OPEN_TICKET')}}" placement="top">
27
+ <ion-button fill="clear" [class.active]="section==='ticket'" (click)="onOpenSection('ticket')" [disabled]="channelType === 'direct'">
28
+ <ion-icon name="ticket"></ion-icon>
29
+ {{translationMap?.get('TICKET.OPEN_TICKET')}}
30
+ </ion-button>
31
+ </div>
25
32
  </div>
26
33
 
27
34
  <div class="footerContainerAlert">
@@ -50,6 +57,22 @@
50
57
  </ion-col>
51
58
  </ion-row>
52
59
 
60
+ <ion-row id="ticket" [style.display]="section==='ticket'? 'flex': 'none'">
61
+ <ion-col col-auto>
62
+ <div class="placeholder">{{translationMap.get('TICKET.DESCRIPTION')}}</div>
63
+ <div class="buttons-container">
64
+ <ion-button name="add" size="small" (click)="onClickTicket('open')">
65
+ <ion-icon name="add"></ion-icon>
66
+ {{translationMap?.get('TICKET.CONFIRM')}}
67
+ </ion-button>
68
+ <ion-button size="small" color="danger" (click)="onClickTicket('close')">
69
+ <ion-icon name="close"></ion-icon>
70
+ {{translationMap?.get('TICKET.CLOSE')}}
71
+ </ion-button>
72
+ </div>
73
+ </ion-col>
74
+ </ion-row>
75
+
53
76
  <ion-row id="message-text-area" [style.display]="section==='chat' || section ==='templates' || section ==='copilot'? 'flex': 'none'">
54
77
 
55
78
  <ion-col col-auto style="display: flex;">
@@ -57,7 +80,7 @@
57
80
  <div class="buttons-left">
58
81
 
59
82
  <!-- CANNED RESPONSES -->
60
- <ng-container *ngIf="areVisibleCAR && supportMode">
83
+ <ng-container *ngIf="areVisibleCAR && supportMode && cannedSection">
61
84
  <div class="canned-responses-btn-wpr" tooltip="{{translationMap?.get('CANNED_RESPONSES')}}" placement="top">
62
85
  <ion-button ion-button fill="clear" class="canned-responses-btn" (click)="openCannedResponses()"
63
86
  [disabled]="!conversationWith?.startsWith(CHANNEL_TYPE.SUPPORT_GROUP) || disableTextarea">
@@ -183,6 +183,36 @@
183
183
  }
184
184
  }
185
185
 
186
+ #ticket{
187
+ text-align: center;
188
+ font-size: 12px;
189
+
190
+ .buttons-container{
191
+ display: flex;
192
+ justify-content: center;
193
+ gap: 10px;
194
+
195
+ ion-button{
196
+ font-size: 12px;
197
+ --padding-top: 4px;
198
+ --padding-bottom: 4px;
199
+ --padding-start: 6px;
200
+ --padding-end: 6px;
201
+ --ripple-color: transparent;
202
+ text-transform: unset;
203
+ height: auto;
204
+
205
+ ion-icon{
206
+ margin-right: 4px;
207
+ }
208
+
209
+ &[name="add"]{
210
+ --background: var(--basic-blue);
211
+ }
212
+ }
213
+ }
214
+ }
215
+
186
216
  #fileInput {
187
217
  position: absolute;
188
218
  opacity: 0;