stream-chat-angular 4.67.0 → 4.68.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import * as i0 from '@angular/core';
3
3
  import { Injectable, Component, Input, EventEmitter, Output, ViewChild, InjectionToken, Directive, HostBinding, Inject, ChangeDetectionStrategy, NgModule } from '@angular/core';
4
4
  import { BehaviorSubject, ReplaySubject, combineLatest, Subject, timer } from 'rxjs';
5
5
  import { StreamChat } from 'stream-chat';
6
- import { take, shareReplay, map, first, filter, tap, distinctUntilChanged, debounceTime } from 'rxjs/operators';
6
+ import { take, shareReplay, map, first, filter, tap, distinctUntilChanged, debounceTime, throttleTime } from 'rxjs/operators';
7
7
  import { v4 } from 'uuid';
8
8
  import * as i9 from '@ngx-translate/core';
9
9
  import { TranslateModule } from '@ngx-translate/core';
@@ -20,7 +20,7 @@ import transliterate from '@stream-io/transliterate';
20
20
  import * as i8$1 from 'angular-mentions';
21
21
  import { MentionModule } from 'angular-mentions';
22
22
 
23
- const version = '4.67.0';
23
+ const version = '4.68.1';
24
24
 
25
25
  /**
26
26
  * The `NotificationService` can be used to add or remove notifications. By default the [`NotificationList`](../components/NotificationListComponent.mdx) component displays the currently active notifications.
@@ -111,6 +111,7 @@ class ChatClientService {
111
111
  this.pendingInvitesSubject = new BehaviorSubject([]);
112
112
  this.userSubject = new ReplaySubject(1);
113
113
  this.subscriptions = [];
114
+ this.trackPendingChannelInvites = true;
114
115
  this.events$ = this.notificationSubject.asObservable();
115
116
  this.connectionState$ = this.connectionStateSubject.asObservable();
116
117
  this.appSettings$ = this.appSettingsSubject.asObservable();
@@ -130,21 +131,23 @@ class ChatClientService {
130
131
  * @param clientOptions Setting to provide to the Stream client instance
131
132
  */
132
133
  init(apiKey, userOrId, userTokenOrProvider, clientOptions) {
133
- var _a;
134
+ var _a, _b;
134
135
  return __awaiter(this, void 0, void 0, function* () {
136
+ this.trackPendingChannelInvites =
137
+ (clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.trackPendingChannelInvites) !== false;
135
138
  this.chatClient = StreamChat.getInstance(apiKey, clientOptions);
136
139
  this.chatClient.recoverStateOnReconnect = false;
137
140
  this.chatClient.devToken;
138
141
  let result;
139
142
  yield this.ngZone.runOutsideAngular(() => __awaiter(this, void 0, void 0, function* () {
140
- var _b;
143
+ var _c;
141
144
  const user = typeof userOrId === 'string' ? { id: userOrId } : userOrId;
142
145
  try {
143
- result = yield ((_b = {
146
+ result = yield ((_c = {
144
147
  guest: () => this.chatClient.setGuestUser(user),
145
148
  anonymous: () => this.chatClient.connectAnonymousUser(),
146
149
  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
147
- }[`${userTokenOrProvider}`]) !== null && _b !== void 0 ? _b : (() => this.chatClient.connectUser(user, userTokenOrProvider)))();
150
+ }[`${userTokenOrProvider}`]) !== null && _c !== void 0 ? _c : (() => this.chatClient.connectUser(user, userTokenOrProvider)))();
148
151
  }
149
152
  catch (error) {
150
153
  this.notificationService.addPermanentNotification('streamChat.Error connecting to chat, refresh the page to try again.', 'error');
@@ -156,9 +159,14 @@ class ChatClientService {
156
159
  this.chatClient.setUserAgent(`${sdkPrefix}-${version}-${this.chatClient.getUserAgent()}`);
157
160
  }
158
161
  }));
159
- const channels = yield this.chatClient.queryChannels({ invite: 'pending' }, // TODO: find out why we need this typecast
160
- {}, { user_id: (_a = this.chatClient.user) === null || _a === void 0 ? void 0 : _a.id });
161
- this.pendingInvitesSubject.next(channels);
162
+ if (((_a = this.chatClient.user) === null || _a === void 0 ? void 0 : _a.id) && this.trackPendingChannelInvites) {
163
+ const channels = yield this.chatClient.queryChannels({
164
+ invite: 'pending',
165
+ members: { $in: [(_b = this.chatClient.user) === null || _b === void 0 ? void 0 : _b.id] },
166
+ } // TODO: find out why we need this typecast
167
+ );
168
+ this.pendingInvitesSubject.next(channels);
169
+ }
162
170
  this.appSettingsSubject.next(undefined);
163
171
  this.subscriptions.push(this.chatClient.on((e) => {
164
172
  this.updateUser(e);
@@ -241,6 +249,9 @@ class ChatClientService {
241
249
  }
242
250
  updatePendingInvites(e) {
243
251
  var _a, _b, _c;
252
+ if (!this.trackPendingChannelInvites) {
253
+ return;
254
+ }
244
255
  if (((_b = (_a = e.member) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.id) === ((_c = this.chatClient.user) === null || _c === void 0 ? void 0 : _c.id) && e.channel) {
245
256
  const pendingInvites = this.pendingInvitesSubject.getValue();
246
257
  if (e.type === 'notification.invited') {
@@ -436,6 +447,23 @@ class ChannelService {
436
447
  .asObservable()
437
448
  .pipe(shareReplay(1));
438
449
  }
450
+ /**
451
+ * internal
452
+ */
453
+ removeOldMessageFromMessageList() {
454
+ const channel = this.activeChannelSubject.getValue();
455
+ const channelMessages = channel === null || channel === void 0 ? void 0 : channel.state.latestMessages;
456
+ const targetLength = Math.ceil(ChannelService.MAX_MESSAGE_COUNT_IN_MESSAGE_LIST / 2);
457
+ if (!channel ||
458
+ !channelMessages ||
459
+ channelMessages !== (channel === null || channel === void 0 ? void 0 : channel.state.latestMessages) ||
460
+ channelMessages.length <= targetLength) {
461
+ return;
462
+ }
463
+ const messages = channelMessages;
464
+ messages.splice(0, messages.length - targetLength);
465
+ this.activeChannelMessagesSubject.next(messages);
466
+ }
439
467
  /**
440
468
  * If set to false, read events won't be sent as new messages are received. If set to true active channel (if any) will immediately be marked as read.
441
469
  */
@@ -1005,6 +1033,12 @@ class ChannelService {
1005
1033
  }
1006
1034
  });
1007
1035
  }
1036
+ /**
1037
+ * Clears the currently selected message to jump
1038
+ */
1039
+ clearMessageJump() {
1040
+ this.jumpToMessageSubject.next({ id: undefined, parentId: undefined });
1041
+ }
1008
1042
  /**
1009
1043
  * Pins the given message in the channel
1010
1044
  * @param message
@@ -1798,7 +1832,11 @@ class ChannelService {
1798
1832
  }
1799
1833
  }
1800
1834
  /**
1801
- * internal
1835
+ * @internal
1836
+ */
1837
+ ChannelService.MAX_MESSAGE_COUNT_IN_MESSAGE_LIST = 250;
1838
+ /**
1839
+ * @internal
1802
1840
  */
1803
1841
  ChannelService.MAX_MESSAGE_REACTIONS_TO_FETCH = 1200;
1804
1842
  ChannelService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: ChannelService, deps: [{ token: ChatClientService }, { token: i0.NgZone }, { token: NotificationService }], target: i0.ɵɵFactoryTarget.Injectable });
@@ -6432,6 +6470,10 @@ class MessageListComponent {
6432
6470
  * You can turn on and off the loading indicator that signals to users that more messages are being loaded to the message list
6433
6471
  */
6434
6472
  this.displayLoadingIndicator = true;
6473
+ /**
6474
+ * @internal
6475
+ */
6476
+ this.limitNumberOfMessagesInList = true;
6435
6477
  this.emptyMainMessageListTemplate = null;
6436
6478
  this.emptyThreadMessageListTemplate = null;
6437
6479
  this.enabledMessageActions = [];
@@ -6447,6 +6489,8 @@ class MessageListComponent {
6447
6489
  this.isLatestMessageInList = true;
6448
6490
  this.parsedDates = new Map();
6449
6491
  this.isViewInited = false;
6492
+ this.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
6493
+ this.forceRepaintSubject = new Subject();
6450
6494
  this.messageNotificationJumpClicked = () => {
6451
6495
  this.jumpToFirstUnreadMessage();
6452
6496
  this.isUnreadNotificationVisible = false;
@@ -6461,6 +6505,9 @@ class MessageListComponent {
6461
6505
  return `str-chat-angular__main-panel-inner str-chat-angular__message-list-host str-chat__main-panel-inner ${this.isEmpty ? 'str-chat-angular__message-list-host--empty' : ''}`;
6462
6506
  }
6463
6507
  ngOnInit() {
6508
+ this.subscriptions.push(this.forceRepaintSubject.pipe(throttleTime(1000)).subscribe(() => {
6509
+ this.forceRepaint();
6510
+ }));
6464
6511
  this.subscriptions.push(this.channelService.activeChannel$.subscribe((channel) => {
6465
6512
  var _a, _b, _c, _d, _e, _f, _g;
6466
6513
  (_b = (_a = this.chatClientService.chatClient) === null || _a === void 0 ? void 0 : _a.logger) === null || _b === void 0 ? void 0 : _b.call(_a, 'info', `${(channel === null || channel === void 0 ? void 0 : channel.cid) || 'undefined'} selected`, { tags: `message list ${this.mode}` });
@@ -6473,6 +6520,9 @@ class MessageListComponent {
6473
6520
  this.isUnreadNotificationVisible = false;
6474
6521
  (_e = (_d = (_c = this.chatClientService) === null || _c === void 0 ? void 0 : _c.chatClient) === null || _d === void 0 ? void 0 : _d.logger) === null || _e === void 0 ? void 0 : _e.call(_d, 'info', `new channel is different from prev channel, reseting scroll state`, { tags: `message list ${this.mode}` });
6475
6522
  this.parsedDates = new Map();
6523
+ if (this.messageRemoveTimeout) {
6524
+ clearTimeout(this.messageRemoveTimeout);
6525
+ }
6476
6526
  this.resetScrollState();
6477
6527
  this.channelId = channel === null || channel === void 0 ? void 0 : channel.id;
6478
6528
  if (this.isViewInited) {
@@ -6621,6 +6671,9 @@ class MessageListComponent {
6621
6671
  .subscribe((config) => {
6622
6672
  var _a, _b;
6623
6673
  let messageId = undefined;
6674
+ if (this.messageRemoveTimeout) {
6675
+ clearTimeout(this.messageRemoveTimeout);
6676
+ }
6624
6677
  if (this.mode === 'main') {
6625
6678
  messageId = config.parentId || config.id;
6626
6679
  }
@@ -6647,6 +6700,7 @@ class MessageListComponent {
6647
6700
  }
6648
6701
  }
6649
6702
  }
6703
+ this.channelService.clearMessageJump();
6650
6704
  }));
6651
6705
  this.subscriptions.push(this.customTemplatesService.emptyMainMessageListPlaceholder$.subscribe((template) => {
6652
6706
  const isChanged = this.emptyMainMessageListTemplate !== template;
@@ -6730,7 +6784,7 @@ class MessageListComponent {
6730
6784
  }
6731
6785
  }
6732
6786
  ngOnDestroy() {
6733
- var _a;
6787
+ var _a, _b;
6734
6788
  this.subscriptions.forEach((s) => s.unsubscribe());
6735
6789
  (_a = this.newMessageSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
6736
6790
  if (this.scrollEndTimeout) {
@@ -6742,6 +6796,10 @@ class MessageListComponent {
6742
6796
  if (this.jumpToLatestButtonVisibilityTimeout) {
6743
6797
  clearTimeout(this.jumpToLatestButtonVisibilityTimeout);
6744
6798
  }
6799
+ if (this.messageRemoveTimeout) {
6800
+ clearTimeout(this.messageRemoveTimeout);
6801
+ }
6802
+ (_b = this.removeOldMessagesSubscription) === null || _b === void 0 ? void 0 : _b.unsubscribe();
6745
6803
  }
6746
6804
  trackByMessageId(index, item) {
6747
6805
  return item.id;
@@ -6756,7 +6814,9 @@ class MessageListComponent {
6756
6814
  scrollToBottom() {
6757
6815
  this.scrollContainer.nativeElement.scrollTop =
6758
6816
  this.scrollContainer.nativeElement.scrollHeight + 0.1;
6759
- this.forceRepaint();
6817
+ if (this.isSafari) {
6818
+ this.forceRepaintSubject.next();
6819
+ }
6760
6820
  }
6761
6821
  scrollToTop() {
6762
6822
  this.scrollContainer.nativeElement.scrollTop = 0;
@@ -6799,7 +6859,7 @@ class MessageListComponent {
6799
6859
  }
6800
6860
  if (this.shouldLoadMoreMessages(scrollPosition)) {
6801
6861
  this.ngZone.run(() => {
6802
- var _a, _b;
6862
+ var _a, _b, _c;
6803
6863
  this.containerHeight = this.scrollContainer.nativeElement.scrollHeight;
6804
6864
  let direction;
6805
6865
  if (this.direction === 'top-to-bottom') {
@@ -6814,6 +6874,9 @@ class MessageListComponent {
6814
6874
  if (result) {
6815
6875
  (_b = (_a = this.chatClientService.chatClient) === null || _a === void 0 ? void 0 : _a.logger) === null || _b === void 0 ? void 0 : _b.call(_a, 'info', `Displaying loading indicator`, { tags: `message list ${this.mode}` });
6816
6876
  this.isLoading = true;
6877
+ (_c = result.catch) === null || _c === void 0 ? void 0 : _c.call(result, () => {
6878
+ this.isLoading = false;
6879
+ });
6817
6880
  }
6818
6881
  this.cdRef.detectChanges();
6819
6882
  });
@@ -6882,16 +6945,20 @@ class MessageListComponent {
6882
6945
  position = 'top';
6883
6946
  }
6884
6947
  else if (Math.ceil(this.scrollContainer.nativeElement.scrollTop) +
6885
- this.scrollContainer.nativeElement.clientHeight >=
6948
+ this.scrollContainer.nativeElement.clientHeight +
6949
+ 1 >=
6886
6950
  this.scrollContainer.nativeElement.scrollHeight) {
6887
6951
  position = 'bottom';
6888
6952
  }
6889
6953
  return position;
6890
6954
  }
6891
6955
  shouldLoadMoreMessages(scrollPosition) {
6892
- return scrollPosition !== 'middle' && !this.highlightedMessageId;
6956
+ return (scrollPosition !== 'middle' &&
6957
+ !this.highlightedMessageId &&
6958
+ !this.isLoading);
6893
6959
  }
6894
6960
  setMessages$() {
6961
+ var _a;
6895
6962
  this.messages$ = (this.mode === 'main'
6896
6963
  ? this.channelService.activeChannelMessages$
6897
6964
  : this.channelService.activeThreadMessages$).pipe(tap((messages) => {
@@ -6955,7 +7022,40 @@ class MessageListComponent {
6955
7022
  lastReadMessageId: this.lastReadMessageId,
6956
7023
  }));
6957
7024
  this.isNextMessageOnSeparateDate = messages.map((m, i) => this.checkIfOnSeparateDates(m, messages[i + 1]));
6958
- }));
7025
+ }), shareReplay(1));
7026
+ (_a = this.removeOldMessagesSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
7027
+ this.removeOldMessagesSubscription = combineLatest([
7028
+ this.channelService.jumpToMessage$,
7029
+ this.messages$,
7030
+ ]).subscribe(([jumpToMessage, messages]) => {
7031
+ if (this.limitNumberOfMessagesInList &&
7032
+ this.mode === 'main' &&
7033
+ messages.length >
7034
+ ChannelService.MAX_MESSAGE_COUNT_IN_MESSAGE_LIST * 0.5 &&
7035
+ !this.isUserScrolled &&
7036
+ !(jumpToMessage === null || jumpToMessage === void 0 ? void 0 : jumpToMessage.id) &&
7037
+ this.isLatestMessageInList) {
7038
+ if (this.messageRemoveTimeout) {
7039
+ clearTimeout(this.messageRemoveTimeout);
7040
+ }
7041
+ if (messages.length >= ChannelService.MAX_MESSAGE_COUNT_IN_MESSAGE_LIST) {
7042
+ this.channelService.removeOldMessageFromMessageList();
7043
+ }
7044
+ else {
7045
+ this.messageRemoveTimeout = setTimeout(() => {
7046
+ if (this.limitNumberOfMessagesInList &&
7047
+ this.mode === 'main' &&
7048
+ messages.length >
7049
+ ChannelService.MAX_MESSAGE_COUNT_IN_MESSAGE_LIST * 0.5 &&
7050
+ !this.isUserScrolled &&
7051
+ !this.highlightedMessageId &&
7052
+ this.isLatestMessageInList) {
7053
+ this.channelService.removeOldMessageFromMessageList();
7054
+ }
7055
+ }, 1500);
7056
+ }
7057
+ }
7058
+ });
6959
7059
  }
6960
7060
  resetScrollState() {
6961
7061
  this.isEmpty = true;
@@ -7038,7 +7138,7 @@ class MessageListComponent {
7038
7138
  }
7039
7139
  }
7040
7140
  MessageListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageListComponent, deps: [{ token: ChannelService }, { token: ChatClientService }, { token: CustomTemplatesService }, { token: DateParserService }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: MessageActionsService }], target: i0.ɵɵFactoryTarget.Component });
7041
- MessageListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageListComponent, selector: "stream-message-list", inputs: { mode: "mode", direction: "direction", messageOptionsTrigger: "messageOptionsTrigger", hideJumpToLatestButtonDuringScroll: "hideJumpToLatestButtonDuringScroll", customMessageActions: "customMessageActions", displayDateSeparator: "displayDateSeparator", displayUnreadSeparator: "displayUnreadSeparator", dateSeparatorTextPos: "dateSeparatorTextPos", openMessageListAt: "openMessageListAt", hideUnreadCountForNotificationAndIndicator: "hideUnreadCountForNotificationAndIndicator", displayLoadingIndicator: "displayLoadingIndicator" }, host: { properties: { "class": "this.class" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }, { propertyName: "parentMessageElement", first: true, predicate: ["parentMessageElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container\n *ngIf=\"\n lastReadMessageId &&\n isUnreadNotificationVisible &&\n openMessageListAt === 'last-message' &&\n displayUnreadSeparator\n \"\n>\n <ng-container\n *ngTemplateOutlet=\"\n customnewMessagesNotificationTemplate ||\n defaultUnreadMessagesNotification;\n context: {\n unreadCount: unreadCount,\n onDismiss: messageNotificationDismissClicked,\n onJump: messageNotificationJumpClicked\n }\n \"\n ></ng-container>\n</ng-container>\n<ng-template\n #defaultUnreadMessagesNotification\n let-unreadCount=\"unreadCount\"\n let-onDismiss=\"onDismiss\"\n let-onJump=\"onJump\"\n>\n <div\n class=\"str-chat__unread-messages-notification\"\n data-testid=\"unread-messages-notification\"\n >\n <button\n data-testid=\"unread-messages-notification-jump-to-message\"\n (click)=\"onJump()\"\n >\n <ng-container\n *ngIf=\"\n unreadCount > 0 && !hideUnreadCountForNotificationAndIndicator;\n else noUnreadCount\n \"\n >\n {{\n (unreadCount === 1\n ? \"streamChat.\\{\\{count\\}\\} unread message\"\n : \"streamChat.\\{\\{count\\}\\} unread messages\"\n ) | translate: { count: unreadCount }\n }}\n </ng-container>\n <ng-template #noUnreadCount>\n {{ \"streamChat.Unread messages\" | translate }}\n </ng-template>\n </button>\n <button\n data-testid=\"unread-messages-notification-dismiss\"\n (click)=\"onDismiss()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n</ng-template>\n<div #scrollContainer data-testid=\"scroll-container\" class=\"str-chat__list\">\n <ng-container *ngIf=\"mode === 'main' && isEmpty && emptyListTemplate\">\n <ng-container *ngTemplateOutlet=\"emptyListTemplate\"></ng-container>\n </ng-container>\n <div class=\"str-chat__reverse-infinite-scroll str-chat__message-list-scroll\">\n <ul\n class=\"str-chat__ul\"\n [class.str-chat__message-options-in-bubble]=\"\n messageOptionsTrigger === 'message-bubble'\n \"\n >\n <li\n #parentMessageElement\n *ngIf=\"mode === 'thread' && parentMessage\"\n data-testid=\"parent-message\"\n class=\"str-chat__parent-message-li\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n messageTemplateContainer;\n context: { message: parentMessage, index: 'parent' }\n \"\n ></ng-container>\n <div data-testid=\"reply-count\" class=\"str-chat__thread-start\">\n {{parentMessage?.reply_count === 1 ? ('streamChat.1 reply' | translate) : ('streamChat.{{ replyCount }}\n replies' | translate:replyCountParam)}}\n </div>\n </li>\n <ng-container *ngIf=\"mode === 'thread' && isEmpty && emptyListTemplate\">\n <ng-container *ngTemplateOutlet=\"emptyListTemplate\"></ng-container>\n </ng-container>\n <stream-loading-indicator\n data-testid=\"top-loading-indicator\"\n *ngIf=\"\n isLoading && direction === 'bottom-to-top' && displayLoadingIndicator\n \"\n ></stream-loading-indicator>\n <ng-container *ngIf=\"messages$ | async as messages\">\n <ng-container\n *ngFor=\"\n let message of messages;\n let i = index;\n let isFirst = first;\n let isLast = last;\n trackBy: trackByMessageId\n \"\n >\n <ng-container *ngIf=\"isFirst\">\n <ng-container\n *ngTemplateOutlet=\"\n dateSeparator;\n context: {\n date: message.created_at,\n parsedDate: parseDate(message.created_at)\n }\n \"\n ></ng-container>\n </ng-container>\n <li\n tabindex=\"0\"\n data-testclass=\"message\"\n class=\"str-chat__li str-chat__li--{{ groupStyles[i] }}\"\n id=\"{{ message.id }}\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n messageTemplateContainer;\n context: { message: message, index: i }\n \"\n ></ng-container>\n </li>\n <ng-container\n *ngIf=\"\n (lastReadMessageId === message?.id &&\n direction === 'bottom-to-top') ||\n (direction === 'top-to-bottom' &&\n lastReadMessageId === messages[i + 1]?.id)\n \"\n >\n <li\n *ngIf=\"displayUnreadSeparator\"\n id=\"stream-chat-new-message-indicator\"\n data-testid=\"new-messages-indicator\"\n class=\"str-chat__li str-chat__unread-messages-separator-wrapper\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customnewMessagesIndicatorTemplate ||\n defaultNewMessagesIndicator;\n context: { unreadCount: unreadCount }\n \"\n ></ng-container>\n </li>\n </ng-container>\n <ng-container *ngIf=\"isNextMessageOnSeparateDate[i]\">\n <ng-container\n *ngTemplateOutlet=\"\n dateSeparator;\n context: {\n date: messages[i + 1].created_at,\n parsedDate: parseDate(messages[i + 1].created_at)\n }\n \"\n ></ng-container>\n </ng-container>\n </ng-container>\n </ng-container>\n <stream-loading-indicator\n data-testid=\"bottom-loading-indicator\"\n *ngIf=\"\n isLoading && direction === 'top-to-bottom' && displayLoadingIndicator\n \"\n ></stream-loading-indicator>\n </ul>\n <ng-template #defaultTypingIndicator let-usersTyping$=\"usersTyping$\">\n <!-- eslint-disable-next-line @angular-eslint/template/no-any -->\n <ng-container *ngIf=\"$any(usersTyping$ | async) as users\">\n <div\n *ngIf=\"users.length > 0\"\n data-testid=\"typing-indicator\"\n class=\"str-chat__typing-indicator str-chat__typing-indicator--typing\"\n >\n <div class=\"str-chat__typing-indicator__dots\">\n <span class=\"str-chat__typing-indicator__dot\"></span>\n <span class=\"str-chat__typing-indicator__dot\"></span>\n <span class=\"str-chat__typing-indicator__dot\"></span>\n </div>\n <div\n data-testid=\"typing-users\"\n class=\"str-chat__typing-indicator__users\"\n >\n {{\n users.length === 1\n ? (\"streamChat.user is typing\"\n | translate: { user: getTypingIndicatorText(users) })\n : (\"streamChat.users are typing\"\n | translate: { users: getTypingIndicatorText(users) })\n }}\n </div>\n </div>\n </ng-container>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n typingIndicatorTemplate || defaultTypingIndicator;\n context: getTypingIndicatorContext()\n \"\n ></ng-container>\n </div>\n</div>\n<div class=\"str-chat__jump-to-latest-message\">\n <button\n data-testid=\"scroll-to-latest\"\n *ngIf=\"isUserScrolled && isJumpToLatestButtonVisible\"\n class=\"\n str-chat__message-notification-scroll-to-latest\n str-chat__message-notification-scroll-to-latest-right\n str-chat__circle-fab\n \"\n (keyup.enter)=\"jumpToLatestMessage()\"\n (click)=\"jumpToLatestMessage()\"\n >\n <stream-icon\n class=\"str-chat__jump-to-latest-icon str-chat__circle-fab-icon\"\n [icon]=\"direction === 'bottom-to-top' ? 'arrow-down' : 'arrow-up'\"\n ></stream-icon>\n <div\n *ngIf=\"newMessageCountWhileBeingScrolled > 0\"\n class=\"\n str-chat__message-notification\n str-chat__message-notification-scroll-to-latest-unread-count\n str-chat__jump-to-latest-unread-count\n \"\n >\n {{ newMessageCountWhileBeingScrolled }}\n </div>\n </button>\n</div>\n\n<ng-template #messageTemplateContainer let-message=\"message\" let-index=\"index\">\n <ng-template\n #defaultMessageTemplate\n let-messageInput=\"message\"\n let-isLastSentMessage=\"isLastSentMessage\"\n let-enabledMessageActions=\"enabledMessageActions\"\n let-mode=\"mode\"\n let-isHighlighted=\"isHighlighted\"\n let-customActions=\"customActions\"\n >\n <stream-message\n [message]=\"messageInput\"\n [isLastSentMessage]=\"isLastSentMessage\"\n [enabledMessageActions]=\"enabledMessageActions\"\n [mode]=\"mode\"\n [isHighlighted]=\"isHighlighted\"\n [customActions]=\"customActions\"\n ></stream-message>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n messageTemplate || defaultMessageTemplate;\n context: {\n message: message,\n isLastSentMessage: !!(\n lastSentMessageId && message?.id === lastSentMessageId\n ),\n enabledMessageActions: enabledMessageActions,\n mode: mode,\n isHighlighted: message?.id === highlightedMessageId,\n customActions: customMessageActions\n }\n \"\n ></ng-container>\n</ng-template>\n\n<ng-template #dateSeparator let-date=\"date\" let-parsedDate=\"parsedDate\">\n <ng-container *ngIf=\"displayDateSeparator\">\n <ng-container\n *ngTemplateOutlet=\"\n customDateSeparatorTemplate || defaultDateSeparator;\n context: {\n date: date,\n parsedDate: parsedDate\n }\n \"\n ></ng-container>\n </ng-container>\n\n <ng-template\n #defaultDateSeparator\n let-date=\"date\"\n let-parsedDate=\"parsedDate\"\n >\n <div data-testid=\"date-separator\" class=\"str-chat__date-separator\">\n <hr\n *ngIf=\"\n dateSeparatorTextPos === 'right' || dateSeparatorTextPos === 'center'\n \"\n class=\"str-chat__date-separator-line\"\n />\n <div class=\"str-chat__date-separator-date\">\n {{ parsedDate }}\n </div>\n <hr\n *ngIf=\"\n dateSeparatorTextPos === 'left' || dateSeparatorTextPos === 'center'\n \"\n class=\"str-chat__date-separator-line\"\n />\n </div>\n </ng-template>\n</ng-template>\n\n<ng-template #defaultNewMessagesIndicator let-unreadCount=\"unreadCount\">\n <div class=\"str-chat__unread-messages-separator\">\n <ng-container\n *ngIf=\"\n unreadCount > 0 && !hideUnreadCountForNotificationAndIndicator;\n else noUnreadCount\n \"\n >\n {{\n (unreadCount === 1\n ? \"streamChat.\\{\\{count\\}\\} unread message\"\n : \"streamChat.\\{\\{count\\}\\} unread messages\"\n ) | translate: { count: unreadCount }\n }}\n </ng-container>\n <ng-template #noUnreadCount>\n {{ \"streamChat.Unread messages\" | translate }}\n </ng-template>\n </div>\n</ng-template>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: LoadingIndicatorComponent, selector: "stream-loading-indicator", inputs: ["size", "color"] }, { type: IconComponent, selector: "stream-icon", inputs: ["icon", "size"] }, { type: MessageComponent, selector: "stream-message", inputs: ["message", "enabledMessageActions", "isLastSentMessage", "mode", "isHighlighted", "customActions"] }], directives: [{ type: i8.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i8.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i8.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "translate": i9.TranslatePipe, "async": i8.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
7141
+ MessageListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageListComponent, selector: "stream-message-list", inputs: { mode: "mode", direction: "direction", messageOptionsTrigger: "messageOptionsTrigger", hideJumpToLatestButtonDuringScroll: "hideJumpToLatestButtonDuringScroll", customMessageActions: "customMessageActions", displayDateSeparator: "displayDateSeparator", displayUnreadSeparator: "displayUnreadSeparator", dateSeparatorTextPos: "dateSeparatorTextPos", openMessageListAt: "openMessageListAt", hideUnreadCountForNotificationAndIndicator: "hideUnreadCountForNotificationAndIndicator", displayLoadingIndicator: "displayLoadingIndicator", limitNumberOfMessagesInList: "limitNumberOfMessagesInList" }, host: { properties: { "class": "this.class" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }, { propertyName: "parentMessageElement", first: true, predicate: ["parentMessageElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container\n *ngIf=\"\n lastReadMessageId &&\n isUnreadNotificationVisible &&\n openMessageListAt === 'last-message' &&\n displayUnreadSeparator\n \"\n>\n <ng-container\n *ngTemplateOutlet=\"\n customnewMessagesNotificationTemplate ||\n defaultUnreadMessagesNotification;\n context: {\n unreadCount: unreadCount,\n onDismiss: messageNotificationDismissClicked,\n onJump: messageNotificationJumpClicked\n }\n \"\n ></ng-container>\n</ng-container>\n<ng-template\n #defaultUnreadMessagesNotification\n let-unreadCount=\"unreadCount\"\n let-onDismiss=\"onDismiss\"\n let-onJump=\"onJump\"\n>\n <div\n class=\"str-chat__unread-messages-notification\"\n data-testid=\"unread-messages-notification\"\n >\n <button\n data-testid=\"unread-messages-notification-jump-to-message\"\n (click)=\"onJump()\"\n >\n <ng-container\n *ngIf=\"\n unreadCount > 0 && !hideUnreadCountForNotificationAndIndicator;\n else noUnreadCount\n \"\n >\n {{\n (unreadCount === 1\n ? \"streamChat.\\{\\{count\\}\\} unread message\"\n : \"streamChat.\\{\\{count\\}\\} unread messages\"\n ) | translate: { count: unreadCount }\n }}\n </ng-container>\n <ng-template #noUnreadCount>\n {{ \"streamChat.Unread messages\" | translate }}\n </ng-template>\n </button>\n <button\n data-testid=\"unread-messages-notification-dismiss\"\n (click)=\"onDismiss()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n</ng-template>\n<div #scrollContainer data-testid=\"scroll-container\" class=\"str-chat__list\">\n <ng-container *ngIf=\"mode === 'main' && isEmpty && emptyListTemplate\">\n <ng-container *ngTemplateOutlet=\"emptyListTemplate\"></ng-container>\n </ng-container>\n <div class=\"str-chat__reverse-infinite-scroll str-chat__message-list-scroll\">\n <ul\n class=\"str-chat__ul\"\n [class.str-chat__message-options-in-bubble]=\"\n messageOptionsTrigger === 'message-bubble'\n \"\n >\n <li\n #parentMessageElement\n *ngIf=\"mode === 'thread' && parentMessage\"\n data-testid=\"parent-message\"\n class=\"str-chat__parent-message-li\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n messageTemplateContainer;\n context: { message: parentMessage, index: 'parent' }\n \"\n ></ng-container>\n <div data-testid=\"reply-count\" class=\"str-chat__thread-start\">\n {{parentMessage?.reply_count === 1 ? ('streamChat.1 reply' | translate) : ('streamChat.{{ replyCount }}\n replies' | translate:replyCountParam)}}\n </div>\n </li>\n <ng-container *ngIf=\"mode === 'thread' && isEmpty && emptyListTemplate\">\n <ng-container *ngTemplateOutlet=\"emptyListTemplate\"></ng-container>\n </ng-container>\n <stream-loading-indicator\n data-testid=\"top-loading-indicator\"\n *ngIf=\"\n isLoading && direction === 'bottom-to-top' && displayLoadingIndicator\n \"\n ></stream-loading-indicator>\n <ng-container *ngIf=\"messages$ | async as messages\">\n <ng-container\n *ngFor=\"\n let message of messages;\n let i = index;\n let isFirst = first;\n let isLast = last;\n trackBy: trackByMessageId\n \"\n >\n <ng-container *ngIf=\"isFirst\">\n <ng-container\n *ngTemplateOutlet=\"\n dateSeparator;\n context: {\n date: message.created_at,\n parsedDate: parseDate(message.created_at)\n }\n \"\n ></ng-container>\n </ng-container>\n <li\n tabindex=\"0\"\n data-testclass=\"message\"\n class=\"str-chat__li str-chat__li--{{ groupStyles[i] }}\"\n id=\"{{ message.id }}\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n messageTemplateContainer;\n context: { message: message, index: i }\n \"\n ></ng-container>\n </li>\n <ng-container\n *ngIf=\"\n (lastReadMessageId === message?.id &&\n direction === 'bottom-to-top') ||\n (direction === 'top-to-bottom' &&\n lastReadMessageId === messages[i + 1]?.id)\n \"\n >\n <li\n *ngIf=\"displayUnreadSeparator\"\n id=\"stream-chat-new-message-indicator\"\n data-testid=\"new-messages-indicator\"\n class=\"str-chat__li str-chat__unread-messages-separator-wrapper\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customnewMessagesIndicatorTemplate ||\n defaultNewMessagesIndicator;\n context: { unreadCount: unreadCount }\n \"\n ></ng-container>\n </li>\n </ng-container>\n <ng-container *ngIf=\"isNextMessageOnSeparateDate[i]\">\n <ng-container\n *ngTemplateOutlet=\"\n dateSeparator;\n context: {\n date: messages[i + 1].created_at,\n parsedDate: parseDate(messages[i + 1].created_at)\n }\n \"\n ></ng-container>\n </ng-container>\n </ng-container>\n </ng-container>\n <stream-loading-indicator\n data-testid=\"bottom-loading-indicator\"\n *ngIf=\"\n isLoading && direction === 'top-to-bottom' && displayLoadingIndicator\n \"\n ></stream-loading-indicator>\n </ul>\n <ng-template #defaultTypingIndicator let-usersTyping$=\"usersTyping$\">\n <!-- eslint-disable-next-line @angular-eslint/template/no-any -->\n <ng-container *ngIf=\"$any(usersTyping$ | async) as users\">\n <div\n *ngIf=\"users.length > 0\"\n data-testid=\"typing-indicator\"\n class=\"str-chat__typing-indicator str-chat__typing-indicator--typing\"\n >\n <div class=\"str-chat__typing-indicator__dots\">\n <span class=\"str-chat__typing-indicator__dot\"></span>\n <span class=\"str-chat__typing-indicator__dot\"></span>\n <span class=\"str-chat__typing-indicator__dot\"></span>\n </div>\n <div\n data-testid=\"typing-users\"\n class=\"str-chat__typing-indicator__users\"\n >\n {{\n users.length === 1\n ? (\"streamChat.user is typing\"\n | translate: { user: getTypingIndicatorText(users) })\n : (\"streamChat.users are typing\"\n | translate: { users: getTypingIndicatorText(users) })\n }}\n </div>\n </div>\n </ng-container>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n typingIndicatorTemplate || defaultTypingIndicator;\n context: getTypingIndicatorContext()\n \"\n ></ng-container>\n </div>\n</div>\n<div class=\"str-chat__jump-to-latest-message\">\n <button\n data-testid=\"scroll-to-latest\"\n *ngIf=\"isUserScrolled && isJumpToLatestButtonVisible\"\n class=\"\n str-chat__message-notification-scroll-to-latest\n str-chat__message-notification-scroll-to-latest-right\n str-chat__circle-fab\n \"\n (keyup.enter)=\"jumpToLatestMessage()\"\n (click)=\"jumpToLatestMessage()\"\n >\n <stream-icon\n class=\"str-chat__jump-to-latest-icon str-chat__circle-fab-icon\"\n [icon]=\"direction === 'bottom-to-top' ? 'arrow-down' : 'arrow-up'\"\n ></stream-icon>\n <div\n *ngIf=\"newMessageCountWhileBeingScrolled > 0\"\n class=\"\n str-chat__message-notification\n str-chat__message-notification-scroll-to-latest-unread-count\n str-chat__jump-to-latest-unread-count\n \"\n >\n {{ newMessageCountWhileBeingScrolled }}\n </div>\n </button>\n</div>\n\n<ng-template #messageTemplateContainer let-message=\"message\" let-index=\"index\">\n <ng-template\n #defaultMessageTemplate\n let-messageInput=\"message\"\n let-isLastSentMessage=\"isLastSentMessage\"\n let-enabledMessageActions=\"enabledMessageActions\"\n let-mode=\"mode\"\n let-isHighlighted=\"isHighlighted\"\n let-customActions=\"customActions\"\n >\n <stream-message\n [message]=\"messageInput\"\n [isLastSentMessage]=\"isLastSentMessage\"\n [enabledMessageActions]=\"enabledMessageActions\"\n [mode]=\"mode\"\n [isHighlighted]=\"isHighlighted\"\n [customActions]=\"customActions\"\n ></stream-message>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n messageTemplate || defaultMessageTemplate;\n context: {\n message: message,\n isLastSentMessage: !!(\n lastSentMessageId && message?.id === lastSentMessageId\n ),\n enabledMessageActions: enabledMessageActions,\n mode: mode,\n isHighlighted: message?.id === highlightedMessageId,\n customActions: customMessageActions\n }\n \"\n ></ng-container>\n</ng-template>\n\n<ng-template #dateSeparator let-date=\"date\" let-parsedDate=\"parsedDate\">\n <ng-container *ngIf=\"displayDateSeparator\">\n <ng-container\n *ngTemplateOutlet=\"\n customDateSeparatorTemplate || defaultDateSeparator;\n context: {\n date: date,\n parsedDate: parsedDate\n }\n \"\n ></ng-container>\n </ng-container>\n\n <ng-template\n #defaultDateSeparator\n let-date=\"date\"\n let-parsedDate=\"parsedDate\"\n >\n <div data-testid=\"date-separator\" class=\"str-chat__date-separator\">\n <hr\n *ngIf=\"\n dateSeparatorTextPos === 'right' || dateSeparatorTextPos === 'center'\n \"\n class=\"str-chat__date-separator-line\"\n />\n <div class=\"str-chat__date-separator-date\">\n {{ parsedDate }}\n </div>\n <hr\n *ngIf=\"\n dateSeparatorTextPos === 'left' || dateSeparatorTextPos === 'center'\n \"\n class=\"str-chat__date-separator-line\"\n />\n </div>\n </ng-template>\n</ng-template>\n\n<ng-template #defaultNewMessagesIndicator let-unreadCount=\"unreadCount\">\n <div class=\"str-chat__unread-messages-separator\">\n <ng-container\n *ngIf=\"\n unreadCount > 0 && !hideUnreadCountForNotificationAndIndicator;\n else noUnreadCount\n \"\n >\n {{\n (unreadCount === 1\n ? \"streamChat.\\{\\{count\\}\\} unread message\"\n : \"streamChat.\\{\\{count\\}\\} unread messages\"\n ) | translate: { count: unreadCount }\n }}\n </ng-container>\n <ng-template #noUnreadCount>\n {{ \"streamChat.Unread messages\" | translate }}\n </ng-template>\n </div>\n</ng-template>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: LoadingIndicatorComponent, selector: "stream-loading-indicator", inputs: ["size", "color"] }, { type: IconComponent, selector: "stream-icon", inputs: ["icon", "size"] }, { type: MessageComponent, selector: "stream-message", inputs: ["message", "enabledMessageActions", "isLastSentMessage", "mode", "isHighlighted", "customActions"] }], directives: [{ type: i8.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i8.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i8.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "translate": i9.TranslatePipe, "async": i8.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
7042
7142
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageListComponent, decorators: [{
7043
7143
  type: Component,
7044
7144
  args: [{
@@ -7069,6 +7169,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
7069
7169
  type: Input
7070
7170
  }], displayLoadingIndicator: [{
7071
7171
  type: Input
7172
+ }], limitNumberOfMessagesInList: [{
7173
+ type: Input
7072
7174
  }], scrollContainer: [{
7073
7175
  type: ViewChild,
7074
7176
  args: ['scrollContainer']