@chat21/chat21-web-widget 5.0.67 → 5.0.69-rc.1

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # chat21-web-widget ver 5.0
2
2
 
3
+ ### 5.0.69-rc.1
4
+ - added: display/dispose widget on mobile/desktop behavior
5
+ - added: open/close widget on page change behavior
6
+ - added: 'redirect' action link on message type 'redirect'
7
+
8
+ ### 5.0.68 in PROD
9
+ - bug-fixed: metadata.includes is not a function at isAudio function
10
+
3
11
  ### 5.0.67 in PROD
4
12
  - bug-fixed: when refresh the page and an already open conversation is selected, footer is blocked
5
13
  - bug-fix: pdf preview image is not contained into its container
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.67",
4
+ "version": "5.0.69-rc.1",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.tiledesk.com",
7
7
  "repository": {
@@ -258,8 +258,26 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
258
258
  this.logger.setLoggerConfig(this.g.isLogEnabled, this.g.logLevel)
259
259
  this.tabTitle = this.g.windowContext.window.document.title
260
260
  this.appStorageService.initialize(environment.storage_prefix, this.g.persistence, this.g.projectid)
261
- this.logger.debug('[APP-COMP] check if token is passed throw url: ', this.g.jwt);
261
+
262
+ //set visibility
263
+ if((this.g.isMobile && !this.g.displayOnMobile) || (!this.g.isMobile && !this.g.displayOnDesktop)){
264
+ this.disposeWidget()
265
+ return;
266
+ }
267
+ //set status (open /close)
268
+ if(this.g.isMobile && this.g.onPageChangeVisibilityMobile !== 'last'){
269
+ let isOpen = this.g.onPageChangeVisibilityMobile === 'open'? true: false
270
+ this.g.setIsOpen(isOpen)
271
+ this.appStorageService.setItem('isOpen', isOpen)
272
+ }
273
+ if(!this.g.isMobile && this.g.onPageChangeVisibilityDesktop !== 'last'){
274
+ let isOpen = this.g.onPageChangeVisibilityDesktop === 'open'? true: false
275
+ this.g.setIsOpen(isOpen)
276
+ this.appStorageService.setItem('isOpen', isOpen)
277
+ }
278
+
262
279
  /**CHECK IF JWT IS IN URL PARAMETERS */
280
+ this.logger.debug('[APP-COMP] check if token is passed throw url: ', this.g.jwt);
263
281
  if (this.g.jwt) {
264
282
  // logging in with custom token from url
265
283
  // add JWY token to localstorage and authenticate with it this.logger.debug('[APP-COMP] token from url. isShown:', this.g.isShown, 'autostart:', this.g.autoStart)
@@ -488,40 +506,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
488
506
  this.logger.debug('[APP-COMP] ---------------- 13 ---------------- ');
489
507
  this.logger.debug('[APP-COMP] ----------- sono già loggato ------- ');
490
508
  this.signInWithCustomToken(tiledeskToken)
491
- // this.tiledeskAuthService.signInWithCustomToken(tiledeskToken).then(user => {
492
- // this.messagingAuthService.createCustomToken(tiledeskToken)
493
- // }).catch(error => { console.error('SIGNINWITHCUSTOMTOKEN error::' + error) })
494
-
495
-
496
- // const currentUser = this.authService2.getCurrentUser();
497
- // this.g.senderId = currentUser.uid;
498
- // this.g.setParameter('senderId', currentUser.uid);
499
-
500
- // const fullName = currentUser.firstname + ' ' + currentUser.lastname;
501
- // this.g.setParameter('userFullname', fullName);
502
- // this.g.setAttributeParameter('userFullname', fullName);
503
- // this.g.setParameter('userEmail', currentUser.email);
504
- // this.g.setAttributeParameter('userEmail', currentUser.email);
505
-
506
- // // if(currentUser.firstname || currentUser.lastname){
507
- // // this.g.wdLog([' ---------------- 13 fullname ---------------- ']);
508
- // // const fullName = currentUser.firstname + ' ' + currentUser.lastname;
509
- // // this.g.setParameter('userFullname', fullName);
510
- // // this.g.setAttributeParameter('userFullname', fullName);
511
- // // }
512
- // // if(currentUser.email){
513
- // // this.g.wdLog([' ---------------- 13 email ---------------- ']);
514
- // // this.g.setParameter('userEmail', currentUser.email);
515
- // // this.g.setAttributeParameter('userEmail', currentUser.email);
516
- // // }
517
-
518
- // // this.g.setParameter('isLogged', true);
519
- // // this.g.setParameter('attributes', this.setAttributesFromStorageService());
520
- // // this.startNwConversation();
521
- // //this.startUI();
522
- // // this.g.wdLog([' 13 - IMPOSTO STATO CONNESSO UTENTE ']);
523
- // // this.presenceService.setPresence(currentUser.uid);
524
- //
509
+
525
510
  } else {
526
511
  // AUTENTICAZIONE ANONIMA
527
512
  this.logger.debug('[APP-COMP] ---------------- 14 ---------------- ');
@@ -543,9 +528,6 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
543
528
  this.g.setAttributeParameter('userEmail', user.email);
544
529
  }
545
530
  });
546
- // this.authService.anonymousAuthentication();
547
- // this.g.wdLog([' authenticateFirebaseAnonymously']);
548
- // this.authService.authenticateFirebaseAnonymously();
549
531
  }
550
532
  }
551
533
 
@@ -599,8 +581,6 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
599
581
  this.logger.error('[APP-COMP] signInAnonymous ERR', error);
600
582
  return Promise.reject(error);
601
583
  });
602
- // this.authService.anonymousAuthentication();
603
- // this.authService.authenticateFirebaseAnonymously();
604
584
  }
605
585
  // ========= end:: AUTHENTICATION ============//
606
586
 
@@ -131,6 +131,7 @@ import { StarRatingWidgetService } from './providers/star-rating-widget.service'
131
131
  import { LikeUnlikeComponent } from './component/message/like-unlike/like-unlike.component';
132
132
  import { Rules } from './utils/rules';
133
133
  import { ScriptService } from 'src/chat21-core/providers/scripts/script.service';
134
+ import { CarouselComponent } from './component/message/carousel/carousel.component';
134
135
 
135
136
 
136
137
 
@@ -290,7 +291,8 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
290
291
  SafeHtmlPipe,
291
292
  LikeUnlikeComponent,
292
293
  TooltipDirective,
293
- AudioComponent
294
+ AudioComponent,
295
+ CarouselComponent
294
296
  ],
295
297
  imports: [
296
298
  BrowserModule,
@@ -42,7 +42,7 @@ import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance'
42
42
  import { TiledeskRequestsService } from 'src/chat21-core/providers/tiledesk/tiledesk-requests.service';
43
43
  import { LIVE_PAGE } from 'src/chat21-core/utils/constants';
44
44
  import { getDateDifference, popupUrl } from 'src/chat21-core/utils/utils';
45
- import { isUserBanned } from 'src/chat21-core/utils/utils-message';
45
+ import { isJustRecived, isUserBanned } from 'src/chat21-core/utils/utils-message';
46
46
  import { AppComponent } from '../../../app.component';
47
47
  import { ConversationContentComponent } from '../conversation-content/conversation-content.component';
48
48
  // import { TranslateService } from '@ngx-translate/core';
@@ -165,6 +165,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
165
165
 
166
166
  public isButtonUrl: boolean = false;
167
167
  public buttonClicked: any;
168
+ public startTime: Date = new Date();
168
169
  private logger: LoggerService = LoggerInstance.getInstance();
169
170
 
170
171
  constructor(
@@ -890,6 +891,12 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
890
891
  }else {
891
892
  this.footerMessagePlaceholder = '';
892
893
  }
894
+
895
+ //check if redirect message is present inside message object
896
+ if(msg.type === 'redirect' && isJustRecived(this.startTime.getTime(), msg.timestamp)){
897
+ let button = { link: msg.metadata.src, target: msg.metadata.target}
898
+ this.openLink(button)
899
+ }
893
900
  }
894
901
 
895
902
  }
@@ -107,6 +107,19 @@
107
107
  (onAttachmentButtonClicked)="onAttachmentButtonClickedFN($event)">
108
108
  </chat-message-attachment>
109
109
  </div>
110
+
111
+ <!-- carousel -->
112
+ <div *ngIf="message?.attributes && message?.attributes?.attachment
113
+ && message?.attributes?.attachment?.gallery" [ngClass]="{'slide-in-left': false}" class="carousel_container">
114
+ <chat-carousel class="carousel_container"
115
+ [message]="message"
116
+ [isConversationArchived]="isConversationArchived"
117
+ [isLastMessage] = "isLastMessage(message?.uid)"
118
+ [stylesMap]="stylesMap"
119
+ (onElementRendered)="onElementRenderedFN($event)"
120
+ (onAttachmentButtonClicked)="onAttachmentButtonClickedFN($event)">
121
+ </chat-carousel>
122
+ </div>
110
123
 
111
124
  </div>
112
125
 
@@ -135,6 +135,10 @@
135
135
  align-content: center;
136
136
  justify-content: flex-end;
137
137
  }
138
+ .carousel_container{
139
+ margin: 0px 20px;
140
+ }
141
+
138
142
  .msg_info_container{
139
143
  text-align: center;
140
144
  padding: 0px 0px 6px 0px
@@ -31,11 +31,11 @@
31
31
  <!-- ICON EMOJII -->
32
32
  <label tabindex="1504" aria-label="emojii" for="chat21-emojii" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-emoticon-picker" (click)="onEmojiiPickerClicked()">
33
33
  <span class="v-align-center">
34
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 28 28" >
35
- <path d="M0 0h24v24H0V0z" fill="none"/>
36
- <circle cx="15.5" cy="9.5" r="1.5"/>
37
- <circle cx="8.5" cy="9.5" r="1.5"/>
38
- <path d="M12 16c-1.48 0-2.75-.81-3.45-2H6.88c.8 2.05 2.79 3.5 5.12 3.5s4.32-1.45 5.12-3.5h-1.67c-.7 1.19-1.97 2-3.45 2zm-.01-14C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/>
34
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
35
+ <path d="M0,0H20.57V20.57H0V0Z" fill="none"/>
36
+ <circle cx="15.02" cy="9.86" r="1.29"/>
37
+ <circle cx="9.02" cy="9.86" r="1.29"/>
38
+ <path d="M12.02,15.43c-1.27,0-2.36-.69-2.96-1.71h-1.43c.69,1.76,2.39,3,4.39,3s3.7-1.24,4.39-3h-1.43c-.6,1.02-1.69,1.71-2.96,1.71Zm0-12C7.28,3.43,3.45,7.27,3.45,12s3.83,8.57,8.56,8.57,8.58-3.84,8.58-8.57S16.75,3.43,12.01,3.43Zm0,15.43c-3.79,0-6.86-3.07-6.86-6.86s3.07-6.86,6.86-6.86,6.86,3.07,6.86,6.86-3.07,6.86-6.86,6.86Z"/>
39
39
  </svg>
40
40
  </span>
41
41
  </label>
@@ -1,3 +1,7 @@
1
+ #audio_container{
2
+ display: flex;
3
+ }
4
+
1
5
  #play-icon,
2
6
  #pause-icon {
3
7
  margin: 20px 2.5% 10px 2.5%;
@@ -0,0 +1,35 @@
1
+ <div class="wrapper">
2
+ <div id="left" class="arrow left" (click)="goTo('previous')" *ngIf="activeElement > 1">
3
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
4
+ <path d="M0 0h24v24H0V0z" fill="none"/><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z"/>
5
+ </svg>
6
+ </div>
7
+ <div class="carousel">
8
+ <!-- <div class="card" style="width: 17px;"></div> -->
9
+ <div class="card" *ngFor="let card of gallery; let i = index">
10
+ <div [style.opacity]="i+1 === activeElement? 1: 0.5">
11
+ <div class="card-image">
12
+ <img [src]="card?.preview?.src" alt="img" draggable="false">
13
+ </div>
14
+ <div class="card-content">
15
+ <div class="card-title">{{card?.title}}</div>
16
+ <div class="card-description">{{card?.description}}</div>
17
+ </div>
18
+ <div class="buttons" *ngIf="card?.buttons && card?.buttons.length > 0">
19
+ <div *ngFor="let button of card?.buttons"
20
+ class="single-button action"
21
+ [ngClass]="{'disabled': (isConversationArchived || !isLastMessage), 'active': button?.active}"
22
+ (click)="actionButtonClick($event, button, i)" >
23
+ {{button.value}}
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ <!-- <div class="card" style="width: 17px;"></div> -->
29
+ </div>
30
+ <div id="right" class="arrow right" (click)="goTo('next')" *ngIf="activeElement !== gallery.length">
31
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
32
+ <path d="M0 0h24v24H0V0z" fill="none"/><path d="M10.02 6L8.61 7.41 13.19 12l-4.58 4.59L10.02 18l6-6-6-6z"/>
33
+ </svg>
34
+ </div>
35
+ </div>
@@ -0,0 +1,260 @@
1
+ @import 'src/app/sass/variables';
2
+
3
+ :host {
4
+ --backgroundColor: #{var(--blue)};
5
+ --textColor: #{var(--bck-msg-sent)};
6
+ --hoverBackgroundColor: #{var(--bck-msg-sent)};
7
+ --hoverTextColor: #{var(--blue)};
8
+ --buttonFontSize: #{var(--button-in-msg-font-size)};
9
+ --max-width: #{var(--button-in-msg-max-width)};
10
+
11
+ --cardWidth: 220px;
12
+ }
13
+
14
+
15
+ .wrapper {
16
+ // max-width: 1100px;
17
+ width: 100%;
18
+ position: relative;
19
+ display: flex;
20
+
21
+ // overflow: scroll;
22
+ gap: 10px;
23
+ margin: 0;
24
+ width: 100%;
25
+ font-size: 14px;
26
+ // margin: 0 25px;
27
+ }
28
+ .wrapper div.arrow {
29
+ top: 50%;
30
+ height: 40px;
31
+ width: 40px;
32
+ cursor: pointer;
33
+ position: absolute;
34
+ background: #fff;
35
+ border-radius: 50%;
36
+ box-shadow: 0 3px 6px rgba(0,0,0,0.23);
37
+ transform: translateY(-50%);
38
+ transition: transform 0.1s linear;
39
+ z-index: 2;
40
+
41
+ display: flex;
42
+ justify-content: center;
43
+ align-items: center;
44
+ }
45
+ .wrapper div.arrow:active{
46
+ transform: translateY(-50%) scale(0.85);
47
+ }
48
+ .wrapper div.arrow:first-child{
49
+ left: -22px;
50
+ }
51
+ .wrapper div.arrow:last-child{
52
+ right: -22px;
53
+ }
54
+ .wrapper .carousel{
55
+ display: grid;
56
+ align-items: start;
57
+ grid-auto-flow: column;
58
+ grid-auto-columns: calc((100% / 3) - 12px);
59
+ // overflow-x: auto;
60
+ overflow-x: hidden;
61
+ scroll-snap-type: x mandatory;
62
+ gap: 10px;
63
+ border-radius: 8px;
64
+ scroll-behavior: smooth;
65
+ scrollbar-width: none;
66
+
67
+ padding: 10px 0px
68
+ }
69
+ .carousel::-webkit-scrollbar {
70
+ display: none;
71
+ }
72
+ .carousel.no-transition {
73
+ scroll-behavior: auto;
74
+ }
75
+ .carousel.dragging {
76
+ scroll-snap-type: none;
77
+ scroll-behavior: auto;
78
+ }
79
+ .carousel.dragging .card {
80
+ cursor: grab;
81
+ user-select: none;
82
+ }
83
+ .carousel :where(.card, .img) {
84
+ display: flex;
85
+ justify-content: center;
86
+ align-items: center;
87
+ }
88
+ .carousel .card {
89
+ // padding: 0px 5px;
90
+ scroll-snap-align: start;
91
+ width: var(--cardWidth);
92
+ background: rgb(255, 255, 255);
93
+ list-style: none;
94
+ cursor: pointer;
95
+ // padding-bottom: 15px;
96
+ flex-direction: column;
97
+ border-radius: 8px;
98
+ box-shadow: 0 3px 6px rgba(0,0,0,0.23);
99
+ }
100
+ .carousel .card .card-image {
101
+ height: 150px;
102
+ width: var(--cardWidth);
103
+ }
104
+ .card .card-image img {
105
+ height: 100%;
106
+ -o-object-fit: cover;
107
+ object-fit: cover;
108
+
109
+ background: transparent!important;
110
+ display: block;
111
+ max-width: 100% !important;
112
+ border-radius: 8px 8px 0px 0px;
113
+ }
114
+ .carousel .card .card-content {
115
+ // font-weight: 500;
116
+ // font-size: 1.56rem;
117
+ // margin: 30px 0 5px;
118
+ -webkit-box-orient: vertical;
119
+ -webkit-box-direction: normal;
120
+ display: -webkit-box;
121
+ display: -ms-flexbox;
122
+ display: flex;
123
+ -ms-flex-direction: column;
124
+ flex-direction: column;
125
+ padding: 0 20px 30px;
126
+
127
+ .card-title{
128
+ word-wrap: break-word;
129
+ font-size: 16px;
130
+ font-weight: 600;
131
+ line-height: 20px;
132
+ margin-top: 12px;
133
+ white-space: pre-wrap;
134
+ width: 100%;
135
+ }
136
+
137
+ .card-description{
138
+ word-wrap: break-word;
139
+ font-size: 16px;
140
+ line-height: 20px;
141
+ margin-top: 12px;
142
+ white-space: pre-wrap;
143
+ }
144
+
145
+ }
146
+
147
+ .cards-scroll-spacer{
148
+ -ms-flex-negative: 0;
149
+ content: "";
150
+ flex-shrink: 0;
151
+ width: 17px;
152
+ }
153
+
154
+ .carousel .card .buttons{
155
+ -webkit-box-orient: vertical;
156
+ -webkit-box-direction: normal;
157
+ display: -webkit-box;
158
+ display: -ms-flexbox;
159
+ display: flex;
160
+ -ms-flex-direction: column;
161
+ flex-direction: column;
162
+ width: 100%;
163
+
164
+
165
+ .single-button{
166
+ // border-top-color: rgb(219, 225, 232);
167
+
168
+ -webkit-box-align: center;
169
+ -ms-flex-align: center;
170
+ align-items: center;
171
+ justify-content: center;
172
+ border-top: 1px solid var(--textColor);
173
+ cursor: pointer;
174
+ display: -webkit-box;
175
+ display: -ms-flexbox;
176
+ display: flex;
177
+ font-weight: 600;
178
+ // height: 45px;
179
+ // padding: 0 10px;
180
+ -webkit-transition: all .3s;
181
+ transition: all .3s;
182
+
183
+ background: var(--backgroundColor);
184
+ font-family: 'Muli', sans-serif;
185
+ font-size: var(--buttonFontSize);
186
+ -o-text-overflow: ellipsis;
187
+ text-overflow: ellipsis;
188
+ // // white-space: nowrap;
189
+ word-wrap: break-word;
190
+ letter-spacing: -0.24px;
191
+ -webkit-font-smoothing: antialiased;
192
+ color: var(--textColor);
193
+ line-height: 16px;
194
+ padding: 8px 16px!important;
195
+
196
+ &:focus,
197
+ &:hover {
198
+ color: var(--hoverTextColor);
199
+ background: var(--hoverBackgroundColor);
200
+ // transform: scale(1.05);
201
+ .icon-button-action {
202
+ svg {
203
+ fill: var(--hoverTextColor);
204
+ }
205
+ }
206
+ }
207
+ &:after {
208
+ content: "";
209
+ position: absolute;
210
+ width: 0;
211
+ height: 0;
212
+ top: 50%;
213
+ left: 50%;
214
+ transform-style: flat;
215
+ transform: translate3d(-50%,-50%,0);
216
+ background: rgba(white,.2);
217
+ border-radius: 100%;
218
+ transition: width .5s ease, height .5s ease;
219
+ }
220
+ &.active{
221
+ color: var(--hoverTextColor);
222
+ background: var(--hoverBackgroundColor);
223
+ .icon-button-action {
224
+ svg {
225
+ fill: var(--hoverTextColor);
226
+ }
227
+ }
228
+ }
229
+
230
+
231
+ }
232
+
233
+ .disabled {
234
+ // border: 1px solid #999999;
235
+ // background-color: #cccccc;
236
+ // color: #666666;
237
+ cursor: default;
238
+ pointer-events: none;
239
+ }
240
+
241
+ .single-button:last-child{
242
+ border-radius: 0px 0px 8px 8px;
243
+ }
244
+ }
245
+
246
+ .carousel .card span {
247
+ color: #6A6D78;
248
+ font-size: 1.31rem;
249
+ }
250
+ @media screen and (max-width: 900px) {
251
+ .wrapper .carousel {
252
+ grid-auto-columns: calc((100% / 2) - 9px);
253
+ }
254
+ }
255
+ @media screen and (max-width: 600px) {
256
+ .wrapper .carousel {
257
+ grid-auto-columns: var(--cardWidth);
258
+ // grid-auto-columns: 17px repeat(var(--cardWidth)) 17px;
259
+ }
260
+ }
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { CarouselComponent } from './carousel.component';
4
+
5
+ describe('CarouselComponent', () => {
6
+ let component: CarouselComponent;
7
+ let fixture: ComponentFixture<CarouselComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ declarations: [ CarouselComponent ]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(CarouselComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,139 @@
1
+ import { Component, ElementRef, EventEmitter, Input, OnInit, Output, SimpleChange, SimpleChanges, ViewChildren } from '@angular/core';
2
+ import { MessageModel } from 'src/chat21-core/models/message';
3
+
4
+ @Component({
5
+ selector: 'chat-carousel',
6
+ templateUrl: './carousel.component.html',
7
+ styleUrls: ['./carousel.component.scss']
8
+ })
9
+ export class CarouselComponent implements OnInit{
10
+
11
+ // ========= begin:: Input/Output values ============//
12
+ @Input() message: MessageModel;
13
+ @Input() isConversationArchived: boolean;
14
+ @Input() isLastMessage: boolean;
15
+ @Input() stylesMap: Map<string, string>;
16
+ @Output() onAttachmentButtonClicked = new EventEmitter<any>();
17
+ @Output() onElementRendered = new EventEmitter<{element: string, status: boolean}>()
18
+ // ========= end:: Input/Output values ============//
19
+ gallery: any[]
20
+
21
+ wrapper: HTMLElement;
22
+ carousel: HTMLElement;
23
+ firstCardWidth: number;
24
+ activeElement: number = 1;
25
+
26
+ fontSize: string;
27
+ backgroundColor: string;
28
+ textColor: string;
29
+ hoverBackgroundColor: string;
30
+ hoverTextColor: string;
31
+ type: string;
32
+ button: any;
33
+
34
+ constructor(private elementRef: ElementRef) { }
35
+
36
+ ngOnInit() {
37
+ console.log('[CAROUSEL-MESSAGE] hello', this.message)
38
+
39
+
40
+ this.wrapper = this.elementRef.nativeElement.querySelector('.wrapper')
41
+ this.carousel = this.elementRef.nativeElement.querySelector('.carousel')
42
+
43
+
44
+ // this.firstCardWidth = (this.elementRef.nativeElement.querySelector(".card") as HTMLElement).offsetWidth
45
+ // // Get the number of cards that can fit in the carousel at once
46
+ // let cardPerView = Math.round(this.carousel.offsetWidth / this.firstCardWidth);
47
+
48
+ // Insert copies of the last few cards to beginning of carousel for infinite scrolling
49
+ // const carouselChildrens = [...this.carousel.children];
50
+ // carouselChildrens.slice(-cardPerView).reverse().forEach(card => {
51
+ // this.carousel.insertAdjacentHTML("afterbegin", card.outerHTML);
52
+ // });
53
+ // // Insert copies of the first few cards to end of carousel for infinite scrolling
54
+ // carouselChildrens.slice(0, cardPerView).forEach(card => {
55
+ // this.carousel.insertAdjacentHTML("beforeend", card.outerHTML);
56
+ // });
57
+
58
+ // Scroll the carousel at appropriate postition to hide first few duplicate cards on Firefox
59
+ this.carousel.classList.add("no-transition");
60
+ this.carousel.scrollLeft = this.carousel.offsetWidth;
61
+ this.carousel.classList.remove("no-transition");
62
+
63
+ let currentItem = 0
64
+ // Store items as an array of objects
65
+ const items = this.carousel.querySelectorAll('.card')
66
+
67
+ this.carousel.addEventListener("scroll", function(el){
68
+ // Find item closest to the goal
69
+ // currentItem = items.reduce((prev, curr) => {
70
+ // return (Math.abs(curr.offsetY - scrollY - goal) < Math.abs(prev.offsetY - scrollY - goal) ? curr : prev); // return the closest to the goal
71
+ // });
72
+ });
73
+
74
+ }
75
+
76
+ ngOnChanges(changes: SimpleChanges){
77
+ if(this.message && this.message.attributes && this.message.attributes?.attachment && this.message.attributes?.attachment?.gallery){
78
+ this.gallery = this.message.attributes.attachment.gallery
79
+ console.log('carrrrrrrrr', this.wrapper, this.elementRef.nativeElement.querySelector(".card"))
80
+ // this.firstCardWidth = (this.elementRef.nativeElement.querySelector(".card") as HTMLElement).offsetWidth
81
+ }
82
+
83
+ if(this.stylesMap ){
84
+ if(this.stylesMap.has('buttonFontSize')) this.elementRef.nativeElement.querySelector('.wrapper').style.setProperty('--buttonFontSize', this.stylesMap.get('buttonFontSize'));
85
+ if(this.stylesMap.has('buttonBackgroundColor')) this.elementRef.nativeElement.querySelector('.wrapper').style.setProperty('--backgroundColor', this.stylesMap.get('buttonBackgroundColor'));
86
+ if(this.stylesMap.has('buttonTextColor')) this.elementRef.nativeElement.querySelector('.wrapper').style.setProperty('--textColor', this.stylesMap.get('buttonTextColor'));
87
+ if(this.stylesMap.has('buttonHoverBackgroundColor')) this.elementRef.nativeElement.querySelector('.wrapper').style.setProperty('--hoverBackgroundColor', this.stylesMap.get('buttonHoverBackgroundColor'));
88
+ if(this.stylesMap.has('buttonHoverTextColor')) this.elementRef.nativeElement.querySelector('.wrapper').style.setProperty('--hoverTextColor', this.stylesMap.get('buttonHoverTextColor'));
89
+ }
90
+
91
+ }
92
+
93
+ goTo(direction: 'next' | 'previous' ){
94
+ let width = (this.carousel.querySelectorAll(".card")[1] as HTMLElement).offsetWidth
95
+ let gap = 17
96
+ let cardPerView = Math.round(this.carousel.offsetWidth / width);
97
+
98
+ console.log('go to -->', direction, width, this.firstCardWidth, cardPerView, this.carousel.offsetWidth)
99
+
100
+ // this.carousel.scrollLeft += direction == "previous" ? -(width+gap) : width+gap;
101
+ this.carousel.scrollLeft += direction == "previous" ? -width : width;
102
+ this.activeElement += direction == "previous" ? -1 : 1;
103
+
104
+ // this.carousel.classList.add("no-transition");
105
+ // this.carousel.scrollLeft += width;
106
+ // this.carousel.classList.remove("no-transition");
107
+
108
+ }
109
+
110
+ actionButtonClick(ev, button, index){
111
+ this.button = button
112
+ this.type = button.type
113
+ console.log('buttonnnnnnn', ev, button)
114
+ if ( button && ((button.action && button.action !== '') || (button.link && button.link !== '') || button.text !== '' )) {
115
+
116
+ //set clicked button as the active one
117
+ this.gallery[index].buttons.find((element)=> { return element === button}).active = true
118
+ const spanCheck = this.elementRef.nativeElement.querySelector('.action');
119
+ if (spanCheck) {
120
+ // const item = domRepresentation[0] as HTMLInputElement;
121
+ // if (!spanCheck.classList.contains('active')) {
122
+ // spanCheck.classList.add('active');
123
+ // }
124
+ // setTimeout(function() {
125
+ // if (spanCheck.classList.contains('active')) {
126
+ // spanCheck.classList.remove('active');
127
+ // }
128
+ // }, 400);
129
+ ev.target.classList.add('active')
130
+ // event.target.classList
131
+ }
132
+ const event = { target: this, currentTarget: this}
133
+ if ( event && event.target ) {
134
+ const ev = {target: event.target, message: this.message, currentTarget: this }
135
+ this.onAttachmentButtonClicked.emit(ev);
136
+ }
137
+ }
138
+ }
139
+ }
@@ -507,6 +507,18 @@ export class GlobalSettingsService {
507
507
  if (variables.hasOwnProperty('fileUploadAccept')) {
508
508
  globals['fileUploadAccept'] = variables['fileUploadAccept'];
509
509
  }
510
+ if (variables.hasOwnProperty('displayOnDesktop')) {
511
+ globals['displayOnDesktop'] = variables['displayOnDesktop'];
512
+ }
513
+ if (variables.hasOwnProperty('displayOnMobile')) {
514
+ globals['displayOnMobile'] = variables['displayOnMobile'];
515
+ }
516
+ if (variables.hasOwnProperty('onPageChangeVisibilityDesktop')) {
517
+ globals['onPageChangeVisibilityDesktop'] = variables['onPageChangeVisibilityDesktop'];
518
+ }
519
+ if (variables.hasOwnProperty('onPageChangeVisibilityMobile')) {
520
+ globals['onPageChangeVisibilityMobile'] = variables['onPageChangeVisibilityMobile'];
521
+ }
510
522
 
511
523
  }
512
524
  }
@@ -997,7 +1009,7 @@ export class GlobalSettingsService {
997
1009
  TEMP = tiledeskSettings['participants'];
998
1010
  // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > participants:: ', TEMP]);
999
1011
  if (TEMP !== undefined) {
1000
- globals.participants = TEMP.split(',').map(key => { return key.trim()});;
1012
+ globals.participants = TEMP.split(',').map(key => { return key.trim()});
1001
1013
  }
1002
1014
  TEMP = tiledeskSettings['whatsappNumber'];
1003
1015
  // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > whatsappNumber:: ', TEMP]);
@@ -1023,6 +1035,26 @@ export class GlobalSettingsService {
1023
1035
  // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > disconnetTime:: ', TEMP]);
1024
1036
  if (TEMP !== undefined) {
1025
1037
  globals.disconnetTime = +TEMP;
1038
+ }
1039
+ TEMP = tiledeskSettings['displayOnDesktop'];
1040
+ // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > displayOnDesktop:: ', TEMP]);
1041
+ if (TEMP !== undefined) {
1042
+ globals.displayOnDesktop = (TEMP === true) ? true : false;
1043
+ }
1044
+ TEMP = tiledeskSettings['displayOnMobile'];
1045
+ // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > displayOnMobile:: ', TEMP]);
1046
+ if (TEMP !== undefined) {
1047
+ globals.displayOnMobile = (TEMP === true) ? true : false;
1048
+ }
1049
+ TEMP = tiledeskSettings['onPageChangeVisibilityDesktop'];
1050
+ // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > onPageChangeVisibilityDesktop:: ', TEMP]);
1051
+ if (TEMP !== undefined) {
1052
+ globals.onPageChangeVisibilityDesktop = TEMP;
1053
+ }
1054
+ TEMP = tiledeskSettings['onPageChangeVisibilityMobile'];
1055
+ // this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > onPageChangeVisibilityMobile:: ', TEMP]);
1056
+ if (TEMP !== undefined) {
1057
+ globals.onPageChangeVisibilityMobile = TEMP;
1026
1058
  }
1027
1059
  }
1028
1060
 
@@ -213,6 +213,11 @@ export class Globals {
213
213
  telegramUsername: string; // ******* new ********
214
214
  fileUploadAccept: string; // ******* new ********
215
215
  disconnetTime: number; // ******* new ********
216
+
217
+ onPageChangeVisibilityMobile: 'open' | 'close' | 'last'; // ******* new ********
218
+ onPageChangeVisibilityDesktop: 'open' | 'close' | 'last'; // ******* new ********
219
+ displayOnMobile: boolean; // ******* new ********
220
+ displayOnDesktop: boolean; // ******* new ********
216
221
  constructor(
217
222
  ) { }
218
223
 
@@ -402,6 +407,14 @@ export class Globals {
402
407
  this.showAttachmentButton = true;
403
408
  this.showAllConversations = true;
404
409
 
410
+ //WIDGET VISIBILITY - desktop
411
+ this.displayOnDesktop = true
412
+ this.onPageChangeVisibilityDesktop ='close'
413
+ //WIDGET VISIBILITY - mobile
414
+ this.displayOnMobile = true
415
+ this.onPageChangeVisibilityMobile = 'close'
416
+
417
+
405
418
  // ============ END: SET EXTERNAL PARAMETERS ==============//
406
419
 
407
420
 
@@ -105,14 +105,14 @@ export class FirebaseConversationHandler extends ConversationHandlerService {
105
105
  this.logger.debug('[FIREBASEConversationHandlerSERVICE] urlNodeFirebase *****', this.urlNodeFirebase);
106
106
  const firebaseMessages = this.firebase.database().ref(this.urlNodeFirebase);
107
107
  this.ref = firebaseMessages.orderByChild('timestamp').limitToLast(100);
108
- this.ref.on('child_added', (childSnapshot) => {
108
+ this.ref.on('child_added', async (childSnapshot) => {
109
109
  that.logger.debug('[FIREBASEConversationHandlerSERVICE] >>>>>>>>>>>>>> child_added: ', childSnapshot.val())
110
110
  const msg: MessageModel = childSnapshot.val();
111
111
  msg.uid = childSnapshot.key;
112
112
 
113
113
  if (msg.attributes && msg.attributes.commands) {
114
114
  this.logger.debug('[FIREBASEConversationHandlerSERVICE] splitted message::::', msg)
115
- that.addCommandMessage(msg)
115
+ await that.addCommandMessage(msg)
116
116
  } else {
117
117
  this.logger.debug('[FIREBASEConversationHandlerSERVICE] NOT splitted message::::', msg)
118
118
  that.addedNew(msg)
@@ -255,6 +255,7 @@ export class FirebaseConversationHandler extends ConversationHandlerService {
255
255
 
256
256
 
257
257
 
258
+
258
259
  this.addRepalceMessageInArray(msg.uid, msg);
259
260
  this.messageAdded.next(msg);
260
261
  } else {
@@ -468,59 +469,63 @@ export class FirebaseConversationHandler extends ConversationHandlerService {
468
469
  }
469
470
 
470
471
 
471
- private addCommandMessage(msg: MessageModel){
472
+ private async addCommandMessage(msg: MessageModel): Promise<boolean> {
472
473
  const that = this;
473
474
  const commands = msg.attributes.commands;
474
475
  let i=0;
475
- function execute(command){
476
- if(command.type === "message"){
477
- that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> type="message"', command, i)
478
- if (i >= 2) {
479
-
480
- //check if previus wait message type has time value, otherwize set to 1000ms
481
- !commands[i-1].time? commands[i-1].time= 1000 : commands[i-1].time
482
- command.message.timestamp = commands[i-2].message.timestamp + commands[i-1].time;
483
-
484
- /** CHECK IF MESSAGE IS JUST RECEIVED: IF false, set next message time (if object exist) to 0 -> this allow to show it immediately */
485
- if(!isJustRecived(that.startTime.getTime(), msg.timestamp)){
486
- let previewsTimeMsg = msg.timestamp;
487
- commands[i-2]? previewsTimeMsg = commands[i-2].message.timestamp : null;
488
- command.message.timestamp = previewsTimeMsg + 100
489
- commands[i+1]? commands[i+1].time = 0 : null
490
- }
491
- } else { /**MANAGE FIRST MESSAGE */
492
- command.message.timestamp = msg.timestamp;
493
- if(!isJustRecived(that.startTime.getTime(), msg.timestamp)){
494
- commands[i+1]? commands[i+1].time = 0 : null
495
- }
496
- }
497
- that.generateMessageObject(msg, command.message, function () {
498
- i += 1
499
- if (i < commands.length) {
500
- execute(commands[i])
476
+ return new Promise((resolve, reject)=>{
477
+ function execute(command){
478
+ if(command.type === "message"){
479
+ that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> type="message"', command, i)
480
+ if (i >= 2) {
481
+
482
+ //check if previus wait message type has time value, otherwize set to 1000ms
483
+ !commands[i-1].time? commands[i-1].time= 1000 : commands[i-1].time
484
+ command.message.timestamp = commands[i-2].message.timestamp + commands[i-1].time;
485
+
486
+ /** CHECK IF MESSAGE IS JUST RECEIVED: IF false, set next message time (if object exist) to 0 -> this allow to show it immediately */
487
+ if(!isJustRecived(that.startTime.getTime(), msg.timestamp)){
488
+ let previewsTimeMsg = msg.timestamp;
489
+ commands[i-2]? previewsTimeMsg = commands[i-2].message.timestamp : null;
490
+ command.message.timestamp = previewsTimeMsg + 100
491
+ commands[i+1]? commands[i+1].time = 0 : null
492
+ }
493
+ } else { /**MANAGE FIRST MESSAGE */
494
+ command.message.timestamp = msg.timestamp;
495
+ if(!isJustRecived(that.startTime.getTime(), msg.timestamp)){
496
+ commands[i+1]? commands[i+1].time = 0 : null
497
+ }
501
498
  }
502
- else {
503
- that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> last command executed (wait), exit')
499
+ that.generateMessageObject(msg, command.message, function () {
500
+ i += 1
501
+ if (i < commands.length) {
502
+ execute(commands[i])
503
+ }
504
+ else {
505
+ that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> last command executed (wait), exit')
506
+ resolve(true)
507
+ }
508
+ })
509
+ }else if(command.type === "wait"){
510
+ that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> type="wait"', command, i, commands.length)
511
+ //publish waiting event to simulate user typing
512
+ if(isJustRecived(that.startTime.getTime(), msg.timestamp)){
513
+ that.messageWait.next({uid: that.conversationWith, uidUserTypingNow: msg.sender, nameUserTypingNow: msg.sender_fullname, waitTime: command.time, command: command})
504
514
  }
505
- })
506
- }else if(command.type === "wait"){
507
- that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> type="wait"', command, i, commands.length)
508
- //publish waiting event to simulate user typing
509
- if(isJustRecived(that.startTime.getTime(), msg.timestamp)){
510
- that.messageWait.next({uid: that.conversationWith, uidUserTypingNow: msg.sender, nameUserTypingNow: msg.sender_fullname, waitTime: command.time, command: command})
515
+ setTimeout(function() {
516
+ i += 1
517
+ if (i < commands.length) {
518
+ execute(commands[i])
519
+ }
520
+ else {
521
+ that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> last command executed (send message), exit')
522
+ resolve(true)
523
+ }
524
+ },command.time)
511
525
  }
512
- setTimeout(function() {
513
- i += 1
514
- if (i < commands.length) {
515
- execute(commands[i])
516
- }
517
- else {
518
- that.logger.debug('[FIREBASEConversationHandlerSERVICE] addCommandMessage --> last command executed (send message), exit')
519
- }
520
- },command.time)
521
526
  }
522
- }
523
- execute(commands[0]) //START render first message
527
+ execute(commands[0]) //START render first message
528
+ })
524
529
  }
525
530
 
526
531
  private generateMessageObject(message, command_message, callback) {
@@ -8,7 +8,7 @@ import {
8
8
  MAX_WIDTH_IMAGES,
9
9
  CHANNEL_TYPE_GROUP,
10
10
  TYPE_SUPPORT_GROUP
11
- } from '../../chat21-core/utils/constants';
11
+ } from './constants';
12
12
 
13
13
  /** */
14
14
  export function isImage(message: any) {
@@ -27,14 +27,14 @@ export function isFrame(message: any) {
27
27
 
28
28
  /** */
29
29
  export function isFile(message: any) {
30
- if (message && message.type && message.type === 'file' && message.metadata && message.metadata.src && !message.metadata.includes('audio')) {
30
+ if (message && message.type && message.type === 'file' && message.metadata && message.metadata.src && !message.metadata.type.includes('audio')) {
31
31
  return true;
32
32
  }
33
33
  return false;
34
34
  }
35
35
 
36
36
  export function isAudio(message: any) {
37
- if (message && message.type && message.type === 'file' && message.metadata && message.metadata.src && message.metadata.includes('audio') ) {
37
+ if (message && message.type && message.type === 'file' && message.metadata && message.metadata.src && message.metadata.type.includes('audio') ) {
38
38
  return true;
39
39
  }
40
40
  return false;