@muraai/mnl-chat 0.0.1 → 0.2.0-alpha-11e96eb

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 (52) hide show
  1. package/fesm2022/muraai-mnl-chat.mjs +1507 -0
  2. package/fesm2022/muraai-mnl-chat.mjs.map +1 -0
  3. package/lib/components/ng-chat/ng-chat.component.d.ts +3 -1
  4. package/lib/components/ng-chat-friends-list/ng-chat-friends-list.component.d.ts +11 -3
  5. package/lib/components/ng-chat-options/ng-chat-options.component.d.ts +1 -1
  6. package/lib/components/ng-chat-window/ng-chat-window.component.d.ts +3 -1
  7. package/lib/core/chat-participant.d.ts +12 -0
  8. package/lib/material.module.d.ts +6 -3
  9. package/package.json +6 -13
  10. package/public-api.d.ts +1 -0
  11. package/src/assets/themes/ng-chat.theme.dark.scss +1 -1
  12. package/esm2020/lib/chat.module.mjs +0 -87
  13. package/esm2020/lib/components/ng-chat/ng-chat.component.mjs +0 -588
  14. package/esm2020/lib/components/ng-chat-friends-list/ng-chat-friends-list.component.mjs +0 -128
  15. package/esm2020/lib/components/ng-chat-options/ng-chat-options.component.mjs +0 -29
  16. package/esm2020/lib/components/ng-chat-window/ng-chat-window.component.mjs +0 -247
  17. package/esm2020/lib/core/chat-adapter.mjs +0 -18
  18. package/esm2020/lib/core/chat-controller.mjs +0 -2
  19. package/esm2020/lib/core/chat-group-adapter.mjs +0 -2
  20. package/esm2020/lib/core/chat-option.mjs +0 -2
  21. package/esm2020/lib/core/chat-participant-status-descriptor.mjs +0 -6
  22. package/esm2020/lib/core/chat-participant-status.enum.mjs +0 -8
  23. package/esm2020/lib/core/chat-participant-type.enum.mjs +0 -6
  24. package/esm2020/lib/core/chat-participant.mjs +0 -2
  25. package/esm2020/lib/core/default-file-upload-adapter.mjs +0 -43
  26. package/esm2020/lib/core/file-upload-adapter.mjs +0 -2
  27. package/esm2020/lib/core/group.mjs +0 -14
  28. package/esm2020/lib/core/guid.mjs +0 -11
  29. package/esm2020/lib/core/localization.mjs +0 -2
  30. package/esm2020/lib/core/message-counter.mjs +0 -25
  31. package/esm2020/lib/core/message-type.enum.mjs +0 -7
  32. package/esm2020/lib/core/message.mjs +0 -7
  33. package/esm2020/lib/core/paged-history-chat-adapter.mjs +0 -9
  34. package/esm2020/lib/core/participant-metadata.mjs +0 -6
  35. package/esm2020/lib/core/participant-response.mjs +0 -3
  36. package/esm2020/lib/core/scroll-direction.enum.mjs +0 -6
  37. package/esm2020/lib/core/theme.enum.mjs +0 -7
  38. package/esm2020/lib/core/user.mjs +0 -7
  39. package/esm2020/lib/core/window.mjs +0 -20
  40. package/esm2020/lib/firstLetter.pipe.mjs +0 -20
  41. package/esm2020/lib/material.module.mjs +0 -50
  42. package/esm2020/lib/pipes/emojify.pipe.mjs +0 -41
  43. package/esm2020/lib/pipes/group-message-display-name.pipe.mjs +0 -24
  44. package/esm2020/lib/pipes/linkfy.pipe.mjs +0 -34
  45. package/esm2020/lib/pipes/sanitize.pipe.mjs +0 -21
  46. package/esm2020/lib/services/chat.service.mjs +0 -14
  47. package/esm2020/muraai-mnl-chat.mjs +0 -5
  48. package/esm2020/public-api.mjs +0 -25
  49. package/fesm2015/muraai-mnl-chat.mjs +0 -1426
  50. package/fesm2015/muraai-mnl-chat.mjs.map +0 -1
  51. package/fesm2020/muraai-mnl-chat.mjs +0 -1424
  52. package/fesm2020/muraai-mnl-chat.mjs.map +0 -1
@@ -0,0 +1,1507 @@
1
+ import * as i1$2 from '@angular/common/http';
2
+ import { HttpClientModule } from '@angular/common/http';
3
+ import * as i3 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i0 from '@angular/core';
6
+ import { NgModule, Injectable, Pipe, EventEmitter, Output, Input, ViewEncapsulation, Component, ViewChild, HostListener, ViewChildren, APP_INITIALIZER } from '@angular/core';
7
+ import { AppConfigService } from '@muraai/mnl-commons';
8
+ import { MatButtonModule } from '@angular/material/button';
9
+ import { MatListModule } from '@angular/material/list';
10
+ import { MatIconModule } from '@angular/material/icon';
11
+ import { MatInputModule } from '@angular/material/input';
12
+ import { MatFormFieldModule } from '@angular/material/form-field';
13
+ import { MatCardModule } from '@angular/material/card';
14
+ import * as i1$1 from '@angular/forms';
15
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
16
+ import { MatMenuModule } from '@angular/material/menu';
17
+ import * as i2 from '@angular/material/tooltip';
18
+ import { MatTooltipModule } from '@angular/material/tooltip';
19
+ import { map } from 'rxjs/operators';
20
+ import { Subject, debounceTime, takeUntil } from 'rxjs';
21
+ import * as i1 from '@angular/platform-browser';
22
+
23
+ function modules() {
24
+ return [
25
+ MatButtonModule,
26
+ MatFormFieldModule,
27
+ MatIconModule,
28
+ MatInputModule,
29
+ FormsModule,
30
+ ReactiveFormsModule,
31
+ MatMenuModule,
32
+ MatListModule,
33
+ MatCardModule,
34
+ MatTooltipModule
35
+ ];
36
+ }
37
+ class ChatMaterialModule {
38
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatMaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
39
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: ChatMaterialModule, imports: [MatButtonModule,
40
+ MatFormFieldModule,
41
+ MatIconModule,
42
+ MatInputModule,
43
+ FormsModule,
44
+ ReactiveFormsModule,
45
+ MatMenuModule,
46
+ MatListModule,
47
+ MatCardModule,
48
+ MatTooltipModule], exports: [MatButtonModule,
49
+ MatFormFieldModule,
50
+ MatIconModule,
51
+ MatInputModule,
52
+ FormsModule,
53
+ ReactiveFormsModule,
54
+ MatMenuModule,
55
+ MatListModule,
56
+ MatCardModule,
57
+ MatTooltipModule] }); }
58
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatMaterialModule, imports: [modules(), MatButtonModule,
59
+ MatFormFieldModule,
60
+ MatIconModule,
61
+ MatInputModule,
62
+ FormsModule,
63
+ ReactiveFormsModule,
64
+ MatMenuModule,
65
+ MatListModule,
66
+ MatCardModule,
67
+ MatTooltipModule] }); }
68
+ }
69
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatMaterialModule, decorators: [{
70
+ type: NgModule,
71
+ args: [{
72
+ imports: modules(),
73
+ exports: modules()
74
+ }]
75
+ }] });
76
+
77
+ class ChatService {
78
+ constructor() { }
79
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
80
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatService, providedIn: 'root' }); }
81
+ }
82
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatService, decorators: [{
83
+ type: Injectable,
84
+ args: [{
85
+ providedIn: 'root'
86
+ }]
87
+ }], ctorParameters: () => [] });
88
+
89
+ var MessageType;
90
+ (function (MessageType) {
91
+ MessageType[MessageType["Text"] = 1] = "Text";
92
+ MessageType[MessageType["File"] = 2] = "File";
93
+ MessageType[MessageType["Image"] = 3] = "Image";
94
+ })(MessageType || (MessageType = {}));
95
+
96
+ class Window {
97
+ constructor(participant, isLoadingHistory, isCollapsed) {
98
+ this.messages = [];
99
+ this.newMessage = "";
100
+ // UI Behavior properties
101
+ this.isCollapsed = false;
102
+ this.isLoadingHistory = false;
103
+ this.hasFocus = false;
104
+ this.hasMoreMessages = true;
105
+ this.historyPage = 0;
106
+ this.participant = participant;
107
+ this.messages = [];
108
+ this.isLoadingHistory = isLoadingHistory;
109
+ this.hasFocus = false; // This will be triggered when the 'newMessage' input gets the current focus
110
+ this.isCollapsed = isCollapsed;
111
+ this.hasMoreMessages = false;
112
+ this.historyPage = 0;
113
+ }
114
+ }
115
+
116
+ var ChatParticipantStatus;
117
+ (function (ChatParticipantStatus) {
118
+ ChatParticipantStatus[ChatParticipantStatus["Online"] = 0] = "Online";
119
+ ChatParticipantStatus[ChatParticipantStatus["Busy"] = 1] = "Busy";
120
+ ChatParticipantStatus[ChatParticipantStatus["Away"] = 2] = "Away";
121
+ ChatParticipantStatus[ChatParticipantStatus["Offline"] = 3] = "Offline";
122
+ })(ChatParticipantStatus || (ChatParticipantStatus = {}));
123
+
124
+ var ScrollDirection;
125
+ (function (ScrollDirection) {
126
+ ScrollDirection[ScrollDirection["Top"] = 0] = "Top";
127
+ ScrollDirection[ScrollDirection["Bottom"] = 1] = "Bottom";
128
+ })(ScrollDirection || (ScrollDirection = {}));
129
+
130
+ class ChatAdapter {
131
+ constructor() {
132
+ // ### Abstract adapter methods ###
133
+ // Event handlers
134
+ /** @internal */
135
+ this.friendsListChangedHandler = (participantsResponse) => { };
136
+ /** @internal */
137
+ this.messageReceivedHandler = (participant, message) => { };
138
+ }
139
+ // ### Adapter/Chat income/ingress events ###
140
+ onFriendsListChanged(participantsResponse) {
141
+ this.friendsListChangedHandler(participantsResponse);
142
+ }
143
+ onMessageReceived(participant, message) {
144
+ this.messageReceivedHandler(participant, message);
145
+ }
146
+ }
147
+
148
+ // import { User } from "./user";
149
+ /**
150
+ * @description Chat Adapter decorator class that adds pagination to load the history of messagesr.
151
+ * You will need an existing @see ChatAdapter implementation
152
+ */
153
+ class PagedHistoryChatAdapter extends ChatAdapter {
154
+ }
155
+
156
+ /*import { IFileUploadAdapter } from './file-upload-adapter';
157
+ import { HttpClient, HttpRequest, HttpEventType, HttpResponse, HttpHeaders } from '@angular/common/http';
158
+ import { Observable, Subject } from 'rxjs';
159
+ import { User } from './user';
160
+ import { Message } from './message';*/
161
+ class DefaultFileUploadAdapter {
162
+ /**
163
+ * @summary Basic file upload adapter implementation for HTTP request form file consumption
164
+ * @param _serverEndpointUrl The API endpoint full qualified address that will receive a form file to process and return the metadata.
165
+ */
166
+ constructor(_serverEndpointUrl, _http) {
167
+ this._serverEndpointUrl = _serverEndpointUrl;
168
+ this._http = _http;
169
+ }
170
+ uploadFile(file, participantId) {
171
+ const formData = new FormData();
172
+ //formData.append('ng-chat-sender-userid', currentUserId);
173
+ formData.append('ng-chat-participant-id', participantId);
174
+ formData.append('file', file, file.name);
175
+ return this._http.post(this._serverEndpointUrl, formData);
176
+ // TODO: Leaving this if we want to track upload progress in detail in the future. Might need a different Subject generic type wrapper
177
+ // const fileRequest = new HttpRequest('POST', this._serverEndpointUrl, formData, {
178
+ // reportProgress: true
179
+ // });
180
+ // const uploadProgress = new Subject<number>();
181
+ // const uploadStatus = uploadProgress.asObservable();
182
+ //const responsePromise = new Subject<Message>();
183
+ // this._http
184
+ // .request(fileRequest)
185
+ // .subscribe(event => {
186
+ // // if (event.type == HttpEventType.UploadProgress)
187
+ // // {
188
+ // // const percentDone = Math.round(100 * event.loaded / event.total);
189
+ // // uploadProgress.next(percentDone);
190
+ // // }
191
+ // // else if (event instanceof HttpResponse)
192
+ // // {
193
+ // // uploadProgress.complete();
194
+ // // }
195
+ // });
196
+ }
197
+ }
198
+
199
+ var Theme;
200
+ (function (Theme) {
201
+ Theme["Custom"] = "custom-theme";
202
+ Theme["Light"] = "light-theme";
203
+ Theme["Dark"] = "dark-theme";
204
+ })(Theme || (Theme = {}));
205
+
206
+ // Poached from: https://github.com/Steve-Fenton/TypeScriptUtilities
207
+ // @dynamic
208
+ class Guid {
209
+ static newGuid() {
210
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
211
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
212
+ return v.toString(16);
213
+ });
214
+ }
215
+ }
216
+
217
+ var ChatParticipantType;
218
+ (function (ChatParticipantType) {
219
+ ChatParticipantType[ChatParticipantType["User"] = 0] = "User";
220
+ ChatParticipantType[ChatParticipantType["Group"] = 1] = "Group";
221
+ })(ChatParticipantType || (ChatParticipantType = {}));
222
+
223
+ class Group {
224
+ constructor(participants) {
225
+ this.id = Guid.newGuid();
226
+ this.participantType = ChatParticipantType.Group;
227
+ this.chattingTo = participants;
228
+ this.status = ChatParticipantStatus.Online;
229
+ // TODO: Add some customization for this in future releases
230
+ this.displayName = participants.map((p) => p.displayName).sort((first, second) => second > first ? -1 : 1).join(", ");
231
+ }
232
+ }
233
+
234
+ class MessageCounter {
235
+ static formatUnreadMessagesTotal(totalUnreadMessages) {
236
+ if (totalUnreadMessages > 0) {
237
+ if (totalUnreadMessages > 99)
238
+ return "99+";
239
+ else
240
+ return String(totalUnreadMessages);
241
+ }
242
+ // Empty fallback.
243
+ return "";
244
+ }
245
+ /**
246
+ * Returns a formatted string containing the total unread messages of a chat window.
247
+ * @param window The window instance to count the unread total messages.
248
+ * @param currentUserId The current chat instance user id. In this context it would be the sender.
249
+ */
250
+ static unreadMessagesTotal(window, currentUserId) {
251
+ let totalUnreadMessages = 0;
252
+ if (window) {
253
+ totalUnreadMessages = window.messages.filter(x => x.fromId != currentUserId && !x.dateSeen).length;
254
+ }
255
+ return MessageCounter.formatUnreadMessagesTotal(totalUnreadMessages);
256
+ }
257
+ }
258
+
259
+ function chatParticipantStatusDescriptor(status, localization) {
260
+ const currentStatus = ChatParticipantStatus[status].toString().toLowerCase();
261
+ return localization.statusDescription[currentStatus];
262
+ }
263
+
264
+ /*
265
+ * Sanitizes an URL resource
266
+ */
267
+ class SanitizePipe {
268
+ constructor(sanitizer) {
269
+ this.sanitizer = sanitizer;
270
+ }
271
+ transform(url) {
272
+ return this.sanitizer.bypassSecurityTrustResourceUrl(url);
273
+ }
274
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SanitizePipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe }); }
275
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: SanitizePipe, isStandalone: false, name: "sanitize" }); }
276
+ }
277
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SanitizePipe, decorators: [{
278
+ type: Pipe,
279
+ args: [{ name: 'sanitize', standalone: false }]
280
+ }], ctorParameters: () => [{ type: i1.DomSanitizer }] });
281
+
282
+ class FirstLetterPipe {
283
+ transform(value) {
284
+ // const parts = value.split(" ");
285
+ // const firstLetter = parts[0][0];
286
+ // const secondLetters = parts.length > 1 ? parts[1].slice(0, 2) : '';
287
+ let parts = value?.split(" ");
288
+ let firstLetter = parts[0].slice(0, 1);
289
+ let secondLetters = parts.length > 1 ? parts[1].slice(0, 1) : '';
290
+ return (firstLetter.toUpperCase() + secondLetters.toUpperCase());
291
+ }
292
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FirstLetterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
293
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: FirstLetterPipe, isStandalone: false, name: "firstLetterPipe" }); }
294
+ }
295
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: FirstLetterPipe, decorators: [{
296
+ type: Pipe,
297
+ args: [{ name: 'firstLetterPipe', standalone: false }]
298
+ }] });
299
+
300
+ /* eslint-disable */
301
+ class NgChatFriendsListComponent {
302
+ constructor() {
303
+ this.participantsInteractedWith = [];
304
+ this.onParticipantClicked = new EventEmitter();
305
+ this.onOptionPromptCanceled = new EventEmitter();
306
+ this.onOptionPromptConfirmed = new EventEmitter();
307
+ this.hideFriendsList = false;
308
+ this.onCloseWindowClick = new EventEmitter();
309
+ this.searchInputChanged = new EventEmitter();
310
+ this.searchInputSubject = new Subject();
311
+ this.selectedUsersFromFriendsList = [];
312
+ this.searchInput = '';
313
+ this.destroy$ = new Subject();
314
+ // Exposes enums and functions for the ng-template
315
+ this.ChatParticipantStatus = ChatParticipantStatus;
316
+ this.chatParticipantStatusDescriptor = chatParticipantStatusDescriptor;
317
+ this.cleanUpUserSelection = () => this.selectedUsersFromFriendsList = [];
318
+ }
319
+ ngOnInit() {
320
+ this.initializeSearchInputListener();
321
+ }
322
+ ngOnChanges(changes) {
323
+ if (this.currentActiveOption) {
324
+ const currentOptionTriggeredBy = this.currentActiveOption && this.currentActiveOption.chattingTo.participant.id;
325
+ const isActivatedUserInSelectedList = (this.selectedUsersFromFriendsList.filter(item => item.id == currentOptionTriggeredBy)).length > 0;
326
+ if (!isActivatedUserInSelectedList) {
327
+ this.selectedUsersFromFriendsList = this.selectedUsersFromFriendsList.concat(this.currentActiveOption.chattingTo.participant);
328
+ }
329
+ }
330
+ }
331
+ get filteredParticipants() {
332
+ const query = (this.searchInput || '').trim().toLowerCase();
333
+ if (!query)
334
+ return this.participants;
335
+ return this.participants.filter(participant => {
336
+ const name = participant.displayName?.toLowerCase() || '';
337
+ if (name.includes(query))
338
+ return true;
339
+ const metadata = participant.metadata;
340
+ if (!metadata)
341
+ return false;
342
+ const fieldsToSearch = [
343
+ metadata.primary,
344
+ metadata.secondary,
345
+ metadata.tertiary
346
+ ];
347
+ return fieldsToSearch.some(field => typeof field === 'string' && field.toLowerCase().includes(query));
348
+ });
349
+ }
350
+ isUserSelectedFromFriendsList(user) {
351
+ return (this.selectedUsersFromFriendsList.filter(item => item.id == user.id)).length > 0;
352
+ }
353
+ unreadMessagesTotalByParticipant(participant) {
354
+ let openedWindow = this.windows.find(x => x.participant.id == participant.id);
355
+ if (openedWindow) {
356
+ return MessageCounter.unreadMessagesTotal(openedWindow, this.userId);
357
+ }
358
+ else {
359
+ let totalUnreadMessages = this.participantsResponse
360
+ .filter(x => x.participant.id == participant.id && !this.participantsInteractedWith.find(u => u.id == participant.id) && x.metadata && x.metadata.totalUnreadMessages > 0)
361
+ .map((participantResponse) => {
362
+ return participantResponse.metadata.totalUnreadMessages;
363
+ })[0];
364
+ return MessageCounter.formatUnreadMessagesTotal(totalUnreadMessages);
365
+ }
366
+ }
367
+ // Toggle friends list visibility
368
+ onChatTitleClicked() {
369
+ this.isCollapsed = !this.isCollapsed;
370
+ }
371
+ onFriendsListCheckboxChange(selectedUser, isChecked) {
372
+ if (isChecked) {
373
+ this.selectedUsersFromFriendsList.push(selectedUser);
374
+ }
375
+ else {
376
+ this.selectedUsersFromFriendsList.splice(this.selectedUsersFromFriendsList.indexOf(selectedUser), 1);
377
+ }
378
+ }
379
+ onUserClick(clickedUser) {
380
+ this.onParticipantClicked.emit(clickedUser);
381
+ }
382
+ onFriendsListActionCancelClicked() {
383
+ this.onOptionPromptCanceled.emit();
384
+ this.cleanUpUserSelection();
385
+ }
386
+ onFriendsListActionConfirmClicked() {
387
+ this.onOptionPromptConfirmed.emit(this.selectedUsersFromFriendsList);
388
+ this.cleanUpUserSelection();
389
+ }
390
+ onCloseWindow() {
391
+ this.searchInput = '';
392
+ this.onCloseWindowClick.emit(true);
393
+ }
394
+ onSearchInputChange(value) {
395
+ this.searchInput = value;
396
+ this.searchInputSubject.next(value);
397
+ }
398
+ initializeSearchInputListener() {
399
+ this.searchInputSubject.pipe(debounceTime(300), takeUntil(this.destroy$)).subscribe(value => {
400
+ this.searchInputChanged.emit(value);
401
+ });
402
+ }
403
+ ngOnDestroy() {
404
+ this.destroy$.next();
405
+ this.destroy$.complete();
406
+ }
407
+ getParticipantTooltip(participant) {
408
+ const { metadata, displayName } = participant;
409
+ if (!metadata)
410
+ return displayName;
411
+ const { primary, secondary, tertiary } = metadata;
412
+ const parts = [primary, secondary, tertiary].filter(Boolean);
413
+ return parts.length > 0 ? parts.join(' | ') : displayName;
414
+ }
415
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChatFriendsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
416
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: NgChatFriendsListComponent, isStandalone: false, selector: "ng-chat-friends-list", inputs: { participants: "participants", participantsResponse: "participantsResponse", participantsInteractedWith: "participantsInteractedWith", windows: "windows", userId: "userId", localization: "localization", shouldDisplay: "shouldDisplay", isCollapsed: "isCollapsed", searchEnabled: "searchEnabled", currentActiveOption: "currentActiveOption", hideFriendsList: "hideFriendsList" }, outputs: { onParticipantClicked: "onParticipantClicked", onOptionPromptCanceled: "onOptionPromptCanceled", onOptionPromptConfirmed: "onOptionPromptConfirmed", onCloseWindowClick: "onCloseWindowClick", searchInputChanged: "searchInputChanged" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"shouldDisplay\" id=\"ng-chat-people\" [ngClass]=\"{'primary-outline-color': true, 'primary-background': true, 'ng-chat-people-collapsed': isCollapsed}\">\n\t<a href=\"javascript:void(0);\" class=\"ng-chat-title secondary-background shadowed\" (click)=\"onChatTitleClicked()\">\n\t\t<span>\n\t\t\t{{localization.title}}\n\t\t</span>\n\t<a href=\"javascript:void(0);\" class=\" ng-chat-close \" (click)=\"onCloseWindow()\" >&#x2715;</a>\n\t</a>\n\t<div *ngIf=\"currentActiveOption\" class=\"ng-chat-people-actions\" (click)=\"onFriendsListActionCancelClicked()\">\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\">\n\t\t\t<i class=\"remove-icon\"></i>\n\t\t</a>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\" (click)=\"onFriendsListActionConfirmClicked()\">\n\t\t\t<i class=\"check-icon\"></i>\n\t\t</a>\n\t</div>\n\t<input *ngIf=\"searchEnabled\" id=\"ng-chat-search_friend\" class=\"friends-search-bar\" type=\"search\" [placeholder]=\"localization.searchPlaceholder\" [(ngModel)]=\"searchInput\" \n\t(ngModelChange)=\"onSearchInputChange($event)\"/>\n\t<ul id=\"ng-chat-users\" *ngIf=\"!isCollapsed\" [ngClass]=\"{'offset-search': searchEnabled}\">\n\t\t<li *ngFor=\"let user of filteredParticipants\">\n\t\t\t<input \n\t\t\t\t*ngIf=\"currentActiveOption && currentActiveOption.validateContext(user)\" \n\t\t\t\ttype=\"checkbox\" \n\t\t\t\tclass=\"ng-chat-users-checkbox\" \n\t\t\t\t(change)=\"onFriendsListCheckboxChange(user, $event.target.checked)\" \n\t\t\t\t[checked]=\"isUserSelectedFromFriendsList(user)\"/>\n\t\t\t<div [ngClass]=\"{'ng-chat-friends-list-selectable-offset': currentActiveOption, 'ng-chat-friends-list-container': true}\" (click)=\"onUserClick(user)\">\n\t\t\t\t<!-- <div *ngIf=\"!user.avatar\" class=\"icon-wrapper\">\n\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t</div> -->\n\t\t\t\t<div class=\"user\" *ngIf=\"!user.avatar\">\n\t\t\t\t\t{{user.displayName|firstLetterPipe}}\n\n\t\t\t\t</div>\n\n\t\t\t\t<img *ngIf=\"user.avatar\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"user.avatar | sanitize\"/>\n\t\t\t\t<div class=\"\"></div>\n\t\t\t\t<strong [matTooltip]=\"getParticipantTooltip(user)\">{{user.displayName}}</strong>\n\t\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': user.status === ChatParticipantStatus.Online, 'busy': user.status === ChatParticipantStatus.Busy, 'away': user.status === ChatParticipantStatus.Away, 'offline': user.status === ChatParticipantStatus.Offline}\" [matTooltip]=\"chatParticipantStatusDescriptor(user.status, localization)\"></span>\n\t\t\t\t<span *ngIf=\"unreadMessagesTotalByParticipant(user).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotalByParticipant(user)}}</span>\n\t\t\t</div>\n\t\t</li>\n\t</ul>\n</div>", styles: ["#ng-chat-people{position:relative;width:240px;height:360px;border-width:1px;border-style:solid;margin-right:20px;box-shadow:0 4px 8px #00000040;border-bottom:0;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;margin-bottom:10px!important;border:1px solid grey!important}#ng-chat-people.ng-chat-people-collapsed{height:30px}#ng-chat-search_friend{display:block;padding:7px 10px;margin:10px auto 0;width:calc(100% - 20px);font-size:.9em;border-radius:20px!important;border:.5px solid grey!important}#ng-chat-users{padding:0 10px;list-style:none;margin:0;overflow:auto;position:absolute;top:42px;bottom:0;width:100%;box-sizing:border-box}#ng-chat-users.offset-search{top:84px}#ng-chat-users .ng-chat-users-checkbox{float:left;margin-right:5px;margin-top:8px}#ng-chat-users li{clear:both;margin-bottom:10px;overflow:hidden;cursor:pointer;max-height:30px}#ng-chat-users li>.ng-chat-friends-list-selectable-offset{margin-left:22px}#ng-chat-users li .ng-chat-friends-list-container{display:inline-block;width:100%}#ng-chat-users li>.ng-chat-friends-list-selectable-offset.ng-chat-friends-list-container{display:block;width:auto}#ng-chat-users li .ng-chat-friends-list-container>img.avatar,#ng-chat-users li .ng-chat-friends-list-container>.user{float:left;display:flex;justify-content:center;margin-right:5px}#ng-chat-users li .ng-chat-friends-list-container>.user{overflow:hidden;width:30px;height:30px}#ng-chat-users li .ng-chat-friends-list-container>.user>i{color:#fff;transform:scale(.7)}#ng-chat-users li .ng-chat-friends-list-container>strong{float:left;line-height:30px;font-size:.8em;max-width:57%;max-height:30px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#ng-chat-users li .ng-chat-friends-list-container>.ng-chat-participant-status{float:right}.ng-chat-people-actions{position:absolute;top:4px;right:5px;margin:0;padding:0;z-index:2}.ng-chat-people-actions>a.ng-chat-people-action{display:inline-block;width:21px;height:21px;margin-right:8px;text-decoration:none;border:none;border-radius:25px;padding:1px}@media only screen and (max-width: 581px){#ng-chat-people{width:300px;height:360px;margin-right:0}}.user{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background-color:#00b04c;color:#fff;font-size:12px;font-family:inherit}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: SanitizePipe, name: "sanitize" }, { kind: "pipe", type: FirstLetterPipe, name: "firstLetterPipe" }], encapsulation: i0.ViewEncapsulation.None }); }
417
+ }
418
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChatFriendsListComponent, decorators: [{
419
+ type: Component,
420
+ args: [{ selector: 'ng-chat-friends-list', encapsulation: ViewEncapsulation.None, standalone: false, template: "<div *ngIf=\"shouldDisplay\" id=\"ng-chat-people\" [ngClass]=\"{'primary-outline-color': true, 'primary-background': true, 'ng-chat-people-collapsed': isCollapsed}\">\n\t<a href=\"javascript:void(0);\" class=\"ng-chat-title secondary-background shadowed\" (click)=\"onChatTitleClicked()\">\n\t\t<span>\n\t\t\t{{localization.title}}\n\t\t</span>\n\t<a href=\"javascript:void(0);\" class=\" ng-chat-close \" (click)=\"onCloseWindow()\" >&#x2715;</a>\n\t</a>\n\t<div *ngIf=\"currentActiveOption\" class=\"ng-chat-people-actions\" (click)=\"onFriendsListActionCancelClicked()\">\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\">\n\t\t\t<i class=\"remove-icon\"></i>\n\t\t</a>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-people-action\" (click)=\"onFriendsListActionConfirmClicked()\">\n\t\t\t<i class=\"check-icon\"></i>\n\t\t</a>\n\t</div>\n\t<input *ngIf=\"searchEnabled\" id=\"ng-chat-search_friend\" class=\"friends-search-bar\" type=\"search\" [placeholder]=\"localization.searchPlaceholder\" [(ngModel)]=\"searchInput\" \n\t(ngModelChange)=\"onSearchInputChange($event)\"/>\n\t<ul id=\"ng-chat-users\" *ngIf=\"!isCollapsed\" [ngClass]=\"{'offset-search': searchEnabled}\">\n\t\t<li *ngFor=\"let user of filteredParticipants\">\n\t\t\t<input \n\t\t\t\t*ngIf=\"currentActiveOption && currentActiveOption.validateContext(user)\" \n\t\t\t\ttype=\"checkbox\" \n\t\t\t\tclass=\"ng-chat-users-checkbox\" \n\t\t\t\t(change)=\"onFriendsListCheckboxChange(user, $event.target.checked)\" \n\t\t\t\t[checked]=\"isUserSelectedFromFriendsList(user)\"/>\n\t\t\t<div [ngClass]=\"{'ng-chat-friends-list-selectable-offset': currentActiveOption, 'ng-chat-friends-list-container': true}\" (click)=\"onUserClick(user)\">\n\t\t\t\t<!-- <div *ngIf=\"!user.avatar\" class=\"icon-wrapper\">\n\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t</div> -->\n\t\t\t\t<div class=\"user\" *ngIf=\"!user.avatar\">\n\t\t\t\t\t{{user.displayName|firstLetterPipe}}\n\n\t\t\t\t</div>\n\n\t\t\t\t<img *ngIf=\"user.avatar\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"user.avatar | sanitize\"/>\n\t\t\t\t<div class=\"\"></div>\n\t\t\t\t<strong [matTooltip]=\"getParticipantTooltip(user)\">{{user.displayName}}</strong>\n\t\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': user.status === ChatParticipantStatus.Online, 'busy': user.status === ChatParticipantStatus.Busy, 'away': user.status === ChatParticipantStatus.Away, 'offline': user.status === ChatParticipantStatus.Offline}\" [matTooltip]=\"chatParticipantStatusDescriptor(user.status, localization)\"></span>\n\t\t\t\t<span *ngIf=\"unreadMessagesTotalByParticipant(user).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotalByParticipant(user)}}</span>\n\t\t\t</div>\n\t\t</li>\n\t</ul>\n</div>", styles: ["#ng-chat-people{position:relative;width:240px;height:360px;border-width:1px;border-style:solid;margin-right:20px;box-shadow:0 4px 8px #00000040;border-bottom:0;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;margin-bottom:10px!important;border:1px solid grey!important}#ng-chat-people.ng-chat-people-collapsed{height:30px}#ng-chat-search_friend{display:block;padding:7px 10px;margin:10px auto 0;width:calc(100% - 20px);font-size:.9em;border-radius:20px!important;border:.5px solid grey!important}#ng-chat-users{padding:0 10px;list-style:none;margin:0;overflow:auto;position:absolute;top:42px;bottom:0;width:100%;box-sizing:border-box}#ng-chat-users.offset-search{top:84px}#ng-chat-users .ng-chat-users-checkbox{float:left;margin-right:5px;margin-top:8px}#ng-chat-users li{clear:both;margin-bottom:10px;overflow:hidden;cursor:pointer;max-height:30px}#ng-chat-users li>.ng-chat-friends-list-selectable-offset{margin-left:22px}#ng-chat-users li .ng-chat-friends-list-container{display:inline-block;width:100%}#ng-chat-users li>.ng-chat-friends-list-selectable-offset.ng-chat-friends-list-container{display:block;width:auto}#ng-chat-users li .ng-chat-friends-list-container>img.avatar,#ng-chat-users li .ng-chat-friends-list-container>.user{float:left;display:flex;justify-content:center;margin-right:5px}#ng-chat-users li .ng-chat-friends-list-container>.user{overflow:hidden;width:30px;height:30px}#ng-chat-users li .ng-chat-friends-list-container>.user>i{color:#fff;transform:scale(.7)}#ng-chat-users li .ng-chat-friends-list-container>strong{float:left;line-height:30px;font-size:.8em;max-width:57%;max-height:30px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#ng-chat-users li .ng-chat-friends-list-container>.ng-chat-participant-status{float:right}.ng-chat-people-actions{position:absolute;top:4px;right:5px;margin:0;padding:0;z-index:2}.ng-chat-people-actions>a.ng-chat-people-action{display:inline-block;width:21px;height:21px;margin-right:8px;text-decoration:none;border:none;border-radius:25px;padding:1px}@media only screen and (max-width: 581px){#ng-chat-people{width:300px;height:360px;margin-right:0}}.user{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;background-color:#00b04c;color:#fff;font-size:12px;font-family:inherit}\n"] }]
421
+ }], ctorParameters: () => [], propDecorators: { participants: [{
422
+ type: Input
423
+ }], participantsResponse: [{
424
+ type: Input
425
+ }], participantsInteractedWith: [{
426
+ type: Input
427
+ }], windows: [{
428
+ type: Input
429
+ }], userId: [{
430
+ type: Input
431
+ }], localization: [{
432
+ type: Input
433
+ }], shouldDisplay: [{
434
+ type: Input
435
+ }], isCollapsed: [{
436
+ type: Input
437
+ }], searchEnabled: [{
438
+ type: Input
439
+ }], currentActiveOption: [{
440
+ type: Input
441
+ }], onParticipantClicked: [{
442
+ type: Output
443
+ }], onOptionPromptCanceled: [{
444
+ type: Output
445
+ }], onOptionPromptConfirmed: [{
446
+ type: Output
447
+ }], hideFriendsList: [{
448
+ type: Input
449
+ }], onCloseWindowClick: [{
450
+ type: Output
451
+ }], searchInputChanged: [{
452
+ type: Output
453
+ }] } });
454
+
455
+ class Message {
456
+ constructor() {
457
+ this.type = MessageType.Text;
458
+ }
459
+ }
460
+
461
+ let emojiDictionary = [
462
+ { patterns: [':)', ':-)', '=)'], unicode: '😃' },
463
+ { patterns: [':D', ':-D', '=D'], unicode: '😀' },
464
+ { patterns: [':(', ':-(', '=('], unicode: '🙁' },
465
+ { patterns: [':|', ':-|', '=|'], unicode: '😐' },
466
+ { patterns: [':*', ':-*', '=*'], unicode: '😙' },
467
+ { patterns: ['T_T', 'T.T'], unicode: '😭' },
468
+ { patterns: [':O', ':-O', '=O', ':o', ':-o', '=o'], unicode: '😮' },
469
+ { patterns: [':P', ':-P', '=P', ':p', ':-p', '=p'], unicode: '😋' },
470
+ { patterns: ['>.<'], unicode: '😣' },
471
+ { patterns: ['@.@'], unicode: '😵' },
472
+ { patterns: ['*.*'], unicode: '😍' },
473
+ { patterns: ['<3'], unicode: '❤️' },
474
+ { patterns: ['^.^'], unicode: '😊' },
475
+ { patterns: [':+1'], unicode: '👍' },
476
+ { patterns: [':-1'], unicode: '👎' }
477
+ ];
478
+ /*
479
+ * Transforms common emoji text to UTF encoded emojis
480
+ */
481
+ class EmojifyPipe {
482
+ transform(message, pipeEnabled) {
483
+ if (pipeEnabled && message && message.length > 1) {
484
+ emojiDictionary.forEach(emoji => {
485
+ emoji.patterns.forEach(pattern => {
486
+ message = message.replace(pattern, emoji.unicode);
487
+ });
488
+ });
489
+ }
490
+ return message;
491
+ }
492
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EmojifyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
493
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: EmojifyPipe, isStandalone: false, name: "emojify" }); }
494
+ }
495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: EmojifyPipe, decorators: [{
496
+ type: Pipe,
497
+ args: [{ name: 'emojify', standalone: false }]
498
+ }] });
499
+
500
+ /*
501
+ * Transforms text containing URLs or E-mails to valid links/mailtos
502
+ */
503
+ class LinkfyPipe {
504
+ transform(message, pipeEnabled) {
505
+ if (pipeEnabled && message && message.length > 1) {
506
+ let replacedText;
507
+ let replacePatternProtocol;
508
+ let replacePatternWWW;
509
+ let replacePatternMailTo;
510
+ // URLs starting with http://, https://, or ftp://
511
+ replacePatternProtocol = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
512
+ replacedText = message.replace(replacePatternProtocol, '<a href="$1" target="_blank">$1</a>');
513
+ // URLs starting with "www." (ignoring // before it).
514
+ replacePatternWWW = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
515
+ replacedText = replacedText.replace(replacePatternWWW, '$1<a href="http://$2" target="_blank">$2</a>');
516
+ // Change email addresses to mailto:: links.
517
+ replacePatternMailTo = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
518
+ replacedText = replacedText.replace(replacePatternMailTo, '<a href="mailto:$1">$1</a>');
519
+ return replacedText;
520
+ }
521
+ else
522
+ return message;
523
+ }
524
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LinkfyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
525
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: LinkfyPipe, isStandalone: false, name: "linkfy" }); }
526
+ }
527
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: LinkfyPipe, decorators: [{
528
+ type: Pipe,
529
+ args: [{ name: 'linkfy', standalone: false }]
530
+ }] });
531
+
532
+ /*
533
+ * Renders the display name of a participant in a group based on who's sent the message
534
+ */
535
+ class GroupMessageDisplayNamePipe {
536
+ transform(participant, message) {
537
+ if (participant && participant.participantType == ChatParticipantType.Group) {
538
+ let group = participant;
539
+ let userIndex = group.chattingTo.findIndex(x => x.id == message.fromId);
540
+ return group.chattingTo[userIndex >= 0 ? userIndex : 0].displayName;
541
+ }
542
+ else
543
+ return "";
544
+ }
545
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: GroupMessageDisplayNamePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
546
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: GroupMessageDisplayNamePipe, isStandalone: false, name: "groupMessageDisplayName" }); }
547
+ }
548
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: GroupMessageDisplayNamePipe, decorators: [{
549
+ type: Pipe,
550
+ args: [{ name: 'groupMessageDisplayName', standalone: false }]
551
+ }] });
552
+
553
+ /* eslint-disable */
554
+ class NgChatWindowComponent {
555
+ constructor() {
556
+ this.emojisEnabled = true;
557
+ this.linkfyEnabled = true;
558
+ this.showMessageDate = true;
559
+ this.messageDatePipeFormat = "short";
560
+ this.hasPagedHistory = true;
561
+ this.onChatWindowClosed = new EventEmitter();
562
+ this.onMessagesSeen = new EventEmitter();
563
+ this.onMessageSent = new EventEmitter();
564
+ this.onTabTriggered = new EventEmitter();
565
+ this.onOptionTriggered = new EventEmitter();
566
+ this.onLoadHistoryTriggered = new EventEmitter();
567
+ // File upload state
568
+ this.fileUploadersInUse = []; // Id bucket of uploaders in use
569
+ // Exposes enums and functions for the ng-template
570
+ this.ChatParticipantType = ChatParticipantType;
571
+ this.ChatParticipantStatus = ChatParticipantStatus;
572
+ this.MessageType = MessageType;
573
+ this.chatParticipantStatusDescriptor = chatParticipantStatusDescriptor;
574
+ }
575
+ defaultWindowOptions(currentWindow) {
576
+ if (this.showOptions && currentWindow.participant.participantType == ChatParticipantType.User) {
577
+ return [{
578
+ isActive: false,
579
+ chattingTo: currentWindow,
580
+ validateContext: (participant) => {
581
+ return participant.participantType == ChatParticipantType.User;
582
+ },
583
+ displayLabel: 'Add People' // TODO: Localize this
584
+ }];
585
+ }
586
+ return [];
587
+ }
588
+ // Asserts if a user avatar is visible in a chat cluster
589
+ isAvatarVisible(window, message, index) {
590
+ if (message.fromId != this.userId) {
591
+ if (index == 0) {
592
+ return true; // First message, good to show the thumbnail
593
+ }
594
+ else {
595
+ // Check if the previous message belongs to the same user, if it belongs there is no need to show the avatar again to form the message cluster
596
+ if (window.messages[index - 1].fromId != message.fromId) {
597
+ return true;
598
+ }
599
+ }
600
+ }
601
+ return false;
602
+ }
603
+ getChatWindowAvatar(participant, message) {
604
+ if (participant.participantType == ChatParticipantType.User) {
605
+ return participant.avatar;
606
+ }
607
+ else if (participant.participantType == ChatParticipantType.Group) {
608
+ let group = participant;
609
+ let userIndex = group.chattingTo.findIndex(x => x.id == message.fromId);
610
+ return group.chattingTo[userIndex >= 0 ? userIndex : 0].avatar;
611
+ }
612
+ return null;
613
+ }
614
+ isUploadingFile(window) {
615
+ const fileUploadInstanceId = this.getUniqueFileUploadInstanceId(window);
616
+ return this.fileUploadersInUse.indexOf(fileUploadInstanceId) > -1;
617
+ }
618
+ // Generates a unique file uploader id for each participant
619
+ getUniqueFileUploadInstanceId(window) {
620
+ if (window && window.participant) {
621
+ return `ng-chat-file-upload-${window.participant.id}`;
622
+ }
623
+ return 'ng-chat-file-upload';
624
+ }
625
+ unreadMessagesTotal(window) {
626
+ return MessageCounter.unreadMessagesTotal(window, this.userId);
627
+ }
628
+ // Scrolls a chat window message flow to the bottom
629
+ scrollChatWindow(window, direction) {
630
+ if (!window.isCollapsed) {
631
+ setTimeout(() => {
632
+ if (this.chatMessages) {
633
+ let element = this.chatMessages.nativeElement;
634
+ let position = (direction === ScrollDirection.Top) ? 0 : element.scrollHeight;
635
+ element.scrollTop = position;
636
+ }
637
+ });
638
+ }
639
+ }
640
+ activeOptionTrackerChange(option) {
641
+ this.onOptionTriggered.emit(option);
642
+ }
643
+ // Triggers native file upload for file selection from the user
644
+ triggerNativeFileUpload(window) {
645
+ if (window) {
646
+ if (this.nativeFileInput)
647
+ this.nativeFileInput.nativeElement.click();
648
+ }
649
+ }
650
+ // Toggles a window focus on the focus/blur of a 'newMessage' input
651
+ toggleWindowFocus(window) {
652
+ window.hasFocus = !window.hasFocus;
653
+ if (window.hasFocus) {
654
+ const unreadMessages = window.messages
655
+ .filter(message => message.dateSeen == null
656
+ && (message.toId == this.userId || window.participant.participantType === ChatParticipantType.Group));
657
+ if (unreadMessages && unreadMessages.length > 0) {
658
+ this.onMessagesSeen.emit(unreadMessages);
659
+ }
660
+ }
661
+ }
662
+ markMessagesAsRead(messages) {
663
+ this.onMessagesSeen.emit(messages);
664
+ }
665
+ fetchMessageHistory(window) {
666
+ this.onLoadHistoryTriggered.emit(window);
667
+ }
668
+ // Closes a chat window via the close 'X' button
669
+ onCloseChatWindow() {
670
+ this.onChatWindowClosed.emit({ closedWindow: this.window, closedViaEscapeKey: false });
671
+ }
672
+ /* Monitors pressed keys on a chat window
673
+ - Dispatches a message when the ENTER key is pressed
674
+ - Tabs between windows on TAB or SHIFT + TAB
675
+ - Closes the current focused window on ESC
676
+ */
677
+ onChatInputTyped(event, window) {
678
+ switch (event.keyCode) {
679
+ case 13:
680
+ if (window.newMessage && window.newMessage.trim() != "") {
681
+ let message = new Message();
682
+ message.fromId = this.userId;
683
+ message.toId = window.participant.id;
684
+ message.message = window.newMessage;
685
+ message.dateSent = new Date();
686
+ window.messages.push(message);
687
+ this.onMessageSent.emit(message);
688
+ window.newMessage = ""; // Resets the new message input
689
+ this.scrollChatWindow(window, ScrollDirection.Bottom);
690
+ }
691
+ break;
692
+ case 9:
693
+ event.preventDefault();
694
+ this.onTabTriggered.emit({ triggeringWindow: window, shiftKeyPressed: event.shiftKey });
695
+ break;
696
+ case 27:
697
+ this.onChatWindowClosed.emit({ closedWindow: window, closedViaEscapeKey: true });
698
+ break;
699
+ }
700
+ }
701
+ // Toggles a chat window visibility between maximized/minimized
702
+ onChatWindowClicked(window) {
703
+ window.isCollapsed = !window.isCollapsed;
704
+ this.scrollChatWindow(window, ScrollDirection.Bottom);
705
+ }
706
+ clearInUseFileUploader(fileUploadInstanceId) {
707
+ const uploaderInstanceIdIndex = this.fileUploadersInUse.indexOf(fileUploadInstanceId);
708
+ if (uploaderInstanceIdIndex > -1) {
709
+ this.fileUploadersInUse.splice(uploaderInstanceIdIndex, 1);
710
+ }
711
+ }
712
+ // Handles file selection and uploads the selected file using the file upload adapter
713
+ onFileChosen(window) {
714
+ const fileUploadInstanceId = this.getUniqueFileUploadInstanceId(window);
715
+ const uploadElementRef = this.nativeFileInput;
716
+ if (uploadElementRef) {
717
+ const file = uploadElementRef.nativeElement.files[0];
718
+ this.fileUploadersInUse.push(fileUploadInstanceId);
719
+ this.fileUploadAdapter.uploadFile(file, window.participant.id)
720
+ .subscribe(fileMessage => {
721
+ this.clearInUseFileUploader(fileUploadInstanceId);
722
+ fileMessage.fromId = this.userId;
723
+ // Push file message to current user window
724
+ window.messages.push(fileMessage);
725
+ this.onMessageSent.emit(fileMessage);
726
+ this.scrollChatWindow(window, ScrollDirection.Bottom);
727
+ // Resets the file upload element
728
+ uploadElementRef.nativeElement.value = '';
729
+ }, (error) => {
730
+ this.clearInUseFileUploader(fileUploadInstanceId);
731
+ // Resets the file upload element
732
+ uploadElementRef.nativeElement.value = '';
733
+ // TODO: Invoke a file upload adapter error here
734
+ });
735
+ }
736
+ }
737
+ getParticipantTooltip(participant) {
738
+ const { metadata, displayName } = participant;
739
+ if (!metadata)
740
+ return displayName;
741
+ const { primary, secondary, tertiary } = metadata;
742
+ const parts = [primary, secondary, tertiary].filter(Boolean);
743
+ return parts.length > 0 ? parts.join(' | ') : displayName;
744
+ }
745
+ getMetadataDisplay(participant) {
746
+ if (!participant.metadata || !participant.showMetadataInTitle) {
747
+ return '';
748
+ }
749
+ const parts = [];
750
+ if (participant.showMetadataInTitle.primary && participant.metadata.primary) {
751
+ parts.push(participant.metadata.primary);
752
+ }
753
+ if (participant.showMetadataInTitle.secondary && participant.metadata.secondary) {
754
+ parts.push(participant.metadata.secondary);
755
+ }
756
+ if (participant.showMetadataInTitle.tertiary && participant.metadata.tertiary) {
757
+ parts.push(participant.metadata.tertiary);
758
+ }
759
+ return parts.join(' | ');
760
+ }
761
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChatWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
762
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: NgChatWindowComponent, isStandalone: false, selector: "ng-chat-window", inputs: { fileUploadAdapter: "fileUploadAdapter", window: "window", userId: "userId", localization: "localization", showOptions: "showOptions", emojisEnabled: "emojisEnabled", linkfyEnabled: "linkfyEnabled", showMessageDate: "showMessageDate", messageDatePipeFormat: "messageDatePipeFormat", hasPagedHistory: "hasPagedHistory" }, outputs: { onChatWindowClosed: "onChatWindowClosed", onMessagesSeen: "onMessagesSeen", onMessageSent: "onMessageSent", onTabTriggered: "onTabTriggered", onOptionTriggered: "onOptionTriggered", onLoadHistoryTriggered: "onLoadHistoryTriggered" }, viewQueries: [{ propertyName: "chatMessages", first: true, predicate: ["chatMessages"], descendants: true }, { propertyName: "nativeFileInput", first: true, predicate: ["nativeFileInput"], descendants: true }, { propertyName: "chatWindowInput", first: true, predicate: ["chatWindowInput"], descendants: true }], ngImport: i0, template: "<ng-container *ngIf=\"window && window.isCollapsed\">\n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong [matTooltip]=\"getParticipantTooltip(window.participant)\">\n\t\t\t\t{{window.participant.displayName}} <span *ngIf=\"getMetadataDisplay(window.participant)\">({{ getMetadataDisplay(window.participant) }})</span>\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" [matTooltip]=\"chatParticipantStatusDescriptor(window.participant.status, localization)\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t</div>\n</ng-container>\n<ng-container *ngIf=\"window && !window.isCollapsed\"> \n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong [matTooltip]=\"getParticipantTooltip(window.participant)\">\n\t\t\t\t{{window.participant.displayName}} <span *ngIf=\"getMetadataDisplay(window.participant)\">({{ getMetadataDisplay(window.participant) }})</span>\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" [matTooltip]=\"chatParticipantStatusDescriptor(window.participant.status, localization)\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t\t<!-- <ng-chat-options [ngClass]=\"'ng-chat-options-container'\" [options]=\"defaultWindowOptions(window)\" (activeOptionTrackerChange)=\"activeOptionTrackerChange($event)\"></ng-chat-options> -->\n\t</div>\n\t\t<div #chatMessages class=\"ng-chat-messages primary-background\">\n\t\t\t<div *ngIf=\"window.isLoadingHistory\" class=\"ng-chat-loading-wrapper\">\n\t\t\t\t<div class=\"loader\">Loading history...</div>\n\t\t\t</div>\n\t\t\t<div *ngIf=\"hasPagedHistory && window.hasMoreMessages && !window.isLoadingHistory\" class=\"ng-chat-load-history\">\n\t\t\t\t<a class=\"load-history-action\" (click)=\"fetchMessageHistory(window)\">{{localization.loadMessageHistoryPlaceholder}}</a>\n\t\t\t</div>\n\n\t\t\t<div *ngFor=\"let message of window.messages; let i = index\" [ngClass]=\"{'ng-chat-message': true, 'ng-chat-message-received': message.fromId !== userId}\">\n\t\t\t\t<ng-container *ngIf=\"isAvatarVisible(window, message, i)\">\n\t\t\t\t\t<div *ngIf=\"!getChatWindowAvatar(window.participant, message)\" class=\"icon-wrapper\">\n\t\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- <img *ngIf=\"getChatWindowAvatar(window.participant, message)\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"getChatWindowAvatar(window.participant, message) | sanitize\" /> -->\n\t\t\t\t\t<span *ngIf=\"window.participant.participantType === ChatParticipantType.Group\" class=\"ng-chat-participant-name\">{{window.participant | groupMessageDisplayName:message}}</span>\n\t\t\t\t</ng-container>\n\t\t\t\t<ng-container [ngSwitch]=\"message.type\">\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Text\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\t\t \t\t\t<span [innerHtml]=\"message.message | emojify:emojisEnabled | linkfy:linkfyEnabled\"></span>\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Image\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\n\t\t\t \t\t\t<img src=\"{{message.message}}\" class=\"image-message\" />\n\t\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.File\" [ngClass]=\"{'file-message-container': true, 'received': message.fromId !== userId}\">\n\t\t\t\t\t\t<div class=\"file-message-icon-container\">\n\t\t\t\t\t\t\t<i class=\"paperclip-icon\"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<!-- <a class=\"file-details\" [attr.href]=\"message.downloadUrl\" target=\"_blank\" rel=\"noopener noreferrer\" (click)=\"this.markMessagesAsRead([message])\" download>\n\t\t\t\t\t\t\t<span class=\"file-message-title\" [attr.title]=\"message.message\">{{message.message}}</span>\n\t\t\t\t\t\t\t<span *ngIf=\"message.fileSizeInBytes\" class=\"file-message-size\">{{message.fileSizeInBytes}} Bytes</span>\n\t\t\t\t\t\t</a> -->\n\t\t\t\t\t</div>\n\t\t\t\t</ng-container>\n\t\t\t</div>\n\t</div>\n\n\t<div class=\"ng-chat-footer primary-outline-color primary-background\">\n\t\t<input #chatWindowInput\n\t\t\ttype=\"text\"\n\t\t\t[ngModel]=\"window.newMessage | emojify:emojisEnabled\"\n\t\t\t(ngModelChange)=\"window.newMessage=$event\"\n\t\t\t[placeholder]=\"localization.messagePlaceholder\"\n\t\t\t[ngClass]=\"{'chat-window-input': true, 'has-side-action': fileUploadAdapter}\"\n\t\t\t(keydown)=\"onChatInputTyped($event, window)\"\n\t\t\t(blur)=\"toggleWindowFocus(window)\"\n\t\t\t(focus)=\"toggleWindowFocus(window)\"/>\n\n\t\t<!-- File Upload -->\n\t\t<ng-container *ngIf=\"fileUploadAdapter\">\n\t\t\t<a *ngIf=\"!isUploadingFile(window)\" class=\"btn-add-file\" (click)=\"triggerNativeFileUpload(window)\">\n\t\t\t\t<i class=\"upload-icon\"></i>\n\t\t\t</a>\n\t\t\t<input\n\t\t\t\ttype=\"file\"\n\t\t\t\t#nativeFileInput\n\t\t\t\tstyle=\"display: none;\"\n\t\t\t\t[attr.id]=\"getUniqueFileUploadInstanceId(window)\"\n\t\t\t\t(change)=\"onFileChosen(window)\" />\n\t\t\t<div *ngIf=\"isUploadingFile(window)\" class=\"loader\"></div>\n\t\t</ng-container>\n\t</div>\n</ng-container>", styles: [".ng-chat-window{right:260px;height:360px;z-index:999;bottom:0;width:300px;position:fixed;border-width:1px;border-style:solid;border-bottom:0;box-shadow:0 4px 8px #00000040;margin-bottom:10px!important;border:1px solid grey!important;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;background-color:#fff}.ng-chat-window ::-webkit-scrollbar{width:8px!important}.ng-chat-window ::-webkit-scrollbar-thumb{background:#1b1b70!important;border-radius:5px!important}.ng-chat-window-collapsed{height:30px!important}.ng-chat-window .ng-chat-footer{box-sizing:border-box!important;padding-bottom:10px!important;display:block;height:10%;width:95%!important;margin:auto!important;background-color:inherit!important}.ng-chat-window .ng-chat-footer>input{font-size:.8em;box-sizing:border-box!important;padding:15px!important;height:100%;width:100%;border-radius:20px!important;border:1px solid grey!important}.ng-chat-window .ng-chat-footer>input.has-side-action{width:calc(100% - 30px)}.ng-chat-window .ng-chat-footer .btn-add-file{position:absolute;right:5px;bottom:7px;height:20px;width:20px;cursor:pointer}.ng-chat-window .ng-chat-footer .loader{position:absolute;right:14px;bottom:8px}.ng-chat-window .ng-chat-load-history{height:30px;text-align:center;font-size:.8em}.ng-chat-window .ng-chat-load-history>a{border-radius:15px;cursor:pointer;padding:5px 10px}.ng-chat-window .ng-chat-messages{padding:10px;width:100%;height:calc(90% - 30px);box-sizing:border-box;position:relative;overflow:auto}.ng-chat-window .ng-chat-messages .ng-chat-message{clear:both}.ng-chat-window .ng-chat-messages .ng-chat-message>img.avatar,.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{position:absolute;left:10px;border-radius:25px}.ng-chat-window .ng-chat-messages .ng-chat-message .ng-chat-participant-name{display:inline-block;margin-left:40px;padding-bottom:5px;font-weight:700;font-size:.8em;text-overflow:ellipsis;max-width:180px}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{background-color:#bababa;overflow:hidden;width:30px;height:30px;padding:0}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper>i{color:#fff;transform:scale(.7)}.ng-chat-window .ng-chat-messages .ng-chat-message .message-sent-date{font-size:.5em!important;display:block;text-align:right;margin-top:5px;color:#a1a0a0!important;font-style:italic!important}.ng-chat-window .ng-chat-messages .ng-chat-message>div{float:right;width:182px;border-radius:15px 15px 0!important;padding:10px;margin-top:0;margin-bottom:5px;font-size:.9em;word-wrap:break-word;background-color:#93d4f0!important}.ng-chat-window .ng-chat-message-received>div.received-chat-message-container{float:left;margin-left:40px;padding-top:7px;padding-bottom:7px;margin-top:0;margin-bottom:5px;background-color:#daf2fc!important;border-radius:15px 15px 15px 0!important}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container{float:right;width:202px;border-style:solid;border-width:3px;border-radius:5px;overflow:hidden;margin-bottom:5px;display:block;text-decoration:none;font-size:.9em;padding:0;box-sizing:border-box}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container.received{float:left;margin-left:40px;width:208px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container{width:20px;height:35px;padding:10px 5px;float:left}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container i{margin-top:8px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details{float:left;padding:10px;width:calc(100% - 60px);color:currentColor;text-decoration:none}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details:hover{text-decoration:underline}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details span{display:block;width:100%;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-title{font-weight:700}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-size{font-size:.8em;margin-top:5px}.ng-chat-window .image-message{width:100%;height:auto}@media only screen and (max-width: 581px){.ng-chat-window{position:initial}}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "pipe", type: i3.DatePipe, name: "date" }, { kind: "pipe", type: EmojifyPipe, name: "emojify" }, { kind: "pipe", type: LinkfyPipe, name: "linkfy" }, { kind: "pipe", type: GroupMessageDisplayNamePipe, name: "groupMessageDisplayName" }], encapsulation: i0.ViewEncapsulation.None }); }
763
+ }
764
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChatWindowComponent, decorators: [{
765
+ type: Component,
766
+ args: [{ selector: 'ng-chat-window', encapsulation: ViewEncapsulation.None, standalone: false, template: "<ng-container *ngIf=\"window && window.isCollapsed\">\n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong [matTooltip]=\"getParticipantTooltip(window.participant)\">\n\t\t\t\t{{window.participant.displayName}} <span *ngIf=\"getMetadataDisplay(window.participant)\">({{ getMetadataDisplay(window.participant) }})</span>\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" [matTooltip]=\"chatParticipantStatusDescriptor(window.participant.status, localization)\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t</div>\n</ng-container>\n<ng-container *ngIf=\"window && !window.isCollapsed\"> \n\t<div class=\"ng-chat-title secondary-background\">\n\t\t<div class=\"ng-chat-title-visibility-toggle-area\" (click)=\"onChatWindowClicked(window)\">\n\t\t\t<strong [matTooltip]=\"getParticipantTooltip(window.participant)\">\n\t\t\t\t{{window.participant.displayName}} <span *ngIf=\"getMetadataDisplay(window.participant)\">({{ getMetadataDisplay(window.participant) }})</span>\n\t\t\t</strong>\n\t\t\t<span [ngClass]=\"{'ng-chat-participant-status': true, 'online': window.participant.status === ChatParticipantStatus.Online, 'busy': window.participant.status === ChatParticipantStatus.Busy, 'away': window.participant.status === ChatParticipantStatus.Away, 'offline': window.participant.status === ChatParticipantStatus.Offline}\" [matTooltip]=\"chatParticipantStatusDescriptor(window.participant.status, localization)\"></span>\n\t\t\t<span *ngIf=\"unreadMessagesTotal(window).length > 0\" class=\"ng-chat-unread-messages-count unread-messages-counter-container primary-text\">{{unreadMessagesTotal(window)}}</span>\n\t\t</div>\n\t\t<a href=\"javascript:void(0);\" class=\"ng-chat-close primary-text\" (click)=\"onCloseChatWindow()\">&#x2715;</a>\n\t\t<!-- <ng-chat-options [ngClass]=\"'ng-chat-options-container'\" [options]=\"defaultWindowOptions(window)\" (activeOptionTrackerChange)=\"activeOptionTrackerChange($event)\"></ng-chat-options> -->\n\t</div>\n\t\t<div #chatMessages class=\"ng-chat-messages primary-background\">\n\t\t\t<div *ngIf=\"window.isLoadingHistory\" class=\"ng-chat-loading-wrapper\">\n\t\t\t\t<div class=\"loader\">Loading history...</div>\n\t\t\t</div>\n\t\t\t<div *ngIf=\"hasPagedHistory && window.hasMoreMessages && !window.isLoadingHistory\" class=\"ng-chat-load-history\">\n\t\t\t\t<a class=\"load-history-action\" (click)=\"fetchMessageHistory(window)\">{{localization.loadMessageHistoryPlaceholder}}</a>\n\t\t\t</div>\n\n\t\t\t<div *ngFor=\"let message of window.messages; let i = index\" [ngClass]=\"{'ng-chat-message': true, 'ng-chat-message-received': message.fromId !== userId}\">\n\t\t\t\t<ng-container *ngIf=\"isAvatarVisible(window, message, i)\">\n\t\t\t\t\t<div *ngIf=\"!getChatWindowAvatar(window.participant, message)\" class=\"icon-wrapper\">\n\t\t\t\t\t\t<i class=\"user-icon\"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- <img *ngIf=\"getChatWindowAvatar(window.participant, message)\" alt=\"\" class=\"avatar\" height=\"30\" width=\"30\" [src]=\"getChatWindowAvatar(window.participant, message) | sanitize\" /> -->\n\t\t\t\t\t<span *ngIf=\"window.participant.participantType === ChatParticipantType.Group\" class=\"ng-chat-participant-name\">{{window.participant | groupMessageDisplayName:message}}</span>\n\t\t\t\t</ng-container>\n\t\t\t\t<ng-container [ngSwitch]=\"message.type\">\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Text\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\t\t \t\t\t<span [innerHtml]=\"message.message | emojify:emojisEnabled | linkfy:linkfyEnabled\"></span>\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.Image\" [ngClass]=\"{'sent-chat-message-container': message.fromId === userId, 'received-chat-message-container': message.fromId !== userId}\">\n\t\n\t\t\t \t\t\t<img src=\"{{message.message}}\" class=\"image-message\" />\n\t\n\t\t\t\t\t\t<span *ngIf=\"showMessageDate && message.dateSent\" class=\"message-sent-date\">{{message.dateSent | date:'dd'}}/{{message.dateSent | date:'MM'}}/{{message.dateSent | date:'y'}} {{message.dateSent | date:'h:mm a '}}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div *ngSwitchCase=\"MessageType.File\" [ngClass]=\"{'file-message-container': true, 'received': message.fromId !== userId}\">\n\t\t\t\t\t\t<div class=\"file-message-icon-container\">\n\t\t\t\t\t\t\t<i class=\"paperclip-icon\"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<!-- <a class=\"file-details\" [attr.href]=\"message.downloadUrl\" target=\"_blank\" rel=\"noopener noreferrer\" (click)=\"this.markMessagesAsRead([message])\" download>\n\t\t\t\t\t\t\t<span class=\"file-message-title\" [attr.title]=\"message.message\">{{message.message}}</span>\n\t\t\t\t\t\t\t<span *ngIf=\"message.fileSizeInBytes\" class=\"file-message-size\">{{message.fileSizeInBytes}} Bytes</span>\n\t\t\t\t\t\t</a> -->\n\t\t\t\t\t</div>\n\t\t\t\t</ng-container>\n\t\t\t</div>\n\t</div>\n\n\t<div class=\"ng-chat-footer primary-outline-color primary-background\">\n\t\t<input #chatWindowInput\n\t\t\ttype=\"text\"\n\t\t\t[ngModel]=\"window.newMessage | emojify:emojisEnabled\"\n\t\t\t(ngModelChange)=\"window.newMessage=$event\"\n\t\t\t[placeholder]=\"localization.messagePlaceholder\"\n\t\t\t[ngClass]=\"{'chat-window-input': true, 'has-side-action': fileUploadAdapter}\"\n\t\t\t(keydown)=\"onChatInputTyped($event, window)\"\n\t\t\t(blur)=\"toggleWindowFocus(window)\"\n\t\t\t(focus)=\"toggleWindowFocus(window)\"/>\n\n\t\t<!-- File Upload -->\n\t\t<ng-container *ngIf=\"fileUploadAdapter\">\n\t\t\t<a *ngIf=\"!isUploadingFile(window)\" class=\"btn-add-file\" (click)=\"triggerNativeFileUpload(window)\">\n\t\t\t\t<i class=\"upload-icon\"></i>\n\t\t\t</a>\n\t\t\t<input\n\t\t\t\ttype=\"file\"\n\t\t\t\t#nativeFileInput\n\t\t\t\tstyle=\"display: none;\"\n\t\t\t\t[attr.id]=\"getUniqueFileUploadInstanceId(window)\"\n\t\t\t\t(change)=\"onFileChosen(window)\" />\n\t\t\t<div *ngIf=\"isUploadingFile(window)\" class=\"loader\"></div>\n\t\t</ng-container>\n\t</div>\n</ng-container>", styles: [".ng-chat-window{right:260px;height:360px;z-index:999;bottom:0;width:300px;position:fixed;border-width:1px;border-style:solid;border-bottom:0;box-shadow:0 4px 8px #00000040;margin-bottom:10px!important;border:1px solid grey!important;border-radius:10px!important;box-sizing:border-box!important;overflow:hidden!important;background-color:#fff}.ng-chat-window ::-webkit-scrollbar{width:8px!important}.ng-chat-window ::-webkit-scrollbar-thumb{background:#1b1b70!important;border-radius:5px!important}.ng-chat-window-collapsed{height:30px!important}.ng-chat-window .ng-chat-footer{box-sizing:border-box!important;padding-bottom:10px!important;display:block;height:10%;width:95%!important;margin:auto!important;background-color:inherit!important}.ng-chat-window .ng-chat-footer>input{font-size:.8em;box-sizing:border-box!important;padding:15px!important;height:100%;width:100%;border-radius:20px!important;border:1px solid grey!important}.ng-chat-window .ng-chat-footer>input.has-side-action{width:calc(100% - 30px)}.ng-chat-window .ng-chat-footer .btn-add-file{position:absolute;right:5px;bottom:7px;height:20px;width:20px;cursor:pointer}.ng-chat-window .ng-chat-footer .loader{position:absolute;right:14px;bottom:8px}.ng-chat-window .ng-chat-load-history{height:30px;text-align:center;font-size:.8em}.ng-chat-window .ng-chat-load-history>a{border-radius:15px;cursor:pointer;padding:5px 10px}.ng-chat-window .ng-chat-messages{padding:10px;width:100%;height:calc(90% - 30px);box-sizing:border-box;position:relative;overflow:auto}.ng-chat-window .ng-chat-messages .ng-chat-message{clear:both}.ng-chat-window .ng-chat-messages .ng-chat-message>img.avatar,.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{position:absolute;left:10px;border-radius:25px}.ng-chat-window .ng-chat-messages .ng-chat-message .ng-chat-participant-name{display:inline-block;margin-left:40px;padding-bottom:5px;font-weight:700;font-size:.8em;text-overflow:ellipsis;max-width:180px}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper{background-color:#bababa;overflow:hidden;width:30px;height:30px;padding:0}.ng-chat-window .ng-chat-messages .ng-chat-message>.icon-wrapper>i{color:#fff;transform:scale(.7)}.ng-chat-window .ng-chat-messages .ng-chat-message .message-sent-date{font-size:.5em!important;display:block;text-align:right;margin-top:5px;color:#a1a0a0!important;font-style:italic!important}.ng-chat-window .ng-chat-messages .ng-chat-message>div{float:right;width:182px;border-radius:15px 15px 0!important;padding:10px;margin-top:0;margin-bottom:5px;font-size:.9em;word-wrap:break-word;background-color:#93d4f0!important}.ng-chat-window .ng-chat-message-received>div.received-chat-message-container{float:left;margin-left:40px;padding-top:7px;padding-bottom:7px;margin-top:0;margin-bottom:5px;background-color:#daf2fc!important;border-radius:15px 15px 15px 0!important}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container{float:right;width:202px;border-style:solid;border-width:3px;border-radius:5px;overflow:hidden;margin-bottom:5px;display:block;text-decoration:none;font-size:.9em;padding:0;box-sizing:border-box}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container.received{float:left;margin-left:40px;width:208px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container{width:20px;height:35px;padding:10px 5px;float:left}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-message-icon-container i{margin-top:8px}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details{float:left;padding:10px;width:calc(100% - 60px);color:currentColor;text-decoration:none}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details:hover{text-decoration:underline}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details span{display:block;width:100%;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-title{font-weight:700}.ng-chat-window .ng-chat-messages .ng-chat-message .file-message-container>.file-details .file-message-size{font-size:.8em;margin-top:5px}.ng-chat-window .image-message{width:100%;height:auto}@media only screen and (max-width: 581px){.ng-chat-window{position:initial}}\n"] }]
767
+ }], ctorParameters: () => [], propDecorators: { fileUploadAdapter: [{
768
+ type: Input
769
+ }], window: [{
770
+ type: Input
771
+ }], userId: [{
772
+ type: Input
773
+ }], localization: [{
774
+ type: Input
775
+ }], showOptions: [{
776
+ type: Input
777
+ }], emojisEnabled: [{
778
+ type: Input
779
+ }], linkfyEnabled: [{
780
+ type: Input
781
+ }], showMessageDate: [{
782
+ type: Input
783
+ }], messageDatePipeFormat: [{
784
+ type: Input
785
+ }], hasPagedHistory: [{
786
+ type: Input
787
+ }], onChatWindowClosed: [{
788
+ type: Output
789
+ }], onMessagesSeen: [{
790
+ type: Output
791
+ }], onMessageSent: [{
792
+ type: Output
793
+ }], onTabTriggered: [{
794
+ type: Output
795
+ }], onOptionTriggered: [{
796
+ type: Output
797
+ }], onLoadHistoryTriggered: [{
798
+ type: Output
799
+ }], chatMessages: [{
800
+ type: ViewChild,
801
+ args: ['chatMessages']
802
+ }], nativeFileInput: [{
803
+ type: ViewChild,
804
+ args: ['nativeFileInput']
805
+ }], chatWindowInput: [{
806
+ type: ViewChild,
807
+ args: ['chatWindowInput']
808
+ }] } });
809
+
810
+ /* eslint-disable */
811
+ class NgChat {
812
+ constructor(_httpClient) {
813
+ this._httpClient = _httpClient;
814
+ // Exposes enums for the ng-template
815
+ this.ChatParticipantType = ChatParticipantType;
816
+ this.ChatParticipantStatus = ChatParticipantStatus;
817
+ this.MessageType = MessageType;
818
+ this._isDisabled = false;
819
+ this.isCollapsed = false;
820
+ this.maximizeWindowOnNewMessage = true;
821
+ this.pollFriendsList = false;
822
+ this.pollingInterval = 5000;
823
+ this.historyEnabled = true;
824
+ this.emojisEnabled = true;
825
+ this.linkfyEnabled = true;
826
+ this.audioEnabled = true;
827
+ this.searchEnabled = true;
828
+ this.audioSource = '../../../assets/notification.wav';
829
+ this.persistWindowsState = true;
830
+ this.title = "Quick chat";
831
+ this.messagePlaceholder = "Type a message";
832
+ this.searchPlaceholder = "Search";
833
+ this.browserNotificationsEnabled = true;
834
+ this.browserNotificationIconSource = '../../../assets/notification.png';
835
+ this.browserNotificationTitle = "New message from";
836
+ this.historyPageSize = 10;
837
+ this.hideFriendsList = false;
838
+ this.hideFriendsListOnUnsupportedViewport = true;
839
+ this.theme = Theme.Light;
840
+ this.messageDatePipeFormat = "short";
841
+ this.showMessageDate = true;
842
+ this.isViewportOnMobileEnabled = false;
843
+ this.onParticipantClicked = new EventEmitter();
844
+ this.onParticipantChatOpened = new EventEmitter();
845
+ this.onParticipantChatClosed = new EventEmitter();
846
+ this.onMessagesSeen = new EventEmitter();
847
+ this.onCloseWindowClicked = new EventEmitter();
848
+ this.searchInputChanged = new EventEmitter();
849
+ this.browserNotificationsBootstrapped = false;
850
+ this.hasPagedHistory = false;
851
+ // Don't want to add this as a setting to simplify usage. Previous placeholder and title settings available to be used, or use full Localization object.
852
+ this.statusDescription = {
853
+ online: 'Online',
854
+ busy: 'Busy',
855
+ away: 'Away',
856
+ offline: 'Offline'
857
+ };
858
+ this.participantsInteractedWith = [];
859
+ // Defines the size of each opened window to calculate how many windows can be opened on the viewport at the same time.
860
+ this.windowSizeFactor = 320;
861
+ // Total width size of the friends list section
862
+ this.friendsListWidth = 262;
863
+ // Set to true if there is no space to display at least one chat window and 'hideFriendsListOnUnsupportedViewport' is true
864
+ this.unsupportedViewport = false;
865
+ this.windows = [];
866
+ this.isBootstrapped = false;
867
+ }
868
+ get isDisabled() {
869
+ return this._isDisabled;
870
+ }
871
+ set isDisabled(value) {
872
+ this._isDisabled = value;
873
+ if (value) {
874
+ window.clearInterval(this.pollingIntervalWindowInstance);
875
+ }
876
+ else {
877
+ this.activateFriendListFetch();
878
+ }
879
+ }
880
+ get localStorageKey() {
881
+ return `ng-chat-users-${this.userId}`; // Appending the user id so the state is unique per user in a computer.
882
+ }
883
+ ;
884
+ ngOnInit() {
885
+ this.bootstrapChat();
886
+ }
887
+ onResize(event) {
888
+ this.viewPortTotalArea = event.target.innerWidth;
889
+ this.NormalizeWindows();
890
+ }
891
+ // Checks if there are more opened windows than the view port can display
892
+ NormalizeWindows() {
893
+ const maxSupportedOpenedWindows = Math.floor((this.viewPortTotalArea - (!this.hideFriendsList ? this.friendsListWidth : 0)) / this.windowSizeFactor);
894
+ const difference = this.windows.length - maxSupportedOpenedWindows;
895
+ if (difference >= 0) {
896
+ this.windows.splice(this.windows.length - difference);
897
+ }
898
+ this.updateWindowsState(this.windows);
899
+ // Viewport should have space for at least one chat window but should show in mobile if option is enabled.
900
+ this.unsupportedViewport = this.isViewportOnMobileEnabled ? false : this.hideFriendsListOnUnsupportedViewport && maxSupportedOpenedWindows < 1;
901
+ }
902
+ // Initializes the chat plugin and the messaging adapter
903
+ bootstrapChat() {
904
+ let initializationException = null;
905
+ if (this.adapter != null && this.userId != null) {
906
+ try {
907
+ this.viewPortTotalArea = window.innerWidth;
908
+ this.initializeTheme();
909
+ this.initializeDefaultText();
910
+ this.initializeBrowserNotifications();
911
+ // Binding event listeners
912
+ this.adapter.messageReceivedHandler = (participant, msg) => this.onMessageReceived(participant, msg);
913
+ this.adapter.friendsListChangedHandler = (participantsResponse) => this.onFriendsListChanged(participantsResponse);
914
+ this.activateFriendListFetch();
915
+ this.bufferAudioFile();
916
+ this.hasPagedHistory = this.adapter instanceof PagedHistoryChatAdapter;
917
+ if (this.fileUploadUrl && this.fileUploadUrl !== "") {
918
+ this.fileUploadAdapter = new DefaultFileUploadAdapter(this.fileUploadUrl, this._httpClient);
919
+ }
920
+ this.NormalizeWindows();
921
+ this.isBootstrapped = true;
922
+ }
923
+ catch (ex) {
924
+ initializationException = ex;
925
+ }
926
+ }
927
+ if (!this.isBootstrapped) {
928
+ console.error("ng-chat component couldn't be bootstrapped.");
929
+ if (this.userId == null) {
930
+ console.error("ng-chat can't be initialized without an user id. Please make sure you've provided an userId as a parameter of the ng-chat component.");
931
+ }
932
+ if (this.adapter == null) {
933
+ console.error("ng-chat can't be bootstrapped without a ChatAdapter. Please make sure you've provided a ChatAdapter implementation as a parameter of the ng-chat component.");
934
+ }
935
+ if (initializationException) {
936
+ console.error(`An exception has occurred while initializing ng-chat. Details: ${initializationException.message}`);
937
+ console.error(initializationException);
938
+ }
939
+ }
940
+ }
941
+ activateFriendListFetch() {
942
+ if (this.adapter) {
943
+ // Loading current users list
944
+ if (this.pollFriendsList) {
945
+ // Setting a long poll interval to update the friends list
946
+ this.fetchFriendsList(true);
947
+ this.pollingIntervalWindowInstance = window.setInterval(() => this.fetchFriendsList(false), this.pollingInterval);
948
+ }
949
+ else {
950
+ // Since polling was disabled, a friends list update mechanism will have to be implemented in the ChatAdapter.
951
+ this.fetchFriendsList(true);
952
+ }
953
+ }
954
+ }
955
+ // Initializes browser notifications
956
+ async initializeBrowserNotifications() {
957
+ if (this.browserNotificationsEnabled && ("Notification" in window)) {
958
+ if (await Notification.requestPermission() === "granted") {
959
+ this.browserNotificationsBootstrapped = true;
960
+ }
961
+ }
962
+ }
963
+ // Initializes default text
964
+ initializeDefaultText() {
965
+ if (!this.localization) {
966
+ this.localization = {
967
+ messagePlaceholder: this.messagePlaceholder,
968
+ searchPlaceholder: this.searchPlaceholder,
969
+ title: this.title,
970
+ statusDescription: this.statusDescription,
971
+ browserNotificationTitle: this.browserNotificationTitle,
972
+ loadMessageHistoryPlaceholder: "Load older messages"
973
+ };
974
+ }
975
+ }
976
+ initializeTheme() {
977
+ if (this.customTheme) {
978
+ this.theme = Theme.Custom;
979
+ }
980
+ else if (this.theme != Theme.Light && this.theme != Theme.Dark) {
981
+ // TODO: Use es2017 in future with Object.values(Theme).includes(this.theme) to do this check
982
+ throw new Error(`Invalid theme configuration for ng-chat. "${this.theme}" is not a valid theme value.`);
983
+ }
984
+ }
985
+ // Sends a request to load the friends list
986
+ fetchFriendsList(isBootstrapping) {
987
+ this.adapter.listFriends()
988
+ .pipe(map((participantsResponse) => {
989
+ this.participantsResponse = participantsResponse;
990
+ this.participants = participantsResponse.map((response) => {
991
+ return response.participant;
992
+ });
993
+ })).subscribe(() => {
994
+ if (isBootstrapping) {
995
+ this.restoreWindowsState();
996
+ }
997
+ });
998
+ }
999
+ fetchMessageHistory(window) {
1000
+ // Not ideal but will keep this until we decide if we are shipping pagination with the default adapter
1001
+ if (this.adapter instanceof PagedHistoryChatAdapter) {
1002
+ window.isLoadingHistory = true;
1003
+ this.adapter.getMessageHistoryByPage(window.participant.id, this.historyPageSize, ++window.historyPage)
1004
+ .pipe(map((result) => {
1005
+ result.forEach((message) => this.assertMessageType(message));
1006
+ window.messages = result.concat(window.messages);
1007
+ window.isLoadingHistory = false;
1008
+ const direction = (window.historyPage == 1) ? ScrollDirection.Bottom : ScrollDirection.Top;
1009
+ window.hasMoreMessages = result.length == this.historyPageSize;
1010
+ setTimeout(() => this.onFetchMessageHistoryLoaded(result, window, direction, true));
1011
+ })).subscribe();
1012
+ }
1013
+ else {
1014
+ this.adapter.getMessageHistory(window.participant.id)
1015
+ .pipe(map((result) => {
1016
+ result.forEach((message) => this.assertMessageType(message));
1017
+ window.messages = result.concat(window.messages);
1018
+ window.isLoadingHistory = false;
1019
+ setTimeout(() => this.onFetchMessageHistoryLoaded(result, window, ScrollDirection.Bottom));
1020
+ })).subscribe();
1021
+ }
1022
+ }
1023
+ onFetchMessageHistoryLoaded(messages, window, direction, forceMarkMessagesAsSeen = false) {
1024
+ this.scrollChatWindow(window, direction);
1025
+ if (window.hasFocus || forceMarkMessagesAsSeen) {
1026
+ const unseenMessages = messages.filter(m => !m.dateSeen);
1027
+ this.markMessagesAsRead(unseenMessages);
1028
+ }
1029
+ }
1030
+ // Updates the friends list via the event handler
1031
+ onFriendsListChanged(participantsResponse) {
1032
+ if (participantsResponse) {
1033
+ this.participantsResponse = participantsResponse;
1034
+ this.participants = participantsResponse.map((response) => {
1035
+ return response.participant;
1036
+ });
1037
+ this.participantsInteractedWith = [];
1038
+ }
1039
+ }
1040
+ // Handles received messages by the adapter
1041
+ onMessageReceived(participant, message) {
1042
+ if (participant && message) {
1043
+ const chatWindow = this.openChatWindow(participant);
1044
+ this.assertMessageType(message);
1045
+ if (!chatWindow[1] || !this.historyEnabled) {
1046
+ chatWindow[0].messages.push(message);
1047
+ this.scrollChatWindow(chatWindow[0], ScrollDirection.Bottom);
1048
+ if (chatWindow[0].hasFocus) {
1049
+ this.markMessagesAsRead([message]);
1050
+ }
1051
+ }
1052
+ this.emitMessageSound(chatWindow[0]);
1053
+ // Do not push browser notifications with message content for privacy purposes if the 'maximizeWindowOnNewMessage' setting is off and this is a new chat window.
1054
+ if (this.maximizeWindowOnNewMessage || (!chatWindow[1] && !chatWindow[0].isCollapsed)) {
1055
+ // Some messages are not pushed because they are loaded by fetching the history hence why we supply the message here
1056
+ this.emitBrowserNotification(chatWindow[0], message);
1057
+ }
1058
+ }
1059
+ }
1060
+ onParticipantClickedFromFriendsList(participant) {
1061
+ this.openChatWindow(participant, true, true);
1062
+ }
1063
+ cancelOptionPrompt() {
1064
+ if (this.currentActiveOption) {
1065
+ this.currentActiveOption.isActive = false;
1066
+ this.currentActiveOption = null;
1067
+ }
1068
+ }
1069
+ onOptionPromptCanceled() {
1070
+ this.cancelOptionPrompt();
1071
+ }
1072
+ onOptionPromptConfirmed(event) {
1073
+ // For now this is fine as there is only one option available. Introduce option types and type checking if a new option is added.
1074
+ this.confirmNewGroup(event);
1075
+ // Canceling current state
1076
+ this.cancelOptionPrompt();
1077
+ }
1078
+ confirmNewGroup(users) {
1079
+ const newGroup = new Group(users);
1080
+ this.openChatWindow(newGroup);
1081
+ if (this.groupAdapter) {
1082
+ this.groupAdapter.groupCreated(newGroup);
1083
+ }
1084
+ }
1085
+ // Opens a new chat whindow. Takes care of available viewport
1086
+ // Works for opening a chat window for an user or for a group
1087
+ // Returns => [Window: Window object reference, boolean: Indicates if this window is a new chat window]
1088
+ openChatWindow(participant, focusOnNewWindow = false, invokedByUserClick = false) {
1089
+ // Is this window opened?
1090
+ const openedWindow = this.windows.find(x => x.participant.id == participant.id);
1091
+ if (!openedWindow) {
1092
+ if (invokedByUserClick) {
1093
+ this.onParticipantClicked.emit(participant);
1094
+ }
1095
+ // Refer to issue #58 on Github
1096
+ const collapseWindow = invokedByUserClick ? false : !this.maximizeWindowOnNewMessage;
1097
+ const newChatWindow = new Window(participant, this.historyEnabled, collapseWindow);
1098
+ // Loads the chat history via an RxJs Observable
1099
+ if (this.historyEnabled) {
1100
+ this.fetchMessageHistory(newChatWindow);
1101
+ }
1102
+ this.windows.unshift(newChatWindow);
1103
+ // Is there enough space left in the view port ? but should be displayed in mobile if option is enabled
1104
+ if (!this.isViewportOnMobileEnabled) {
1105
+ if (this.windows.length * this.windowSizeFactor >= this.viewPortTotalArea - (!this.hideFriendsList ? this.friendsListWidth : 0)) {
1106
+ this.windows.pop();
1107
+ }
1108
+ }
1109
+ this.updateWindowsState(this.windows);
1110
+ if (focusOnNewWindow && !collapseWindow) {
1111
+ this.focusOnWindow(newChatWindow);
1112
+ }
1113
+ this.participantsInteractedWith.push(participant);
1114
+ this.onParticipantChatOpened.emit(participant);
1115
+ return [newChatWindow, true];
1116
+ }
1117
+ else {
1118
+ // Returns the existing chat window
1119
+ return [openedWindow, false];
1120
+ }
1121
+ }
1122
+ // Focus on the input element of the supplied window
1123
+ focusOnWindow(window, callback = () => { }) {
1124
+ const windowIndex = this.windows.indexOf(window);
1125
+ if (windowIndex >= 0) {
1126
+ setTimeout(() => {
1127
+ if (this.chatWindows) {
1128
+ const chatWindowToFocus = this.chatWindows.toArray()[windowIndex];
1129
+ chatWindowToFocus.chatWindowInput.nativeElement.focus();
1130
+ }
1131
+ callback();
1132
+ });
1133
+ }
1134
+ }
1135
+ assertMessageType(message) {
1136
+ // Always fallback to "Text" messages to avoid rendenring issues
1137
+ if (!message.type) {
1138
+ message.type = MessageType.Text;
1139
+ }
1140
+ }
1141
+ // Marks all messages provided as read with the current time.
1142
+ markMessagesAsRead(messages) {
1143
+ const currentDate = new Date();
1144
+ messages.forEach((msg) => {
1145
+ msg.dateSeen = currentDate;
1146
+ });
1147
+ this.onMessagesSeen.emit(messages);
1148
+ }
1149
+ // Buffers audio file (For component's bootstrapping)
1150
+ bufferAudioFile() {
1151
+ if (this.audioSource && this.audioSource.length > 0) {
1152
+ this.audioFile = new Audio();
1153
+ this.audioFile.src = this.audioSource;
1154
+ this.audioFile.load();
1155
+ }
1156
+ }
1157
+ // Emits a message notification audio if enabled after every message received
1158
+ emitMessageSound(window) {
1159
+ if (this.audioEnabled && !window.hasFocus && this.audioFile) {
1160
+ this.audioFile.play();
1161
+ }
1162
+ }
1163
+ // Emits a browser notification
1164
+ emitBrowserNotification(window, message) {
1165
+ // if (this.browserNotificationsBootstrapped && !window.hasFocus && message) {
1166
+ if (this.browserNotificationsBootstrapped && message) {
1167
+ const notification = new Notification(`${this.localization.browserNotificationTitle} ${window.participant.displayName}`, {
1168
+ 'body': message.message,
1169
+ 'icon': this.browserNotificationIconSource
1170
+ });
1171
+ setTimeout(() => {
1172
+ notification.close();
1173
+ }, message.message.length <= 50 ? 5000 : 7000); // More time to read longer messages
1174
+ }
1175
+ }
1176
+ // Saves current windows state into local storage if persistence is enabled
1177
+ updateWindowsState(windows) {
1178
+ if (this.persistWindowsState) {
1179
+ const participantIds = windows.map((w) => {
1180
+ return w.participant.id;
1181
+ });
1182
+ localStorage.setItem(this.localStorageKey, JSON.stringify(participantIds));
1183
+ }
1184
+ }
1185
+ restoreWindowsState() {
1186
+ try {
1187
+ if (this.persistWindowsState) {
1188
+ const stringfiedParticipantIds = localStorage.getItem(this.localStorageKey);
1189
+ if (stringfiedParticipantIds && stringfiedParticipantIds.length > 0) {
1190
+ const participantIds = JSON.parse(stringfiedParticipantIds);
1191
+ const participantsToRestore = this.participants.filter(u => participantIds.indexOf(u.id) >= 0);
1192
+ participantsToRestore.forEach((participant) => {
1193
+ this.openChatWindow(participant);
1194
+ });
1195
+ }
1196
+ }
1197
+ }
1198
+ catch (ex) {
1199
+ console.error(`An error occurred while restoring ng-chat windows state. Details: ${ex}`);
1200
+ }
1201
+ }
1202
+ // Gets closest open window if any. Most recent opened has priority (Right)
1203
+ getClosestWindow(window) {
1204
+ const index = this.windows.indexOf(window);
1205
+ if (index > 0) {
1206
+ return this.windows[index - 1];
1207
+ }
1208
+ else if (index == 0 && this.windows.length > 1) {
1209
+ return this.windows[index + 1];
1210
+ }
1211
+ }
1212
+ closeWindow(window) {
1213
+ const index = this.windows.indexOf(window);
1214
+ this.windows.splice(index, 1);
1215
+ this.updateWindowsState(this.windows);
1216
+ this.onParticipantChatClosed.emit(window.participant);
1217
+ }
1218
+ getChatWindowComponentInstance(targetWindow) {
1219
+ const windowIndex = this.windows.indexOf(targetWindow);
1220
+ if (this.chatWindows) {
1221
+ let targetWindow = this.chatWindows.toArray()[windowIndex];
1222
+ return targetWindow;
1223
+ }
1224
+ return null;
1225
+ }
1226
+ // Scrolls a chat window message flow to the bottom
1227
+ scrollChatWindow(window, direction) {
1228
+ const chatWindow = this.getChatWindowComponentInstance(window);
1229
+ if (chatWindow) {
1230
+ chatWindow.scrollChatWindow(window, direction);
1231
+ }
1232
+ }
1233
+ onWindowMessagesSeen(messagesSeen) {
1234
+ this.markMessagesAsRead(messagesSeen);
1235
+ }
1236
+ onWindowChatClosed(payload) {
1237
+ const { closedWindow, closedViaEscapeKey } = payload;
1238
+ if (closedViaEscapeKey) {
1239
+ let closestWindow = this.getClosestWindow(closedWindow);
1240
+ if (closestWindow) {
1241
+ this.focusOnWindow(closestWindow, () => { this.closeWindow(closedWindow); });
1242
+ }
1243
+ else {
1244
+ this.closeWindow(closedWindow);
1245
+ }
1246
+ }
1247
+ else {
1248
+ this.closeWindow(closedWindow);
1249
+ }
1250
+ }
1251
+ onWindowTabTriggered(payload) {
1252
+ const { triggeringWindow, shiftKeyPressed } = payload;
1253
+ const currentWindowIndex = this.windows.indexOf(triggeringWindow);
1254
+ let windowToFocus = this.windows[currentWindowIndex + (shiftKeyPressed ? 1 : -1)]; // Goes back on shift + tab
1255
+ if (!windowToFocus) {
1256
+ // Edge windows, go to start or end
1257
+ windowToFocus = this.windows[currentWindowIndex > 0 ? 0 : this.chatWindows.length - 1];
1258
+ }
1259
+ this.focusOnWindow(windowToFocus);
1260
+ }
1261
+ onWindowMessageSent(messageSent) {
1262
+ this.adapter.sendMessage(messageSent);
1263
+ }
1264
+ onWindowOptionTriggered(option) {
1265
+ this.currentActiveOption = option;
1266
+ }
1267
+ triggerOpenChatWindow(user) {
1268
+ if (user) {
1269
+ this.openChatWindow(user);
1270
+ }
1271
+ }
1272
+ triggerCloseChatWindow(userId) {
1273
+ const openedWindow = this.windows.find(x => x.participant.id == userId);
1274
+ if (openedWindow) {
1275
+ this.closeWindow(openedWindow);
1276
+ }
1277
+ }
1278
+ triggerToggleChatWindowVisibility(userId) {
1279
+ const openedWindow = this.windows.find(x => x.participant.id == userId);
1280
+ if (openedWindow) {
1281
+ const chatWindow = this.getChatWindowComponentInstance(openedWindow);
1282
+ if (chatWindow) {
1283
+ chatWindow.onChatWindowClicked(openedWindow);
1284
+ }
1285
+ }
1286
+ }
1287
+ // closeFriendList(){
1288
+ // this.hideFriendsList = !this.hideFriendsList
1289
+ // this.hideFriendsListChange.emit(this.hideFriendsList)
1290
+ // }
1291
+ onCloseWindowClick(click) {
1292
+ this.onCloseWindowClicked.emit(click);
1293
+ }
1294
+ onSearchInputChanged(value) {
1295
+ this.searchInputChanged.emit(value);
1296
+ }
1297
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChat, deps: [{ token: i1$2.HttpClient }], target: i0.ɵɵFactoryTarget.Component }); }
1298
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: NgChat, isStandalone: false, selector: "mnl-chat", inputs: { isDisabled: "isDisabled", adapter: "adapter", groupAdapter: "groupAdapter", userId: "userId", isCollapsed: "isCollapsed", maximizeWindowOnNewMessage: "maximizeWindowOnNewMessage", pollFriendsList: "pollFriendsList", pollingInterval: "pollingInterval", historyEnabled: "historyEnabled", emojisEnabled: "emojisEnabled", linkfyEnabled: "linkfyEnabled", audioEnabled: "audioEnabled", searchEnabled: "searchEnabled", audioSource: "audioSource", persistWindowsState: "persistWindowsState", title: "title", messagePlaceholder: "messagePlaceholder", searchPlaceholder: "searchPlaceholder", browserNotificationsEnabled: "browserNotificationsEnabled", browserNotificationIconSource: "browserNotificationIconSource", browserNotificationTitle: "browserNotificationTitle", historyPageSize: "historyPageSize", localization: "localization", hideFriendsList: "hideFriendsList", hideFriendsListOnUnsupportedViewport: "hideFriendsListOnUnsupportedViewport", fileUploadUrl: "fileUploadUrl", theme: "theme", customTheme: "customTheme", messageDatePipeFormat: "messageDatePipeFormat", showMessageDate: "showMessageDate", isViewportOnMobileEnabled: "isViewportOnMobileEnabled", fileUploadAdapter: "fileUploadAdapter" }, outputs: { onParticipantClicked: "onParticipantClicked", onParticipantChatOpened: "onParticipantChatOpened", onParticipantChatClosed: "onParticipantChatClosed", onMessagesSeen: "onMessagesSeen", onCloseWindowClicked: "onCloseWindowClicked", searchInputChanged: "searchInputChanged" }, host: { listeners: { "window:resize": "onResize($event)" } }, viewQueries: [{ propertyName: "chatWindows", predicate: ["chatWindow"], descendants: true }], ngImport: i0, template: "<link *ngIf=\"customTheme\" rel=\"stylesheet\" [href]='customTheme | sanitize'>\n\n<div id=\"ng-chat\" *ngIf=\"!isDisabled && isBootstrapped && !unsupportedViewport\" [ngClass]=\"theme\">\n <ng-chat-friends-list\n [localization]=\"localization\"\n [shouldDisplay]=\"!hideFriendsList\"\n [userId]=\"userId\"\n [isCollapsed]=\"isCollapsed\"\n [searchEnabled]=\"searchEnabled\"\n [participants]=\"participants\"\n [participantsResponse]=\"participantsResponse\"\n [participantsInteractedWith]=\"participantsInteractedWith\"\n [windows]=\"windows\"\n [currentActiveOption]=\"currentActiveOption\"\n (onParticipantClicked)=\"onParticipantClickedFromFriendsList($event)\"\n (onOptionPromptCanceled)=\"onOptionPromptCanceled()\"\n (onOptionPromptConfirmed)=\"onOptionPromptConfirmed($event)\"\n (onCloseWindowClick)=\"onCloseWindowClick($event)\"\n (searchInputChanged)=\"onSearchInputChanged($event)\"\n >\n </ng-chat-friends-list>\n\n <div *ngFor=\"let window of windows; let i = index\" [ngClass]=\"{'ng-chat-window': true, 'primary-outline-color': true, 'ng-chat-window-collapsed': window.isCollapsed}\" [ngStyle]=\"{'right': (!hideFriendsList ? friendsListWidth : 0) + 20 + windowSizeFactor * i + 'px'}\">\n <ng-chat-window\n #chatWindow\n [fileUploadAdapter]=\"fileUploadAdapter\"\n [localization]=\"localization\"\n [userId]=\"userId\"\n [window]=\"window\"\n [showOptions]=\"groupAdapter\"\n [emojisEnabled]=\"emojisEnabled\"\n [linkfyEnabled]=\"linkfyEnabled\"\n [showMessageDate]=\"showMessageDate\"\n [messageDatePipeFormat]=\"messageDatePipeFormat\"\n [hasPagedHistory]=\"hasPagedHistory\"\n (onMessagesSeen)=\"onWindowMessagesSeen($event)\"\n (onMessageSent)=\"onWindowMessageSent($event)\"\n (onTabTriggered)=\"onWindowTabTriggered($event)\"\n (onChatWindowClosed)=\"onWindowChatClosed($event)\"\n (onOptionTriggered)=\"onWindowOptionTriggered($event)\"\n (onLoadHistoryTriggered)=\"fetchMessageHistory($event)\"\n >\n </ng-chat-window>\n </div>\n</div>\n", styles: [".user-icon{box-sizing:border-box;background-color:#fff;border:2px solid;width:32px;height:20px;border-radius:64px 64px 0 0/64px;margin-top:14px;margin-left:-1px;display:inline-block;vertical-align:middle;position:relative;font-style:normal;color:#ddd;text-align:left;text-indent:-9999px}.user-icon:before{border:2px solid;background-color:#fff;width:12px;height:12px;top:-19px;border-radius:50%;position:absolute;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translate(-50%)}.user-icon:before,.user-icon:after{content:\"\";pointer-events:none}.upload-icon{position:absolute;margin-left:3px;margin-top:12px;width:13px;height:4px;border:solid 1px currentColor;border-top:none;border-radius:1px}.upload-icon:before{content:\"\";position:absolute;top:-8px;left:6px;width:1px;height:9px;background-color:currentColor}.upload-icon:after{content:\"\";position:absolute;top:-8px;left:4px;width:4px;height:4px;border-top:solid 1px currentColor;border-right:solid 1px currentColor;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.paperclip-icon{position:absolute;margin-left:9px;margin-top:2px;width:6px;height:12px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.paperclip-icon:before{content:\"\";position:absolute;top:11px;left:-1px;width:4px;height:6px;border-radius:0 0 3px 3px;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-bottom:solid 1px currentColor}.paperclip-icon:after{content:\"\";position:absolute;left:1px;top:1px;width:2px;height:10px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor}.check-icon{color:#000;position:absolute;margin-left:3px;margin-top:4px;width:14px;height:8px;border-bottom:solid 1px currentColor;border-left:solid 1px currentColor;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.remove-icon{color:#000;position:absolute;margin-left:3px;margin-top:10px}.remove-icon:before{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.remove-icon:after{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}\n", ".loader,.loader:before,.loader:after{background:#e3e3e3;-webkit-animation:load1 1s infinite ease-in-out;animation:load1 1s infinite ease-in-out;width:1em;height:4em}.loader{color:#e3e3e3;text-indent:-9999em;margin:4px auto 0;position:relative;font-size:4px;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation-delay:-.16s;animation-delay:-.16s}.loader:before,.loader:after{position:absolute;top:0;content:\"\"}.loader:before{left:-1.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.loader:after{left:1.5em}@-webkit-keyframes load1{0%,80%,to{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}@keyframes load1{0%,80%,to{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}\n", "#ng-chat{position:fixed;z-index:999;right:0;bottom:0;box-sizing:initial;font-size:11pt;text-align:left}#ng-chat input{outline:none}#ng-chat .shadowed{box-shadow:0 4px 8px #00000040}.ng-chat-loading-wrapper{height:30px;text-align:center;font-size:.9em}.ng-chat-close{text-decoration:none!important;color:red!important;float:right!important;font-weight:900!important;font-size:15px!important}.ng-chat-title,.ng-chat-title:hover{position:relative;z-index:2;height:30px;line-height:30px;font-size:.9em;padding:0 10px;display:block;text-decoration:none;color:#fff!important;font-weight:600!important;cursor:pointer;background-color:#1b1b70!important}.ng-chat-title .ng-chat-title-visibility-toggle-area{display:inline-block;width:85%}.ng-chat-title .ng-chat-title-visibility-toggle-area>strong{font-weight:600;display:block;overflow:hidden;height:30px;text-overflow:ellipsis;white-space:nowrap;max-width:85%;float:left}.ng-chat-title .ng-chat-title-visibility-toggle-area .ng-chat-participant-status{float:left;margin-left:5px}.ng-chat-participant-status{display:inline-block;border-radius:25px;width:8px;height:8px;margin-top:10px}.ng-chat-participant-status.online{background-color:#92a400}.ng-chat-participant-status.busy{background-color:#f91c1e}.ng-chat-participant-status.away{background-color:#f7d21b}.ng-chat-participant-status.offline{background-color:#bababa}.ng-chat-unread-messages-count{margin-left:5px;padding:0 5px;border-radius:25px;font-size:.9em;line-height:30px}.ng-chat-options-container{float:right;margin-right:5px}\n", "#ng-chat.light-theme .primary-text,#ng-chat.light-theme{color:#5c5c5c;font-family:Arial,Helvetica,sans-serif}#ng-chat.light-theme .primary-background{background-color:#fff}#ng-chat.light-theme .secondary-background{background-color:#fafafa}#ng-chat.light-theme .primary-outline-color{border-color:#a3a3a3}#ng-chat.light-theme .friends-search-bar{background-color:#fff}#ng-chat.light-theme .unread-messages-counter-container,#ng-chat.light-theme .ng-chat-people-action,#ng-chat.light-theme .ng-chat-people-action>i{color:#5c5c5c;background-color:#e3e3e3}#ng-chat.light-theme .load-history-action{background-color:#e3e3e3}#ng-chat.light-theme .chat-window-input{background-color:#fff}#ng-chat.light-theme .sent-chat-message-container,#ng-chat.light-theme .file-message-container{background-color:#e3e3e3;border-color:#e3e3e3}#ng-chat.light-theme .received-chat-message-container,#ng-chat.light-theme .file-message-container.received{background-color:#fff;border-color:#e3e3e3}body{font-family:Roboto,Helvetica Neue,sans-serif!important}*{box-shadow:none!important}\n", "#ng-chat.dark-theme .primary-text,#ng-chat.dark-theme{color:#fff;font-family:Arial,Helvetica,sans-serif}#ng-chat.dark-theme .primary-background{background-color:#565656}#ng-chat.dark-theme .secondary-background{background-color:#444}#ng-chat.dark-theme .primary-outline-color{border-color:#353535}#ng-chat.dark-theme .friends-search-bar{background-color:#444;border:1px solid #444;color:#fff}#ng-chat.dark-theme .unread-messages-counter-container,#ng-chat.dark-theme .ng-chat-people-action,#ng-chat.dark-theme .ng-chat-people-action>i{background-color:#fff;color:#444}#ng-chat.dark-theme .load-history-action{background-color:#444}#ng-chat.dark-theme .chat-window-input{background-color:#444;color:#fff}#ng-chat.dark-theme .sent-chat-message-container,#ng-chat.dark-theme .file-message-container{border-color:#444;background-color:#444}#ng-chat.dark-theme .received-chat-message-container,#ng-chat.dark-theme .file-message-container.received{background-color:#565656;border-color:#444}#ng-chat.dark-theme .ng-chat-footer{background-color:#444}#ng-chat.dark-theme .ng-chat-message a{color:#fff}body{font-family:Roboto,Helvetica Neue,sans-serif!important}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: NgChatFriendsListComponent, selector: "ng-chat-friends-list", inputs: ["participants", "participantsResponse", "participantsInteractedWith", "windows", "userId", "localization", "shouldDisplay", "isCollapsed", "searchEnabled", "currentActiveOption", "hideFriendsList"], outputs: ["onParticipantClicked", "onOptionPromptCanceled", "onOptionPromptConfirmed", "onCloseWindowClick", "searchInputChanged"] }, { kind: "component", type: NgChatWindowComponent, selector: "ng-chat-window", inputs: ["fileUploadAdapter", "window", "userId", "localization", "showOptions", "emojisEnabled", "linkfyEnabled", "showMessageDate", "messageDatePipeFormat", "hasPagedHistory"], outputs: ["onChatWindowClosed", "onMessagesSeen", "onMessageSent", "onTabTriggered", "onOptionTriggered", "onLoadHistoryTriggered"] }, { kind: "pipe", type: SanitizePipe, name: "sanitize" }], encapsulation: i0.ViewEncapsulation.None }); }
1299
+ }
1300
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChat, decorators: [{
1301
+ type: Component,
1302
+ args: [{ selector: 'mnl-chat', encapsulation: ViewEncapsulation.None, standalone: false, template: "<link *ngIf=\"customTheme\" rel=\"stylesheet\" [href]='customTheme | sanitize'>\n\n<div id=\"ng-chat\" *ngIf=\"!isDisabled && isBootstrapped && !unsupportedViewport\" [ngClass]=\"theme\">\n <ng-chat-friends-list\n [localization]=\"localization\"\n [shouldDisplay]=\"!hideFriendsList\"\n [userId]=\"userId\"\n [isCollapsed]=\"isCollapsed\"\n [searchEnabled]=\"searchEnabled\"\n [participants]=\"participants\"\n [participantsResponse]=\"participantsResponse\"\n [participantsInteractedWith]=\"participantsInteractedWith\"\n [windows]=\"windows\"\n [currentActiveOption]=\"currentActiveOption\"\n (onParticipantClicked)=\"onParticipantClickedFromFriendsList($event)\"\n (onOptionPromptCanceled)=\"onOptionPromptCanceled()\"\n (onOptionPromptConfirmed)=\"onOptionPromptConfirmed($event)\"\n (onCloseWindowClick)=\"onCloseWindowClick($event)\"\n (searchInputChanged)=\"onSearchInputChanged($event)\"\n >\n </ng-chat-friends-list>\n\n <div *ngFor=\"let window of windows; let i = index\" [ngClass]=\"{'ng-chat-window': true, 'primary-outline-color': true, 'ng-chat-window-collapsed': window.isCollapsed}\" [ngStyle]=\"{'right': (!hideFriendsList ? friendsListWidth : 0) + 20 + windowSizeFactor * i + 'px'}\">\n <ng-chat-window\n #chatWindow\n [fileUploadAdapter]=\"fileUploadAdapter\"\n [localization]=\"localization\"\n [userId]=\"userId\"\n [window]=\"window\"\n [showOptions]=\"groupAdapter\"\n [emojisEnabled]=\"emojisEnabled\"\n [linkfyEnabled]=\"linkfyEnabled\"\n [showMessageDate]=\"showMessageDate\"\n [messageDatePipeFormat]=\"messageDatePipeFormat\"\n [hasPagedHistory]=\"hasPagedHistory\"\n (onMessagesSeen)=\"onWindowMessagesSeen($event)\"\n (onMessageSent)=\"onWindowMessageSent($event)\"\n (onTabTriggered)=\"onWindowTabTriggered($event)\"\n (onChatWindowClosed)=\"onWindowChatClosed($event)\"\n (onOptionTriggered)=\"onWindowOptionTriggered($event)\"\n (onLoadHistoryTriggered)=\"fetchMessageHistory($event)\"\n >\n </ng-chat-window>\n </div>\n</div>\n", styles: [".user-icon{box-sizing:border-box;background-color:#fff;border:2px solid;width:32px;height:20px;border-radius:64px 64px 0 0/64px;margin-top:14px;margin-left:-1px;display:inline-block;vertical-align:middle;position:relative;font-style:normal;color:#ddd;text-align:left;text-indent:-9999px}.user-icon:before{border:2px solid;background-color:#fff;width:12px;height:12px;top:-19px;border-radius:50%;position:absolute;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translate(-50%)}.user-icon:before,.user-icon:after{content:\"\";pointer-events:none}.upload-icon{position:absolute;margin-left:3px;margin-top:12px;width:13px;height:4px;border:solid 1px currentColor;border-top:none;border-radius:1px}.upload-icon:before{content:\"\";position:absolute;top:-8px;left:6px;width:1px;height:9px;background-color:currentColor}.upload-icon:after{content:\"\";position:absolute;top:-8px;left:4px;width:4px;height:4px;border-top:solid 1px currentColor;border-right:solid 1px currentColor;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.paperclip-icon{position:absolute;margin-left:9px;margin-top:2px;width:6px;height:12px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.paperclip-icon:before{content:\"\";position:absolute;top:11px;left:-1px;width:4px;height:6px;border-radius:0 0 3px 3px;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-bottom:solid 1px currentColor}.paperclip-icon:after{content:\"\";position:absolute;left:1px;top:1px;width:2px;height:10px;border-radius:4px 4px 0 0;border-left:solid 1px currentColor;border-right:solid 1px currentColor;border-top:solid 1px currentColor}.check-icon{color:#000;position:absolute;margin-left:3px;margin-top:4px;width:14px;height:8px;border-bottom:solid 1px currentColor;border-left:solid 1px currentColor;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.remove-icon{color:#000;position:absolute;margin-left:3px;margin-top:10px}.remove-icon:before{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.remove-icon:after{content:\"\";position:absolute;width:15px;height:1px;background-color:currentColor;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}\n", ".loader,.loader:before,.loader:after{background:#e3e3e3;-webkit-animation:load1 1s infinite ease-in-out;animation:load1 1s infinite ease-in-out;width:1em;height:4em}.loader{color:#e3e3e3;text-indent:-9999em;margin:4px auto 0;position:relative;font-size:4px;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation-delay:-.16s;animation-delay:-.16s}.loader:before,.loader:after{position:absolute;top:0;content:\"\"}.loader:before{left:-1.5em;-webkit-animation-delay:-.32s;animation-delay:-.32s}.loader:after{left:1.5em}@-webkit-keyframes load1{0%,80%,to{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}@keyframes load1{0%,80%,to{box-shadow:0 0;height:4em}40%{box-shadow:0 -2em;height:5em}}\n", "#ng-chat{position:fixed;z-index:999;right:0;bottom:0;box-sizing:initial;font-size:11pt;text-align:left}#ng-chat input{outline:none}#ng-chat .shadowed{box-shadow:0 4px 8px #00000040}.ng-chat-loading-wrapper{height:30px;text-align:center;font-size:.9em}.ng-chat-close{text-decoration:none!important;color:red!important;float:right!important;font-weight:900!important;font-size:15px!important}.ng-chat-title,.ng-chat-title:hover{position:relative;z-index:2;height:30px;line-height:30px;font-size:.9em;padding:0 10px;display:block;text-decoration:none;color:#fff!important;font-weight:600!important;cursor:pointer;background-color:#1b1b70!important}.ng-chat-title .ng-chat-title-visibility-toggle-area{display:inline-block;width:85%}.ng-chat-title .ng-chat-title-visibility-toggle-area>strong{font-weight:600;display:block;overflow:hidden;height:30px;text-overflow:ellipsis;white-space:nowrap;max-width:85%;float:left}.ng-chat-title .ng-chat-title-visibility-toggle-area .ng-chat-participant-status{float:left;margin-left:5px}.ng-chat-participant-status{display:inline-block;border-radius:25px;width:8px;height:8px;margin-top:10px}.ng-chat-participant-status.online{background-color:#92a400}.ng-chat-participant-status.busy{background-color:#f91c1e}.ng-chat-participant-status.away{background-color:#f7d21b}.ng-chat-participant-status.offline{background-color:#bababa}.ng-chat-unread-messages-count{margin-left:5px;padding:0 5px;border-radius:25px;font-size:.9em;line-height:30px}.ng-chat-options-container{float:right;margin-right:5px}\n", "#ng-chat.light-theme .primary-text,#ng-chat.light-theme{color:#5c5c5c;font-family:Arial,Helvetica,sans-serif}#ng-chat.light-theme .primary-background{background-color:#fff}#ng-chat.light-theme .secondary-background{background-color:#fafafa}#ng-chat.light-theme .primary-outline-color{border-color:#a3a3a3}#ng-chat.light-theme .friends-search-bar{background-color:#fff}#ng-chat.light-theme .unread-messages-counter-container,#ng-chat.light-theme .ng-chat-people-action,#ng-chat.light-theme .ng-chat-people-action>i{color:#5c5c5c;background-color:#e3e3e3}#ng-chat.light-theme .load-history-action{background-color:#e3e3e3}#ng-chat.light-theme .chat-window-input{background-color:#fff}#ng-chat.light-theme .sent-chat-message-container,#ng-chat.light-theme .file-message-container{background-color:#e3e3e3;border-color:#e3e3e3}#ng-chat.light-theme .received-chat-message-container,#ng-chat.light-theme .file-message-container.received{background-color:#fff;border-color:#e3e3e3}body{font-family:Roboto,Helvetica Neue,sans-serif!important}*{box-shadow:none!important}\n", "#ng-chat.dark-theme .primary-text,#ng-chat.dark-theme{color:#fff;font-family:Arial,Helvetica,sans-serif}#ng-chat.dark-theme .primary-background{background-color:#565656}#ng-chat.dark-theme .secondary-background{background-color:#444}#ng-chat.dark-theme .primary-outline-color{border-color:#353535}#ng-chat.dark-theme .friends-search-bar{background-color:#444;border:1px solid #444;color:#fff}#ng-chat.dark-theme .unread-messages-counter-container,#ng-chat.dark-theme .ng-chat-people-action,#ng-chat.dark-theme .ng-chat-people-action>i{background-color:#fff;color:#444}#ng-chat.dark-theme .load-history-action{background-color:#444}#ng-chat.dark-theme .chat-window-input{background-color:#444;color:#fff}#ng-chat.dark-theme .sent-chat-message-container,#ng-chat.dark-theme .file-message-container{border-color:#444;background-color:#444}#ng-chat.dark-theme .received-chat-message-container,#ng-chat.dark-theme .file-message-container.received{background-color:#565656;border-color:#444}#ng-chat.dark-theme .ng-chat-footer{background-color:#444}#ng-chat.dark-theme .ng-chat-message a{color:#fff}body{font-family:Roboto,Helvetica Neue,sans-serif!important}\n"] }]
1303
+ }], ctorParameters: () => [{ type: i1$2.HttpClient }], propDecorators: { isDisabled: [{
1304
+ type: Input
1305
+ }], adapter: [{
1306
+ type: Input
1307
+ }], groupAdapter: [{
1308
+ type: Input
1309
+ }], userId: [{
1310
+ type: Input
1311
+ }], isCollapsed: [{
1312
+ type: Input
1313
+ }], maximizeWindowOnNewMessage: [{
1314
+ type: Input
1315
+ }], pollFriendsList: [{
1316
+ type: Input
1317
+ }], pollingInterval: [{
1318
+ type: Input
1319
+ }], historyEnabled: [{
1320
+ type: Input
1321
+ }], emojisEnabled: [{
1322
+ type: Input
1323
+ }], linkfyEnabled: [{
1324
+ type: Input
1325
+ }], audioEnabled: [{
1326
+ type: Input
1327
+ }], searchEnabled: [{
1328
+ type: Input
1329
+ }], audioSource: [{
1330
+ type: Input
1331
+ }], persistWindowsState: [{
1332
+ type: Input
1333
+ }], title: [{
1334
+ type: Input
1335
+ }], messagePlaceholder: [{
1336
+ type: Input
1337
+ }], searchPlaceholder: [{
1338
+ type: Input
1339
+ }], browserNotificationsEnabled: [{
1340
+ type: Input
1341
+ }], browserNotificationIconSource: [{
1342
+ type: Input
1343
+ }], browserNotificationTitle: [{
1344
+ type: Input
1345
+ }], historyPageSize: [{
1346
+ type: Input
1347
+ }], localization: [{
1348
+ type: Input
1349
+ }], hideFriendsList: [{
1350
+ type: Input
1351
+ }], hideFriendsListOnUnsupportedViewport: [{
1352
+ type: Input
1353
+ }], fileUploadUrl: [{
1354
+ type: Input
1355
+ }], theme: [{
1356
+ type: Input
1357
+ }], customTheme: [{
1358
+ type: Input
1359
+ }], messageDatePipeFormat: [{
1360
+ type: Input
1361
+ }], showMessageDate: [{
1362
+ type: Input
1363
+ }], isViewportOnMobileEnabled: [{
1364
+ type: Input
1365
+ }], fileUploadAdapter: [{
1366
+ type: Input
1367
+ }], onParticipantClicked: [{
1368
+ type: Output
1369
+ }], onParticipantChatOpened: [{
1370
+ type: Output
1371
+ }], onParticipantChatClosed: [{
1372
+ type: Output
1373
+ }], onMessagesSeen: [{
1374
+ type: Output
1375
+ }], onCloseWindowClicked: [{
1376
+ type: Output
1377
+ }], searchInputChanged: [{
1378
+ type: Output
1379
+ }], chatWindows: [{
1380
+ type: ViewChildren,
1381
+ args: ['chatWindow']
1382
+ }], onResize: [{
1383
+ type: HostListener,
1384
+ args: ['window:resize', ['$event']]
1385
+ }] } });
1386
+
1387
+ /* eslint-disable */
1388
+ class NgChatOptionsComponent {
1389
+ constructor() {
1390
+ this.activeOptionTrackerChange = new EventEmitter();
1391
+ }
1392
+ onOptionClicked(option) {
1393
+ option.isActive = true;
1394
+ if (option.action) {
1395
+ option.action(option.chattingTo);
1396
+ }
1397
+ this.activeOptionTrackerChange.emit(option);
1398
+ }
1399
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChatOptionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1400
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: NgChatOptionsComponent, isStandalone: false, selector: "ng-chat-options", inputs: { options: "options", activeOptionTracker: "activeOptionTracker" }, outputs: { activeOptionTrackerChange: "activeOptionTrackerChange" }, ngImport: i0, template: "<div *ngIf=\"options && options.length > 0\" class=\"ng-chat-options\">\n\t\t<button class=\"ng-chat-options-activator\">\n\t\t\t<span class=\"primary-text\">...</span>\n\t\t</button>\n\t<div class=\"ng-chat-options-content primary-background shadowed\">\n\t\t<a *ngFor=\"let option of options; let i = index\" [ngClass]=\"'primary-text'\" (click)=\"onOptionClicked(option)\">\n\t\t\t{{option.displayLabel}}\n\t\t</a>\n\t</div> \n</div>\n", styles: [".ng-chat-options-activator{background-color:unset;color:#fff;line-height:28px;border:none;position:relative}.ng-chat-options-activator>span{position:relative;top:-5px;left:0}.ng-chat-options{position:relative;display:inline-block}.ng-chat-options:hover .ng-chat-options-content{display:block}.ng-chat-options:hover .ng-chat-options-activator{background-color:#ddd}.ng-chat-options-content{display:none;position:absolute;min-width:160px;z-index:1}.ng-chat-options-content a:hover{background-color:#ddd}.ng-chat-options-content a{padding:6px 16px;text-decoration:none;display:block}@media only screen and (max-width: 581px){.ng-chat-options-content{right:0}}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
1401
+ }
1402
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NgChatOptionsComponent, decorators: [{
1403
+ type: Component,
1404
+ args: [{ selector: 'ng-chat-options', standalone: false, template: "<div *ngIf=\"options && options.length > 0\" class=\"ng-chat-options\">\n\t\t<button class=\"ng-chat-options-activator\">\n\t\t\t<span class=\"primary-text\">...</span>\n\t\t</button>\n\t<div class=\"ng-chat-options-content primary-background shadowed\">\n\t\t<a *ngFor=\"let option of options; let i = index\" [ngClass]=\"'primary-text'\" (click)=\"onOptionClicked(option)\">\n\t\t\t{{option.displayLabel}}\n\t\t</a>\n\t</div> \n</div>\n", styles: [".ng-chat-options-activator{background-color:unset;color:#fff;line-height:28px;border:none;position:relative}.ng-chat-options-activator>span{position:relative;top:-5px;left:0}.ng-chat-options{position:relative;display:inline-block}.ng-chat-options:hover .ng-chat-options-content{display:block}.ng-chat-options:hover .ng-chat-options-activator{background-color:#ddd}.ng-chat-options-content{display:none;position:absolute;min-width:160px;z-index:1}.ng-chat-options-content a:hover{background-color:#ddd}.ng-chat-options-content a{padding:6px 16px;text-decoration:none;display:block}@media only screen and (max-width: 581px){.ng-chat-options-content{right:0}}\n"] }]
1405
+ }], ctorParameters: () => [], propDecorators: { options: [{
1406
+ type: Input
1407
+ }], activeOptionTracker: [{
1408
+ type: Input
1409
+ }], activeOptionTrackerChange: [{
1410
+ type: Output
1411
+ }] } });
1412
+
1413
+ class ChatModule {
1414
+ static forRoot() {
1415
+ return {
1416
+ ngModule: ChatModule,
1417
+ providers: [
1418
+ {
1419
+ provide: APP_INITIALIZER,
1420
+ useFactory: loadConfig,
1421
+ deps: [
1422
+ AppConfigService,
1423
+ ChatService
1424
+ ],
1425
+ multi: true,
1426
+ },
1427
+ ],
1428
+ };
1429
+ }
1430
+ static forChild() {
1431
+ return {
1432
+ ngModule: ChatModule,
1433
+ };
1434
+ }
1435
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1436
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.17", ngImport: i0, type: ChatModule, declarations: [NgChat,
1437
+ EmojifyPipe,
1438
+ LinkfyPipe,
1439
+ SanitizePipe,
1440
+ GroupMessageDisplayNamePipe,
1441
+ NgChatOptionsComponent,
1442
+ NgChatFriendsListComponent,
1443
+ NgChatWindowComponent,
1444
+ FirstLetterPipe], imports: [ChatMaterialModule,
1445
+ CommonModule,
1446
+ FormsModule,
1447
+ HttpClientModule], exports: [NgChat] }); }
1448
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatModule, imports: [ChatMaterialModule,
1449
+ CommonModule,
1450
+ FormsModule,
1451
+ HttpClientModule] }); }
1452
+ }
1453
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ChatModule, decorators: [{
1454
+ type: NgModule,
1455
+ args: [{
1456
+ declarations: [
1457
+ NgChat,
1458
+ EmojifyPipe,
1459
+ LinkfyPipe,
1460
+ SanitizePipe,
1461
+ GroupMessageDisplayNamePipe,
1462
+ NgChatOptionsComponent,
1463
+ NgChatFriendsListComponent,
1464
+ NgChatWindowComponent,
1465
+ FirstLetterPipe
1466
+ ],
1467
+ imports: [
1468
+ ChatMaterialModule,
1469
+ CommonModule,
1470
+ FormsModule,
1471
+ HttpClientModule
1472
+ ],
1473
+ exports: [
1474
+ NgChat
1475
+ ],
1476
+ }]
1477
+ }] });
1478
+ function loadConfig(config) {
1479
+ const conf = () => config.load();
1480
+ return conf;
1481
+ }
1482
+
1483
+ class User {
1484
+ constructor() {
1485
+ this.participantType = ChatParticipantType.User;
1486
+ }
1487
+ }
1488
+
1489
+ class ParticipantResponse {
1490
+ }
1491
+
1492
+ class ParticipantMetadata {
1493
+ constructor() {
1494
+ this.totalUnreadMessages = 0;
1495
+ }
1496
+ }
1497
+
1498
+ /*
1499
+ * Public API Surface of mnl-chat
1500
+ */
1501
+
1502
+ /**
1503
+ * Generated bundle index. Do not edit.
1504
+ */
1505
+
1506
+ export { ChatAdapter, ChatMaterialModule, ChatModule, ChatParticipantStatus, ChatParticipantType, ChatService, Group, Message, MessageType, NgChat, PagedHistoryChatAdapter, ParticipantMetadata, ParticipantResponse, Theme, User, Window, loadConfig, modules };
1507
+ //# sourceMappingURL=muraai-mnl-chat.mjs.map