@chat21/chat21-web-widget 5.1.34-rc1 → 5.1.34

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 (193) hide show
  1. package/.github/workflows/docker-community-push-latest.yml +13 -23
  2. package/.github/workflows/docker-image-tag-community-tag-push.yml +12 -22
  3. package/CHANGELOG.md +8 -129
  4. package/Dockerfile +5 -4
  5. package/angular.json +3 -21
  6. package/docs/changelog/this-branch.md +0 -36
  7. package/env.sample +2 -3
  8. package/nginx.conf +2 -22
  9. package/package.json +3 -10
  10. package/src/app/app.component.html +2 -2
  11. package/src/app/app.component.scss +14 -25
  12. package/src/app/app.component.spec.ts +6 -21
  13. package/src/app/app.component.ts +9 -10
  14. package/src/app/app.module.ts +0 -13
  15. package/src/app/component/conversation-detail/conversation/conversation.component.html +11 -25
  16. package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -40
  17. package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +75 -644
  18. package/src/app/component/conversation-detail/conversation/conversation.component.ts +14 -100
  19. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +13 -25
  20. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +5 -123
  21. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +0 -1
  22. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +10 -23
  23. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -19
  24. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +149 -242
  25. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +5 -8
  26. package/src/app/component/conversation-detail/conversation-emojii/conversation-emojii.component.spec.ts +3 -53
  27. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +96 -200
  28. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +6 -211
  29. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +78 -452
  30. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +76 -291
  31. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +53 -113
  32. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.scss +4 -12
  33. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +29 -274
  34. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html +9 -23
  35. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.spec.ts +8 -80
  36. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html +23 -29
  37. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.spec.ts +16 -185
  38. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts +14 -34
  39. package/src/app/component/error-alert/error-alert.component.spec.ts +5 -65
  40. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html +7 -16
  41. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss +0 -21
  42. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.spec.ts +7 -89
  43. package/src/app/component/form/form-builder/form-builder.component.html +1 -1
  44. package/src/app/component/form/form-builder/form-builder.component.spec.ts +21 -163
  45. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.html +4 -8
  46. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.scss +5 -10
  47. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.spec.ts +16 -90
  48. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.ts +0 -26
  49. package/src/app/component/form/inputs/form-label/form-label.component.spec.ts +11 -45
  50. package/src/app/component/form/inputs/form-radio-button/form-radio-button.component.spec.ts +6 -24
  51. package/src/app/component/form/inputs/form-select/form-select.component.spec.ts +5 -14
  52. package/src/app/component/form/inputs/form-text/form-text.component.html +12 -14
  53. package/src/app/component/form/inputs/form-text/form-text.component.scss +1 -11
  54. package/src/app/component/form/inputs/form-text/form-text.component.spec.ts +17 -113
  55. package/src/app/component/form/inputs/form-text/form-text.component.ts +3 -35
  56. package/src/app/component/form/inputs/form-textarea/form-textarea.component.html +11 -13
  57. package/src/app/component/form/inputs/form-textarea/form-textarea.component.scss +5 -6
  58. package/src/app/component/form/inputs/form-textarea/form-textarea.component.spec.ts +13 -149
  59. package/src/app/component/form/inputs/form-textarea/form-textarea.component.ts +0 -26
  60. package/src/app/component/form/prechat-form/prechat-form.component.html +11 -14
  61. package/src/app/component/form/prechat-form/prechat-form.component.spec.ts +10 -102
  62. package/src/app/component/form/prechat-form/prechat-form.component.ts +1 -8
  63. package/src/app/component/home/home.component.html +31 -38
  64. package/src/app/component/home/home.component.scss +2 -4
  65. package/src/app/component/home/home.component.spec.ts +11 -226
  66. package/src/app/component/home-conversations/home-conversations.component.html +26 -30
  67. package/src/app/component/home-conversations/home-conversations.component.scss +0 -3
  68. package/src/app/component/home-conversations/home-conversations.component.spec.ts +36 -212
  69. package/src/app/component/last-message/last-message.component.html +9 -15
  70. package/src/app/component/last-message/last-message.component.scss +2 -16
  71. package/src/app/component/last-message/last-message.component.spec.ts +23 -204
  72. package/src/app/component/last-message/last-message.component.ts +1 -4
  73. package/src/app/component/launcher-button/launcher-button.component.html +13 -8
  74. package/src/app/component/launcher-button/launcher-button.component.spec.ts +8 -104
  75. package/src/app/component/list-all-conversations/list-all-conversations.component.html +17 -12
  76. package/src/app/component/list-all-conversations/list-all-conversations.component.scss +0 -2
  77. package/src/app/component/list-conversations/list-conversations.component.html +22 -22
  78. package/src/app/component/menu-options/menu-options.component.html +20 -30
  79. package/src/app/component/menu-options/menu-options.component.spec.ts +9 -125
  80. package/src/app/component/message/audio/audio.component.html +15 -13
  81. package/src/app/component/message/audio/audio.component.spec.ts +5 -140
  82. package/src/app/component/message/audio/audio.component.ts +5 -1
  83. package/src/app/component/message/avatar/avatar.component.html +2 -2
  84. package/src/app/component/message/avatar/avatar.component.spec.ts +7 -99
  85. package/src/app/component/message/bubble-message/bubble-message.component.html +51 -38
  86. package/src/app/component/message/bubble-message/bubble-message.component.scss +1 -54
  87. package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +57 -154
  88. package/src/app/component/message/bubble-message/bubble-message.component.ts +11 -89
  89. package/src/app/component/message/buttons/action-button/action-button.component.html +4 -3
  90. package/src/app/component/message/buttons/action-button/action-button.component.spec.ts +5 -49
  91. package/src/app/component/message/buttons/link-button/link-button.component.scss +8 -5
  92. package/src/app/component/message/buttons/link-button/link-button.component.spec.ts +5 -50
  93. package/src/app/component/message/buttons/text-button/text-button.component.spec.ts +5 -44
  94. package/src/app/component/message/carousel/carousel.component.html +16 -29
  95. package/src/app/component/message/carousel/carousel.component.scss +8 -20
  96. package/src/app/component/message/carousel/carousel.component.spec.ts +3 -80
  97. package/src/app/component/message/carousel/carousel.component.ts +0 -16
  98. package/src/app/component/message/frame/frame.component.html +4 -9
  99. package/src/app/component/message/frame/frame.component.spec.ts +15 -34
  100. package/src/app/component/message/frame/frame.component.ts +2 -7
  101. package/src/app/component/message/html/html.component.html +1 -1
  102. package/src/app/component/message/html/html.component.scss +1 -1
  103. package/src/app/component/message/html/html.component.spec.ts +7 -24
  104. package/src/app/component/message/image/image.component.html +10 -12
  105. package/src/app/component/message/image/image.component.scss +0 -16
  106. package/src/app/component/message/image/image.component.spec.ts +15 -101
  107. package/src/app/component/message/image/image.component.ts +51 -90
  108. package/src/app/component/message/info-message/info-message.component.spec.ts +14 -26
  109. package/src/app/component/message/like-unlike/like-unlike.component.html +9 -7
  110. package/src/app/component/message/like-unlike/like-unlike.component.spec.ts +3 -31
  111. package/src/app/component/message/return-receipt/return-receipt.component.spec.ts +17 -38
  112. package/src/app/component/message/text/text.component.html +3 -3
  113. package/src/app/component/message/text/text.component.scss +86 -80
  114. package/src/app/component/message/text/text.component.spec.ts +13 -106
  115. package/src/app/component/message-attachment/message-attachment.component.spec.ts +13 -134
  116. package/src/app/component/selection-department/selection-department.component.html +23 -21
  117. package/src/app/component/selection-department/selection-department.component.spec.ts +14 -159
  118. package/src/app/component/selection-department/selection-department.component.ts +1 -8
  119. package/src/app/component/send-button/send-button.component.html +13 -5
  120. package/src/app/component/send-button/send-button.component.spec.ts +2 -2
  121. package/src/app/component/star-rating-widget/star-rating-widget.component.html +81 -51
  122. package/src/app/directives/tooltip.directive.spec.ts +4 -8
  123. package/src/app/modals/confirm-close/confirm-close.component.html +8 -20
  124. package/src/app/modals/confirm-close/confirm-close.component.scss +0 -3
  125. package/src/app/modals/confirm-close/confirm-close.component.spec.ts +4 -13
  126. package/src/app/modals/confirm-close/confirm-close.component.ts +1 -8
  127. package/src/app/pipe/html-entites-encode.pipe.spec.ts +2 -35
  128. package/src/app/pipe/marked.pipe.spec.ts +2 -38
  129. package/src/app/pipe/marked.pipe.ts +41 -51
  130. package/src/app/providers/app-config.service.ts +2 -4
  131. package/src/app/providers/brand.service.spec.ts +2 -23
  132. package/src/app/providers/brand.service.ts +1 -1
  133. package/src/app/providers/global-settings.service.spec.ts +14 -1009
  134. package/src/app/providers/global-settings.service.ts +2 -82
  135. package/src/app/providers/translator.service.ts +6 -26
  136. package/src/app/sass/_variables.scss +0 -3
  137. package/src/app/sass/animations.scss +1 -19
  138. package/src/app/utils/globals.ts +1 -21
  139. package/src/app/utils/utils-resources.ts +1 -1
  140. package/src/assets/i18n/en.json +99 -106
  141. package/src/assets/i18n/es.json +100 -107
  142. package/src/assets/i18n/fr.json +100 -107
  143. package/src/assets/i18n/it.json +98 -107
  144. package/src/assets/twp/index-dev.html +0 -18
  145. package/src/chat21-core/models/message.ts +1 -2
  146. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +2 -3
  147. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +0 -12
  148. package/src/chat21-core/providers/scripts/script.service.spec.ts +2 -12
  149. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
  150. package/src/chat21-core/utils/utils-message.ts +0 -7
  151. package/src/chat21-core/utils/utils.ts +2 -5
  152. package/src/widget-config-template.json +1 -4
  153. package/src/widget-config.json +1 -4
  154. package/tsconfig.json +0 -5
  155. package/.angular-mcp-cache/package.json +0 -1
  156. package/.cursor/angular18-accessibility-auditor-skill.md +0 -442
  157. package/.cursor/mcp.json +0 -15
  158. package/.github/workflows/build.yml +0 -22
  159. package/.github/workflows/playwright.yml +0 -27
  160. package/mocks/voice-websocket-mock/server.cjs +0 -245
  161. package/playwright.config.ts +0 -41
  162. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +0 -46
  163. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +0 -83
  164. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +0 -192
  165. package/src/app/component/form/prechat-form-test-mock.ts +0 -35
  166. package/src/app/component/message/audio-sync/audio-sync.component.html +0 -18
  167. package/src/app/component/message/audio-sync/audio-sync.component.scss +0 -65
  168. package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +0 -103
  169. package/src/app/component/message/audio-sync/audio-sync.component.ts +0 -643
  170. package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +0 -117
  171. package/src/app/providers/tts-audio-playback-coordinator.service.ts +0 -109
  172. package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +0 -12
  173. package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +0 -171
  174. package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +0 -39
  175. package/src/app/providers/voice/audio.types.ts +0 -40
  176. package/src/app/providers/voice/vad.service.spec.ts +0 -28
  177. package/src/app/providers/voice/vad.service.ts +0 -70
  178. package/src/app/providers/voice/voice-streaming.service.spec.ts +0 -23
  179. package/src/app/providers/voice/voice-streaming.service.ts +0 -702
  180. package/src/app/providers/voice/voice-streaming.types.ts +0 -112
  181. package/src/app/providers/voice/voice.service.spec.ts +0 -227
  182. package/src/app/providers/voice/voice.service.ts +0 -973
  183. package/src/app/shims/onnxruntime-web-wasm.ts +0 -4
  184. package/src/assets/onnx/ort-wasm-simd-threaded.mjs +0 -59
  185. package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
  186. package/src/assets/sounds/keyboard.mp3 +0 -0
  187. package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +0 -14
  188. package/src/assets/vad/silero_vad_legacy.onnx +0 -0
  189. package/src/assets/vad/vad.worklet.bundle.min.js +0 -1
  190. package/src/chat21-core/providers/chat-manager.spec.ts +0 -72
  191. package/tests/widget-form-rich.spec.ts +0 -67
  192. package/tests/widget-index-dev-settings.spec.ts +0 -52
  193. package/tests/widget-twp-iframe.spec.ts +0 -39
@@ -161,11 +161,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
161
161
  membersConversation = ['SYSTEM'];
162
162
  // ========== end:: typying =======
163
163
 
164
- // ========== begin:: stream audio ======= //
165
- public isStreamAudioActive = false;
166
- public isStreamAudioConnecting = false;
167
- // ========== end:: stream audio ======= //
168
-
169
164
  @ViewChild(ConversationFooterComponent) conversationFooter: ConversationFooterComponent
170
165
  @ViewChild(ConversationContentComponent) conversationContent: ConversationContentComponent
171
166
  conversationHandlerService: ConversationHandlerService
@@ -251,22 +246,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
251
246
  'CONTINUE',
252
247
  'EMOJI_NOT_ELLOWED',
253
248
  'ATTACHMENT',
254
- 'EMOJI',
255
- 'BUTTON_ATTACH_FILE',
256
- 'BUTTON_SEND_MESSAGE',
257
- 'BUTTON_RECORD_AUDIO',
258
- 'BUTTON_DELETE_AUDIO',
259
- 'BUTTON_SEND_AUDIO',
260
- 'BUTTON_PLAY_AUDIO',
261
- 'BUTTON_PAUSE_AUDIO',
262
- 'SKIP_TO_COMPOSER',
263
- 'CLOSE_CHAT',
264
- 'CLOSE',
265
- 'VOICE_CONNECTING',
266
- 'VOICE_LISTENING',
267
- 'VOICE_PROCESSING',
268
- 'STREAM_AUDIO',
269
- 'MAX_ATTACHMENT'
249
+ 'EMOJI'
270
250
  ];
271
251
 
272
252
  const keysContent = [
@@ -286,21 +266,13 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
286
266
  'LABEL_THINKING',
287
267
  'LABEL_TO',
288
268
  'ARRAY_DAYS',
289
- 'CONVERSATION_LOG_LABEL',
290
- 'BUTTON_SCROLL_TO_BOTTOM',
291
- 'CAROUSEL_PREVIOUS',
292
- 'CAROUSEL_NEXT',
293
- 'CAROUSEL_LABEL',
294
- 'CAROUSEL_SLIDE_LABEL'
295
269
  ];
296
270
 
297
271
  const keysPreview= [
298
272
  'BACK',
299
273
  'CLOSE',
300
274
  'LABEL_PLACEHOLDER',
301
- 'LABEL_PREVIEW',
302
- 'BUTTON_CLOSE_PREVIEW',
303
- 'BUTTON_SEND_MESSAGE'
275
+ 'LABEL_PREVIEW'
304
276
  ];
305
277
 
306
278
  const keysCloseChatDialog= [
@@ -529,29 +501,25 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
529
501
  return this.isConversationArchived;
530
502
  }
531
503
 
532
- // FALLBACK TO TILEDESK
533
- let requests_list: { requests: any[] };
534
- try {
535
- requests_list = await this.tiledeskRequestService.getMyRequests();
536
- } catch (err) {
504
+ //FALLBACK TO TILEDESK
505
+ const requests_list = await this.tiledeskRequestService.getMyRequests().catch(err => {
537
506
  this.logger.error('[CONV-COMP] getConversationDetail: error getting request from Tiledesk', err);
538
- this.isConversationArchived = true;
539
- return this.isConversationArchived;
540
- }
541
-
507
+ this.isConversationArchived=true
508
+ return { requests: [] }
509
+ });
542
510
  if (requests_list && requests_list.requests.length > 0) {
543
511
  this.logger.debug('[CONV-COMP] getConversationDetail: request exist on Tiledesk', requests_list);
544
- const conversation = requests_list.requests.find((request) => request.request_id === this.conversationId);
545
- if (conversation) {
546
- this.isConversationArchived = false;
547
- return this.isConversationArchived;
512
+ let conversation = requests_list.requests.find((request)=> request.request_id === this.conversationId)
513
+ if(conversation){
514
+ this.isConversationArchived = false
515
+ return this.isConversationArchived
548
516
  }
549
517
  this.logger.debug('[CONV-COMP] getConversationDetail: request NOT EXIST on Tiledesk', requests_list);
550
- this.isConversationArchived = true;
551
- return this.isConversationArchived;
518
+ this.isConversationArchived = true
519
+ return this.isConversationArchived
552
520
  }
553
521
 
554
- this.isConversationArchived = false;
522
+ this.isConversationArchived = true;
555
523
  return null;
556
524
  }
557
525
 
@@ -854,10 +822,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
854
822
  this.showThinkingMessage = false;
855
823
  }
856
824
 
857
- if (this.isStreamAudioActive && msg.sender !== this.senderId) {
858
- this.conversationFooter?.interruptStreamDueToPeerMessage();
859
- }
860
-
861
825
  that.newMessageAdded(msg);
862
826
  // Update badge based on the latest message received from the server.
863
827
  // We rely on `messages` being kept in-sync by the conversation handler.
@@ -913,20 +877,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
913
877
  this.subscriptions.push(subscribe);
914
878
  }
915
879
 
916
- subscribtionKey = 'conversationsAdded';
917
- subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
918
- if(!subscribtion){
919
-
920
- subscribtion = this.chatManager.conversationsHandlerService.conversationChanged.pipe(takeUntil(this.unsubscribe$)).subscribe((conversation) => {
921
- this.logger.debug('[CONV-COMP] ***** DATAIL conversationsChanged *****', conversation, this.conversationWith, this.isConversationArchived);
922
- if(conversation && conversation.recipient === this.conversationId){
923
- this.isConversationArchived = false
924
- }
925
- });
926
- const subscribe = {key: subscribtionKey, value: subscribtion };
927
- this.subscriptions.push(subscribe);
928
- }
929
-
930
880
  subscribtionKey = 'messageWait';
931
881
  subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
932
882
  if (!subscribtion) {
@@ -1082,21 +1032,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
1082
1032
 
1083
1033
 
1084
1034
 
1085
- /**
1086
- * Programmatically moves keyboard focus to the message composer textarea.
1087
- * Wired to the visible-on-focus skip link in conversation.component.html (WCAG 2.4.1 Bypass Blocks).
1088
- */
1089
- skipToCompose() {
1090
- try {
1091
- const textarea = document.getElementById('chat21-main-message-context') as HTMLTextAreaElement | null;
1092
- if (textarea) {
1093
- textarea.focus();
1094
- }
1095
- } catch(e) {
1096
- this.logger.warn('[CONV-COMP] skipToCompose error', e);
1097
- }
1098
- }
1099
-
1100
1035
  scrollToBottom() {
1101
1036
  this.conversationContent.scrollToBottom();
1102
1037
  // const that = this;
@@ -1448,29 +1383,8 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
1448
1383
  this.logger.debug('[CONV-COMP] floating onNewConversationButtonClicked')
1449
1384
  this.onNewConversationButtonClicked.emit()
1450
1385
  }
1451
-
1452
- /** CALLED BY: conv-footer streaming audio button */
1453
- onStreamAudioActiveChange(event: boolean){
1454
- this.isStreamAudioActive = event
1455
- }
1456
- /** CALLED BY: conv-footer when connecting state changes */
1457
- onStreamAudioConnectingChange(event: boolean){
1458
- this.isStreamAudioConnecting = event
1459
- }
1460
- /** CALLED BY: conv-footer component */
1461
- onCloseChatButtonClickedFN(event){
1462
- this.logger.debug('[CONV-COMP] onCloseChatButtonClicked::::', event)
1463
- this.onCloseChat()
1464
- }
1465
1386
  // =========== END: event emitter function ====== //
1466
1387
 
1467
- /**
1468
- * True quando è visibile il pulsante chiudi stream (`.close-stream-button`, `isStreamAudioActive`).
1469
- * Solo in quel caso il bottom del foglio include `--chat-footer-stream-button-height`.
1470
- */
1471
- closeStreamButtonActiveForSheetBottom(): boolean {
1472
- return !!(this.g?.showAudioStreamFooterButton && (this.isStreamAudioActive || this.isStreamAudioConnecting));
1473
- }
1474
1388
 
1475
1389
  openInputFiles() {
1476
1390
  alert('ok');
@@ -1,44 +1,32 @@
1
1
  <div class="audio-recorder">
2
- <button *ngIf="audioUrl" type="button" [attr.aria-label]="translationMap?.get('BUTTON_DELETE_AUDIO') || 'Delete recording'" (click)="deleteRecording()">
2
+ <button *ngIf="audioUrl" (click)="deleteRecording()">
3
3
  <span class="v-align-center">
4
- <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
4
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
5
5
  <path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm80-160h80v-360h-80v360Zm160 0h80v-360h-80v360Z"/>
6
6
  </svg>
7
+ <!-- <i class="material-icons">delete_outline</i> -->
7
8
  </span>
8
9
  </button>
9
10
 
10
- <chat-audio class="test" *ngIf="audioBlob && audioUrl"
11
- [audioBlob]="audioBlob"
11
+ <chat-audio class="test" *ngIf="audioBlob && audioUrl"
12
+ [audioBlob] = "audioBlob"
12
13
  [color]="'var(--chat-footer-color)'"
13
- [translationMap]="translationMap"
14
14
  [stylesMap]="stylesMap">
15
15
  </chat-audio>
16
-
17
- <button *ngIf="!audioUrl"
18
- type="button"
19
- class="mic-button"
20
- [attr.aria-label]="translationMap?.get('BUTTON_RECORD_AUDIO') || 'Hold to record an audio message'"
21
- [attr.aria-pressed]="isRecording ? 'true' : 'false'"
22
- (mousedown)="startRecording($event)"
23
- (mouseup)="stopRecording($event)"
24
- (touchstart)="startRecording($event)"
25
- (touchend)="stopRecording($event)"
26
- (keydown.space)="$event.preventDefault(); !isRecording && startRecording($event)"
27
- (keyup.space)="$event.preventDefault(); isRecording && stopRecording($event)">
28
- <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
16
+
17
+ <button *ngIf="!audioUrl" class="mic-button" (mousedown)="startRecording($event)" (mouseup)="stopRecording($event)" (touchstart)="startRecording($event)" (touchend)="stopRecording($event)">
18
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
29
19
  <path d="M480-400q-50 0-85-35t-35-85v-240q0-50 35-85t85-35q50 0 85 35t35 85v240q0 50-35 85t-85 35Zm0-240Zm-40 520v-123q-104-14-172-93t-68-184h80q0 83 58.5 141.5T480-320q83 0 141.5-58.5T680-520h80q0 105-68 184t-172 93v123h-80Zm40-360q17 0 28.5-11.5T520-520v-240q0-17-11.5-28.5T480-800q-17 0-28.5 11.5T440-760v240q0 17 11.5 28.5T480-480Z"/>
30
20
  </svg>
31
21
  </button>
32
-
33
- <button *ngIf="audioUrl"
34
- type="button"
35
- [attr.aria-label]="translationMap?.get('BUTTON_SEND_AUDIO') || 'Send audio message'"
36
- (click)="sendMessage()">
22
+ <!-- <button *ngIf="isRecording" (click)="stopRecording()"><i class="material-icons">pause_circle_outline</i></button> -->
23
+
24
+ <button *ngIf="audioUrl" (click)="sendMessage()">
37
25
  <span class="v-align-center">
38
- <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="20" width="24" viewBox="0 0 24 20" xml:space="preserve">
26
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="20" width="24" viewBox="0 0 24 20" xml:space="preserve">
39
27
  <path d="M1.8,18.9V1.7L22,10.3L1.8,18.9z M3.9,15.6l12.6-5.4L3.9,4.9v3.7l6.4,1.6l-6.4,1.6V15.6z M3.9,15.6V4.9v7V15.6z"/>
40
28
  </svg>
41
29
  </span>
42
30
  </button>
43
31
 
44
- </div>
32
+ </div>
@@ -1,141 +1,23 @@
1
- import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
2
 
3
3
  import { ConversationAudioRecorderComponent } from './conversation-audio-recorder.component';
4
4
 
5
- describe('ConversationAudioRecorderComponent', () => {
5
+ describe('AudioRecorderComponent', () => {
6
6
  let component: ConversationAudioRecorderComponent;
7
7
  let fixture: ComponentFixture<ConversationAudioRecorderComponent>;
8
- let stopListeners: { stop?: () => void; data?: (e: { data: Blob }) => void };
9
- let mediaRecorderInstance: {
10
- start: jasmine.Spy;
11
- stop: jasmine.Spy;
12
- mimeType: string;
13
- addEventListener: jasmine.Spy;
14
- };
15
8
 
16
9
  beforeEach(async () => {
17
- stopListeners = {};
18
- mediaRecorderInstance = {
19
- start: jasmine.createSpy('start'),
20
- stop: jasmine.createSpy('stop').and.callFake(() => {
21
- const fn = stopListeners.stop;
22
- if (fn) {
23
- fn();
24
- }
25
- }),
26
- mimeType: 'audio/webm',
27
- addEventListener: jasmine.createSpy('addEventListener').and.callFake((ev: string, fn: any) => {
28
- if (ev === 'stop') {
29
- stopListeners.stop = fn;
30
- }
31
- if (ev === 'dataavailable') {
32
- stopListeners.data = fn;
33
- }
34
- }),
35
- };
36
-
37
- const stream = {
38
- getTracks: () => [{ stop: jasmine.createSpy('trackStop') }],
39
- };
40
-
41
- spyOn(window.navigator.mediaDevices, 'getUserMedia').and.returnValue(Promise.resolve(stream as any));
42
- (window as any).MediaRecorder = jasmine.createSpy('MediaRecorder').and.returnValue(mediaRecorderInstance);
43
-
44
10
  await TestBed.configureTestingModule({
45
- declarations: [ConversationAudioRecorderComponent],
46
- }).compileComponents();
11
+ declarations: [ ConversationAudioRecorderComponent ]
12
+ })
13
+ .compileComponents();
47
14
 
48
15
  fixture = TestBed.createComponent(ConversationAudioRecorderComponent);
49
16
  component = fixture.componentInstance;
50
- component.translationMap = new Map();
51
- component.stylesMap = new Map();
52
- spyOn(component.startRecordingEvent, 'emit');
53
- spyOn(component.endRecordingEvent, 'emit');
54
17
  fixture.detectChanges();
55
18
  });
56
19
 
57
20
  it('should create', () => {
58
21
  expect(component).toBeTruthy();
59
22
  });
60
-
61
- describe('startRecording', () => {
62
- it('should preventDefault on touchstart', fakeAsync(() => {
63
- const ev = { type: 'touchstart', preventDefault: jasmine.createSpy('pd') } as any;
64
- component.startRecording(ev);
65
- tick();
66
- expect(ev.preventDefault).toHaveBeenCalled();
67
- expect(component.startRecordingEvent.emit).toHaveBeenCalled();
68
- }));
69
-
70
- it('should request microphone and start MediaRecorder on mousedown', fakeAsync(() => {
71
- const ev = new MouseEvent('mousedown');
72
- component.startRecording(ev);
73
- tick();
74
- expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith({ audio: true });
75
- expect(mediaRecorderInstance.start).toHaveBeenCalled();
76
- expect(component.isRecording).toBe(true);
77
- }));
78
-
79
- it('should log when getUserMedia fails', fakeAsync(() => {
80
- (navigator.mediaDevices.getUserMedia as jasmine.Spy).and.returnValue(Promise.reject(new Error('denied')));
81
- spyOn(console, 'error');
82
- component.startRecording(new MouseEvent('mousedown'));
83
- tick();
84
- expect(console.error).toHaveBeenCalled();
85
- }));
86
- });
87
-
88
- describe('stopRecording', () => {
89
- it('should discard very short press without stopping recorder', fakeAsync(() => {
90
- component.startTime = Date.now();
91
- component.stopRecording(new MouseEvent('mouseup'));
92
- tick(400);
93
- expect(mediaRecorderInstance.stop).not.toHaveBeenCalled();
94
- }));
95
-
96
- it('should stop recorder after long press', fakeAsync(() => {
97
- component.mediaRecorder = mediaRecorderInstance as any;
98
- component.startTime = Date.now() - 600;
99
- component.stopRecording(new MouseEvent('mouseup'));
100
- tick(400);
101
- expect(mediaRecorderInstance.stop).toHaveBeenCalled();
102
- }));
103
-
104
- it('should preventDefault on touchend', () => {
105
- const ev = { type: 'touchend', preventDefault: jasmine.createSpy('pd') } as any;
106
- component.stopRecording(ev);
107
- expect(ev.preventDefault).toHaveBeenCalled();
108
- });
109
- });
110
-
111
- describe('deleteRecording', () => {
112
- it('should reset state and emit', () => {
113
- spyOn(component.deleteRecordingEvent, 'emit');
114
- component.audioUrl = {} as any;
115
- component.audioBlob = new Blob();
116
- component.deleteRecording();
117
- expect(component.audioUrl).toBeNull();
118
- expect(component.audioBlob).toBeNull();
119
- expect(component.deleteRecordingEvent.emit).toHaveBeenCalledWith(null);
120
- });
121
- });
122
-
123
- describe('sendMessage', () => {
124
- it('should emit blob and clear url when recording exists', () => {
125
- spyOn(component.sendRecordingEvent, 'emit');
126
- const b = new Blob(['a'], { type: 'audio/webm' });
127
- component.audioBlob = b;
128
- component.audioUrl = {} as any;
129
- component.sendMessage();
130
- expect(component.sendRecordingEvent.emit).toHaveBeenCalledWith(b);
131
- expect(component.audioUrl).toBeNull();
132
- });
133
-
134
- it('should no-op when there is no audioUrl', () => {
135
- spyOn(component.sendRecordingEvent, 'emit');
136
- component.audioUrl = null;
137
- component.sendMessage();
138
- expect(component.sendRecordingEvent.emit).not.toHaveBeenCalled();
139
- });
140
- });
141
23
  });
@@ -9,7 +9,6 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
9
9
  export class ConversationAudioRecorderComponent {
10
10
 
11
11
  @Input() stylesMap: Map<string, string>;
12
- @Input() translationMap: Map<string, string>;
13
12
  @Output() startRecordingEvent = new EventEmitter<void>();
14
13
  @Output() deleteRecordingEvent = new EventEmitter<void>();
15
14
  @Output() endRecordingEvent = new EventEmitter<Blob | null>();
@@ -2,12 +2,7 @@
2
2
 
3
3
  <div class="c21-body-container">
4
4
 
5
- <div class="c21-body-content"
6
- role="log"
7
- aria-live="polite"
8
- aria-relevant="additions text"
9
- aria-atomic="false"
10
- [attr.aria-label]="translationMap?.get('CONVERSATION_LOG_LABEL') || 'Conversation messages'">
5
+ <div class="c21-body-content" tabindex="1520" aria-label=" messaggi della conversazione: ">
11
6
 
12
7
  <!-- USER TYPING (WAIT MESSAGE) -->
13
8
  <span *ngIf="messages && this.messages.length === 0 && !isTypings">
@@ -24,22 +19,21 @@
24
19
  <div #scrollMe id="scroll-me" (scroll)="onScroll($event)">
25
20
 
26
21
  <div id="{{idDivScroll}}" class="c21-contentScroll" > <!-- (resized)="onResized($event)" -->
27
- <div *ngFor="let message of messages; let first = first; let last = last; let i = index" class="rowMsg">
28
-
22
+ <div *ngFor="let message of messages; let first = first; let last = last; let i = index" tabindex="1521" class="rowMsg">
23
+
29
24
  <!-- message SENDER:: -->
30
- <div role="article" *ngIf="messageType(MESSAGE_TYPE_MINE, message) && !message.isJustRecived" class="msg_container base_sent">
25
+ <div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_MINE, message)" class="msg_container base_sent">
31
26
 
32
27
  <!--backgroundColor non viene ancora usato -->
33
28
  <!-- class="messages msg_sent slide-in-right" -->
34
29
  <chat-bubble-message class="messages msg_sent"
35
- [class.no-background]="(isImage(message) || isFrame(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
30
+ [class.no-background]="(isImage(message) || isFrame(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
36
31
  [class.emoticon]="isEmojii(message?.text)"
37
32
  [ngStyle]="{'background': stylesMap.get('bubbleSentBackground'), 'color': stylesMap.get('bubbleSentTextColor')}"
38
33
  [ngClass]="{'button-in-msg' : message?.metadata && message?.metadata?.button}"
39
34
  [message]="message"
40
35
  [fontColor]="stylesMap.get('bubbleSentTextColor')"
41
36
  [stylesMap]="stylesMap"
42
- [translationMap]="translationMap"
43
37
  (onBeforeMessageRender)="onBeforeMessageRenderFN($event)"
44
38
  (onAfterMessageRender)="onAfterMessageRenderFN($event)"
45
39
  (onElementRendered)="onElementRenderedFN($event)">
@@ -53,9 +47,9 @@
53
47
  </div>
54
48
 
55
49
  <!-- message RECIPIENT:: -->
56
- <div role="article" *ngIf="messageType(MESSAGE_TYPE_OTHERS, message)" class="msg_container base_receive">
50
+ <div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_OTHERS, message)" class="msg_container base_receive">
57
51
 
58
- <chat-avatar-image *ngIf="!isSameSender(message?.sender, i) && !isStreamAudioActive"
52
+ <chat-avatar-image *ngIf="!isSameSender(message?.sender, i)"
59
53
  [ngClass]="{'slide-in-left': false}"
60
54
  [senderID]="message?.sender"
61
55
  [senderFullname]="message?.sender_fullname"
@@ -66,17 +60,14 @@
66
60
  <!-- [ngClass]="{'slide-in-left': !isFirstMessage(message?.sender, i)}" -->
67
61
  <chat-bubble-message class="messages msg_receive"
68
62
  [ngClass]="{'slide-in-left': false}"
69
- [class.no-background]="(isImage(message) || isFrame(message) || isCarousel(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
63
+ [class.no-background]="(isImage(message) || isFrame(message) || isCarousel(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
70
64
  [class.emoticon]="isEmojii(message?.text)"
71
- [class.fullSizeMessage]="isStreamAudioActive"
72
- [style.margin-left]="isSameSender(message?.sender, i) ? 'calc(var(--avatar-width) + 10px)' : null"
65
+ [style.margin-left]="isSameSender(message?.sender, i)? 'calc(var(--avatar-width) + 10px)': null"
73
66
  [ngStyle]="{'background': stylesMap.get('bubbleReceivedBackground'), 'color': stylesMap.get('bubbleReceivedTextColor'), 'width':isFrame(message) ?'100%' : null}"
74
67
  [isSameSender]="isSameSender(message?.sender, i)"
75
68
  [message]="message"
76
69
  [fontColor]="stylesMap.get('bubbleReceivedTextColor')"
77
70
  [stylesMap]="stylesMap"
78
- [translationMap]="translationMap"
79
- [streamOnArrival]="false"
80
71
  (onBeforeMessageRender)="onBeforeMessageRenderFN($event)"
81
72
  (onAfterMessageRender)="onAfterMessageRenderFN($event)"
82
73
  (onElementRendered)="onElementRenderedFN($event)">
@@ -119,7 +110,6 @@
119
110
  [isConversationArchived]="isConversationArchived"
120
111
  [isLastMessage] = "isLastMessage(message?.uid)"
121
112
  [stylesMap]="stylesMap"
122
- [translationMap]="translationMap"
123
113
  (onElementRendered)="onElementRenderedFN($event)"
124
114
  (onAttachmentButtonClicked)="onAttachmentButtonClickedFN($event)">
125
115
  </chat-carousel>
@@ -144,10 +134,9 @@
144
134
  [senderFullname]="nameUserTypingNow"
145
135
  [baseLocation]="baseLocation">
146
136
  </chat-avatar-image>
147
-
148
137
  <user-typing
149
- [color]="stylesMap?.get('iconColor')"
150
138
  [ngClass]="{'userTypingNowExist': !idUserTypingNow}"
139
+ [color]="stylesMap?.get('iconColor')"
151
140
  [translationMap]="translationMap"
152
141
  [idUserTypingNow]="idUserTypingNow"
153
142
  [nameUserTypingNow]="nameUserTypingNow">
@@ -156,9 +145,7 @@
156
145
 
157
146
  <div *ngIf="showThinkingMessage && lastServerSenderKind === 'bot'" class="msg_container base_receive thinking_receive">
158
147
  <user-typing class="loading thinking-dots"
159
- [class.fullSize]="isStreamAudioActive"
160
148
  [color]="stylesMap?.get('iconColor')"
161
- [class.fullSize]="isStreamAudioActive"
162
149
  [translationMap]="translationMap"
163
150
  [idUserTypingNow]="idUserTypingNow"
164
151
  [nameUserTypingNow]="nameUserTypingNow">
@@ -27,10 +27,6 @@
27
27
  margin: 25px 50px
28
28
  }
29
29
 
30
- :host .loading.fullSize ::ng-deep > div.spinner{
31
- margin: 15px 0px !important;
32
- }
33
-
34
30
  // ============= CSS c21-body ================= //
35
31
  .c21-body {
36
32
  // -webkit-box-shadow: inset 0 10px 10px -10px rgba(0,0,0,0.4);
@@ -48,7 +44,7 @@
48
44
  top: 0;
49
45
  right: 0;
50
46
  left: 0;
51
- bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height));
47
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
52
48
  overflow: hidden;
53
49
  .time{
54
50
  margin-bottom: 20px;
@@ -246,11 +242,6 @@
246
242
  height: fit-content;
247
243
  width: auto;
248
244
 
249
- &.fullSizeMessage {
250
- max-width: 100%;
251
- margin: auto 0 auto 0 !important;
252
- }
253
-
254
245
  }
255
246
  .msg_receive.json-resources{
256
247
  min-height: unset;
@@ -292,15 +283,6 @@
292
283
  }// end c21-body-container
293
284
  }// end c21-body
294
285
 
295
- /* Solo con pulsante chiudi stream (stream in ascolto): altezza extra come #streamAudioAlert */
296
- :host-context(#chat21-conversation-component.chat21-conversation--close-stream-active) .c21-body .c21-body-container .c21-body-content .chat21-sheet-content {
297
- bottom: calc(
298
- var(--chat-footer-logo-height) +
299
- var(--chat-footer-height) +
300
- var(--chat-footer-stream-button-height)
301
- );
302
- }
303
-
304
286
  @keyframes thinking-dot {
305
287
  0%, 80%, 100% {
306
288
  opacity: 0.2;