@chat21/chat21-web-widget 5.0.82 → 5.0.83-rc.2

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 (21) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +1 -1
  3. package/src/app/app.module.ts +7 -3
  4. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +29 -0
  5. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.scss +103 -0
  6. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +23 -0
  7. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +96 -0
  8. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +14 -3
  9. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +26 -10
  10. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +131 -49
  11. package/src/app/component/message/audio-track/audio-track.component.html +32 -0
  12. package/src/app/component/message/audio-track/audio-track.component.scss +107 -0
  13. package/src/app/component/message/{audio/audio.component.spec.ts → audio-track/audio-track.component.spec.ts} +6 -6
  14. package/src/app/component/message/audio-track/audio-track.component.ts +147 -0
  15. package/src/app/component/message/bubble-message/bubble-message.component.html +30 -5
  16. package/src/app/component/message/bubble-message/bubble-message.component.scss +7 -0
  17. package/src/app/component/message/bubble-message/bubble-message.component.ts +1 -0
  18. package/src/app/utils/globals.ts +1 -1
  19. package/src/app/component/message/audio/audio.component.html +0 -20
  20. package/src/app/component/message/audio/audio.component.scss +0 -122
  21. package/src/app/component/message/audio/audio.component.ts +0 -122
package/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@
6
6
  ### **Copyrigth**:
7
7
  *Tiledesk SRL*
8
8
 
9
+ # 5.0.83-rc.2
10
+ - **changed**: ui improvements on audio elements
11
+
12
+ # 5.0.83-rc.1
13
+ - **added**: ability to send and receive voice message into the conversation
14
+
9
15
  # 5.0.82 in PROD
10
16
 
11
17
  # 5.0.82-rc.4
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-web-widget",
3
3
  "author": "Tiledesk SRL",
4
- "version": "5.0.82",
4
+ "version": "5.0.83-rc.2",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.tiledesk.com",
7
7
  "repository": {
@@ -15,6 +15,7 @@ import { ConversationContentComponent } from './component/conversation-detail/co
15
15
  import { ConversationFooterComponent } from './component/conversation-detail/conversation-footer/conversation-footer.component';
16
16
  import { ConversationInternalFrameComponent } from './component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component';
17
17
  import { ConversationPreviewComponent } from './component/conversation-detail/conversation-preview/conversation-preview.component';
18
+ import { ConversationAudioRecorderComponent } from './component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component';
18
19
  /** CONVERSATION-DETAIL COMPONENTS */
19
20
  import { BubbleMessageComponent } from './component/message/bubble-message/bubble-message.component';
20
21
  import { AvatarComponent } from './component/message/avatar/avatar.component';
@@ -23,7 +24,7 @@ import { ImageComponent } from './component/message/image/image.component';
23
24
  import { InfoMessageComponent } from './component/message/info-message/info-message.component';
24
25
  import { HtmlComponent } from './component/message/html/html.component';
25
26
  import { FrameComponent } from './component/message/frame/frame.component';
26
- import { AudioComponent } from './component/message/audio/audio.component';
27
+ import { AudioTrackComponent } from './component/message/audio-track/audio-track.component';
27
28
  import { UserTypingComponent } from './../chat21-core/utils/user-typing/user-typing.component';
28
29
  /** MESSAGE ATTACHMENTS COMPONENTS */
29
30
  import { MessageAttachmentComponent } from './component/message-attachment/message-attachment.component';
@@ -137,6 +138,7 @@ import { NetworkOfflineComponent } from './component/network-offline/network-off
137
138
 
138
139
 
139
140
 
141
+
140
142
  const appInitializerFn = (appConfig: AppConfigService, brandService: BrandService, logger: NGXLogger) => {
141
143
  return async() => {
142
144
  let customLogger = new CustomLogger(logger)
@@ -275,6 +277,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
275
277
  ConversationFooterComponent,
276
278
  ConversationPreviewComponent,
277
279
  ConversationInternalFrameComponent,
280
+ ConversationAudioRecorderComponent,
278
281
  BubbleMessageComponent,
279
282
  AvatarComponent,
280
283
  FrameComponent,
@@ -286,6 +289,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
286
289
  ActionButtonComponent,
287
290
  LinkButtonComponent,
288
291
  TextButtonComponent,
292
+ AudioTrackComponent,
289
293
  UserTypingComponent,
290
294
  /**DIRECTIVES */
291
295
  HtmlEntitiesEncodePipe,
@@ -294,9 +298,9 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
294
298
  SafeHtmlPipe,
295
299
  LikeUnlikeComponent,
296
300
  TooltipDirective,
297
- AudioComponent,
298
301
  CarouselComponent,
299
- NetworkOfflineComponent
302
+ NetworkOfflineComponent,
303
+
300
304
  ],
301
305
  imports: [
302
306
  BrowserModule,
@@ -0,0 +1,29 @@
1
+ <div class="audio-recorder">
2
+ <button *ngIf="audioUrl" (click)="deleteRecording()">
3
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"><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"/></svg>
4
+ <!-- <i class="material-icons">delete_outline</i> -->
5
+ </button>
6
+
7
+ <chat-audio-track *ngIf="audioBlob && audioUrl"
8
+ [audioBlob] = "audioBlob"
9
+ [color]="stylesMap.get('themeColor')"
10
+ [fontSize]="stylesMap.get('fontSize')"
11
+ [stylesMap]="stylesMap">
12
+ </chat-audio-track>
13
+
14
+ <button *ngIf="!audioUrl" class="mic-button" (mousedown)="startRecording()" (mouseup)="stopRecording()">
15
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
16
+ <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"/>
17
+ </svg>
18
+ </button>
19
+ <!-- <button *ngIf="isRecording" (click)="stopRecording()"><i class="material-icons">pause_circle_outline</i></button> -->
20
+
21
+ <button *ngIf="audioUrl" (click)="sendMessage()">
22
+ <span class="v-align-center">
23
+ <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">
24
+ <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"/>
25
+ </svg>
26
+ </span>
27
+ </button>
28
+
29
+ </div>
@@ -0,0 +1,103 @@
1
+
2
+ @font-face {
3
+ font-family: 'Material Icons';
4
+ src: url('https://fonts.googleapis.com/icon?family=Material+Icons');
5
+ }
6
+
7
+ .material-icons.mic_none::before {
8
+ content: 'mic_none';
9
+ }
10
+
11
+ .audio-recorder {
12
+ text-align: center;
13
+ margin: 0px;
14
+ display: inline-flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ height: 100%;
18
+ width: 100%;
19
+ float: left;
20
+ gap: 10px
21
+ }
22
+
23
+ chat-audio-track {
24
+ display: flex;
25
+ width: 70%;
26
+ margin: 8px 0;
27
+ pointer-events: auto;
28
+ border-radius: var(--chat-footer-border-radius);
29
+ background-color: var(--chat-footer-background-color);
30
+ }
31
+
32
+ button {
33
+ margin: 0px;
34
+ padding: 0px;
35
+ font-size: 16px;
36
+ border: none;
37
+ background-color: transparent;
38
+ color: var(--icon-fill-color);
39
+ fill: var(--icon-fill-color);
40
+ height: var(--chat-footer-height);
41
+ }
42
+
43
+ .mic-button {
44
+ position: relative;
45
+ background-color: transparent;
46
+ border: none;
47
+ outline: none;
48
+ cursor: pointer;
49
+ padding: 20px;
50
+ border-radius: 50%;
51
+ overflow: hidden;
52
+ display: inline-flex;
53
+ justify-content: center;
54
+ align-items: center;
55
+ }
56
+
57
+ .mic-button svg {
58
+ width: 24px;
59
+ height: 24px;
60
+ fill: var(--icon-fill-color);
61
+ }
62
+
63
+ .mic-button::before {
64
+ content: '';
65
+ position: absolute;
66
+ top: auto;
67
+ left: auto;
68
+ width: 35px;
69
+ height: 35px;
70
+ background-color: var(--icon-fill-color);
71
+ border-radius: 50%;
72
+ transform: scale(0);
73
+ transition: transform 0.3s ease;
74
+ transform-origin: center;
75
+ z-index: -1;
76
+ }
77
+
78
+ .mic-button:active::before {
79
+ transform: scale(0.5);
80
+ animation: pulse 1s infinite ease-in-out;
81
+ }
82
+
83
+ .mic-button:active {
84
+ svg {
85
+ color: #fff;
86
+ fill: #fff;
87
+ }
88
+ }
89
+
90
+ @keyframes pulse {
91
+ 0% {
92
+ transform: scale(1);
93
+ opacity: 1;
94
+ }
95
+ 50% {
96
+ transform: scale(0.9);
97
+ opacity: 0.7;
98
+ }
99
+ 100% {
100
+ transform: scale(1);
101
+ opacity: 1;
102
+ }
103
+ }
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { ConversationAudioRecorderComponent } from './conversation-audio-recorder.component';
4
+
5
+ describe('AudioRecorderComponent', () => {
6
+ let component: ConversationAudioRecorderComponent;
7
+ let fixture: ComponentFixture<ConversationAudioRecorderComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ declarations: [ ConversationAudioRecorderComponent ]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(ConversationAudioRecorderComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,96 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
3
+
4
+ @Component({
5
+ selector: 'chat-audio-recorder',
6
+ templateUrl: './conversation-audio-recorder.component.html',
7
+ styleUrls: ['./conversation-audio-recorder.component.scss']
8
+ })
9
+ export class ConversationAudioRecorderComponent {
10
+
11
+ @Input() stylesMap: Map<string, string>;
12
+ @Output() startRecordingEvent = new EventEmitter<void>();
13
+ @Output() deleteRecordingEvent = new EventEmitter<void>();
14
+ @Output() endRecordingEvent = new EventEmitter<Blob | null>();
15
+ @Output() sendRecordingEvent = new EventEmitter<Blob | null>();
16
+
17
+ mediaRecorder: MediaRecorder | null = null;
18
+ audioChunks: Blob[] = [];
19
+ audioBlob: Blob | null = null;
20
+ audioUrl: SafeUrl | null = null;
21
+
22
+ isRecording = false;
23
+ rawAudioUrl: string | null = null;
24
+ isPlaying: boolean = false;
25
+ startTime: number;
26
+
27
+
28
+
29
+ constructor(private sanitizer: DomSanitizer) {}
30
+
31
+
32
+ startRecording() {
33
+ // console.log('startRecording');
34
+ this.startTime = Date.now();
35
+ navigator.mediaDevices.getUserMedia({ audio: true })
36
+ .then(stream => {
37
+ this.mediaRecorder = new MediaRecorder(stream);
38
+ this.mediaRecorder.start();
39
+ this.isRecording = true;
40
+ this.startRecordingEvent.emit();
41
+ this.mediaRecorder.addEventListener('dataavailable', (event) => {
42
+ this.audioChunks.push(event.data);
43
+ });
44
+
45
+ this.mediaRecorder.addEventListener('stop', () => {
46
+ this.audioBlob = new Blob(this.audioChunks, { type: 'audio/mpeg' });
47
+ this.rawAudioUrl = URL.createObjectURL(this.audioBlob);
48
+ this.audioUrl = this.sanitizer.bypassSecurityTrustUrl(this.rawAudioUrl);
49
+ this.audioChunks = [];
50
+ this.endRecordingEvent.emit(this.audioBlob);
51
+ });
52
+ })
53
+ .catch(error => {
54
+ console.error('Errore nell’accesso al microfono:', error);
55
+ });
56
+ }
57
+
58
+ stopRecording() {
59
+ let endTime = Date.now();
60
+ let time = endTime - this.startTime;
61
+ if(time > 500){
62
+ setTimeout(() => {
63
+ if (this.mediaRecorder) {
64
+ this.mediaRecorder.stop();
65
+ this.isRecording = false;
66
+ }
67
+ }, 300);
68
+
69
+ } else {
70
+ this.audioUrl = null;
71
+ this.isPlaying = false;
72
+ this.rawAudioUrl = null;
73
+ this.audioUrl = null;
74
+ this.audioBlob = null;
75
+ }
76
+
77
+
78
+ }
79
+
80
+ deleteRecording() {
81
+ this.audioUrl = null;
82
+ this.isPlaying = false;
83
+ this.rawAudioUrl = null;
84
+ this.audioUrl = null;
85
+ this.audioBlob = null;
86
+ this.deleteRecordingEvent.emit(null);
87
+ }
88
+
89
+ sendMessage(){
90
+ if (this.audioUrl) {
91
+ this.sendRecordingEvent.emit(this.audioBlob);
92
+ this.audioUrl = null;
93
+ }
94
+ }
95
+
96
+ }
@@ -7,7 +7,7 @@
7
7
  <!-- TEXTAREA + ICONS: conv active-->
8
8
  <div class="textarea-container" *ngIf="!hideTextAreaContent && !hideTextReply">
9
9
 
10
- <div class="icons-container">
10
+ <div *ngIf="!isStopRec" class="icons-container">
11
11
  <!-- ICON ATTACHMENT -->
12
12
  <label *ngIf="showAttachmentButton == true" tabindex="1502" aria-label="allegati" for="chat21-file" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-start-upload-doc">
13
13
  <span class="v-align-center">
@@ -44,7 +44,7 @@
44
44
 
45
45
 
46
46
 
47
- <div class="visible-text-area" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
47
+ <div *ngIf="!isStopRec" class="visible-text-area" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
48
48
  <!-- isFilePendingToUpload || -->
49
49
  <textarea
50
50
  [attr.disabled] = "(hideTextReply)? true : null"
@@ -67,13 +67,24 @@
67
67
  </div>
68
68
 
69
69
  <!-- ICON SEND -->
70
- <div tabindex="-1" class="chat21-textarea-button" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
70
+ <div *ngIf="textInputTextArea && !isStopRec" tabindex="-1" class="chat21-textarea-button" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
71
71
  <span class="v-align-center">
72
72
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="24" width="24" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
73
73
  <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"/>
74
74
  </svg>
75
75
  </span>
76
76
  </div>
77
+
78
+ <!-- ICON REC -->
79
+ <div *ngIf="!textInputTextArea" tabindex="-1" class="chat21-audio-button" [class.active]="isStopRec" id="chat21-button-rec">
80
+ <chat-audio-recorder
81
+ (startRecordingEvent)="onStartRecording()"
82
+ (deleteRecordingEvent)="onDeleteRecording()"
83
+ (endRecordingEvent)="onEndRecording($event)"
84
+ (sendRecordingEvent)="onSendRecording($event)"
85
+ [stylesMap]="stylesMap">
86
+ </chat-audio-recorder>
87
+ </div>
77
88
  </div>
78
89
 
79
90
 
@@ -7,14 +7,16 @@
7
7
  display: flex;
8
8
  width: 100%;
9
9
  align-items: center;
10
- gap: 10px;
10
+ gap: 0px;
11
11
  justify-content: space-between;
12
12
  }
13
13
 
14
14
  .icons-container{
15
15
  display: flex;
16
16
  align-self: flex-end;
17
- margin-left: 8px
17
+ margin-left: 8px;
18
+ gap: 2px;
19
+ margin-right: 8px;
18
20
  }
19
21
 
20
22
  .visible-text-area {
@@ -31,7 +33,6 @@
31
33
  .chat21-textarea-button {
32
34
  fill: var(--icon-fill-color);
33
35
  pointer-events: auto;
34
-
35
36
  height: var(--chat-footer-height);
36
37
  bottom: 22px;
37
38
  opacity: 0.3;
@@ -185,11 +186,29 @@ textarea:active{
185
186
  }
186
187
 
187
188
  #chat21-button-send {
188
- // right: 8px;
189
- // bottom: 0;
190
189
  display: flex;
191
190
  align-self: flex-end;
192
- margin: 0 8px
191
+ margin: 0 8px;
192
+ width: 34px;
193
+ text-align: center;
194
+ justify-content: center;
195
+ }
196
+
197
+ #chat21-button-rec {
198
+ display: flex;
199
+ width: 34px;
200
+ text-align: center;
201
+ justify-content: center;
202
+ height: var(--chat-footer-height);
203
+ &.active{
204
+ width: 100%;
205
+ }
206
+ chat-audio-recorder {
207
+ width: 100%;
208
+ display: flex;
209
+ align-items: center;
210
+ justify-content: center;
211
+ }
193
212
  }
194
213
 
195
214
  #chat21-file{
@@ -337,7 +356,4 @@ textarea:active{
337
356
  // left: 10px;
338
357
  border: none;
339
358
  margin: -2px -2px 0px;
340
-
341
- }
342
-
343
-
359
+ }