stream-chat-angular 4.42.1 → 4.44.0

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 { map, shareReplay, take, first, filter, tap, distinctUntilChanged, debounceTime } from 'rxjs/operators';
6
+ import { shareReplay, map, take, first, filter, tap, distinctUntilChanged, debounceTime } 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 from 'angular-mentions';
21
21
  import { MentionModule } from 'angular-mentions';
22
22
 
23
- const version = '4.42.1';
23
+ const version = '4.44.0';
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.
@@ -337,19 +337,24 @@ class ChannelService {
337
337
  this.parentMessageSetter = (message) => {
338
338
  this.activeParentMessageIdSubject.next(message === null || message === void 0 ? void 0 : message.id);
339
339
  };
340
- this.channels$ = this.channelsSubject.asObservable();
341
- this.activeChannel$ = this.activeChannelSubject.asObservable();
340
+ this.channels$ = this.channelsSubject.asObservable().pipe(shareReplay(1));
341
+ this.activeChannel$ = this.activeChannelSubject
342
+ .asObservable()
343
+ .pipe(shareReplay(1));
342
344
  this.activeChannelMessages$ = this.activeChannelMessagesSubject.pipe(map((messages) => {
343
345
  const channel = this.activeChannelSubject.getValue();
344
346
  return messages.map((message) => this.transformToStreamMessage(message, channel));
345
- }));
346
- this.hasMoreChannels$ = this.hasMoreChannelsSubject.asObservable();
347
- this.activeParentMessageId$ =
348
- this.activeParentMessageIdSubject.asObservable();
347
+ }), shareReplay(1));
348
+ this.hasMoreChannels$ = this.hasMoreChannelsSubject
349
+ .asObservable()
350
+ .pipe(shareReplay(1));
351
+ this.activeParentMessageId$ = this.activeParentMessageIdSubject
352
+ .asObservable()
353
+ .pipe(shareReplay(1));
349
354
  this.activeThreadMessages$ = this.activeThreadMessagesSubject.pipe(map((messages) => {
350
355
  const channel = this.activeChannelSubject.getValue();
351
356
  return messages.map((message) => this.transformToStreamMessage(message, channel));
352
- }));
357
+ }), shareReplay(1));
353
358
  this.activeParentMessage$ = combineLatest([
354
359
  this.activeChannelMessages$,
355
360
  this.activeParentMessageId$,
@@ -367,17 +372,29 @@ class ChannelService {
367
372
  return message;
368
373
  }
369
374
  }
370
- }), shareReplay());
371
- this.messageToQuote$ = this.messageToQuoteSubject.asObservable();
372
- this.jumpToMessage$ = this.jumpToMessageSubject.asObservable();
373
- this.usersTypingInChannel$ =
374
- this.usersTypingInChannelSubject.asObservable();
375
- this.usersTypingInThread$ = this.usersTypingInThreadSubject.asObservable();
375
+ }), shareReplay(1));
376
+ this.messageToQuote$ = this.messageToQuoteSubject
377
+ .asObservable()
378
+ .pipe(shareReplay(1));
379
+ this.jumpToMessage$ = this.jumpToMessageSubject
380
+ .asObservable()
381
+ .pipe(shareReplay(1));
382
+ this.usersTypingInChannel$ = this.usersTypingInChannelSubject
383
+ .asObservable()
384
+ .pipe(shareReplay(1));
385
+ this.usersTypingInThread$ = this.usersTypingInThreadSubject
386
+ .asObservable()
387
+ .pipe(shareReplay(1));
376
388
  this.latestMessageDateByUserByChannels$ =
377
- this.latestMessageDateByUserByChannelsSubject.asObservable();
378
- this.activeChannelPinnedMessages$ =
379
- this.activeChannelPinnedMessagesSubject.asObservable();
380
- this.channelQueryState$ = this.channelQueryStateSubject.asObservable();
389
+ this.latestMessageDateByUserByChannelsSubject
390
+ .asObservable()
391
+ .pipe(shareReplay(1));
392
+ this.activeChannelPinnedMessages$ = this.activeChannelPinnedMessagesSubject
393
+ .asObservable()
394
+ .pipe(shareReplay(1));
395
+ this.channelQueryState$ = this.channelQueryStateSubject
396
+ .asObservable()
397
+ .pipe(shareReplay(1));
381
398
  }
382
399
  /**
383
400
  * 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.
@@ -481,30 +498,31 @@ class ChannelService {
481
498
  * @param direction
482
499
  */
483
500
  loadMoreMessages(direction = 'older') {
484
- var _a, _b, _c, _d, _e, _f, _g;
485
- return __awaiter(this, void 0, void 0, function* () {
486
- const activeChnannel = this.activeChannelSubject.getValue();
487
- const messages = this.activeChannelMessagesSubject.getValue();
488
- const lastMessageId = (_a = messages[direction === 'older' ? 0 : messages.length - 1]) === null || _a === void 0 ? void 0 : _a.id;
489
- if (direction === 'newer' &&
490
- ((_b = activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.state) === null || _b === void 0 ? void 0 : _b.latestMessages) === ((_c = activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.state) === null || _c === void 0 ? void 0 : _c.messages)) {
491
- // If we are on latest message set, activeChannelMessages$ will be refreshed by WS events, no need for a request
492
- return;
493
- }
494
- yield (activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.query({
495
- messages: {
496
- limit: (_d = this.options) === null || _d === void 0 ? void 0 : _d.message_limit,
497
- [direction === 'older' ? 'id_lt' : 'id_gt']: lastMessageId,
498
- },
499
- members: { limit: 0 },
500
- watchers: { limit: 0 },
501
- }));
502
- if (((_e = activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.data) === null || _e === void 0 ? void 0 : _e.id) ===
503
- ((_g = (_f = this.activeChannelSubject.getValue()) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.id)) {
501
+ var _a, _b, _c, _d;
502
+ const activeChnannel = this.activeChannelSubject.getValue();
503
+ const messages = this.activeChannelMessagesSubject.getValue();
504
+ const lastMessageId = (_a = messages[direction === 'older' ? 0 : messages.length - 1]) === null || _a === void 0 ? void 0 : _a.id;
505
+ if (direction === 'newer' &&
506
+ ((_b = activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.state) === null || _b === void 0 ? void 0 : _b.latestMessages) === ((_c = activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.state) === null || _c === void 0 ? void 0 : _c.messages)) {
507
+ // If we are on latest message set, activeChannelMessages$ will be refreshed by WS events, no need for a request
508
+ return false;
509
+ }
510
+ return activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.query({
511
+ messages: {
512
+ limit: (_d = this.options) === null || _d === void 0 ? void 0 : _d.message_limit,
513
+ [direction === 'older' ? 'id_lt' : 'id_gt']: lastMessageId,
514
+ },
515
+ members: { limit: 0 },
516
+ watchers: { limit: 0 },
517
+ }).then((res) => {
518
+ var _a, _b, _c;
519
+ if (((_a = activeChnannel === null || activeChnannel === void 0 ? void 0 : activeChnannel.data) === null || _a === void 0 ? void 0 : _a.id) ===
520
+ ((_c = (_b = this.activeChannelSubject.getValue()) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.id)) {
504
521
  this.activeChannelMessagesSubject.next([
505
522
  ...activeChnannel.state.messages,
506
523
  ]);
507
524
  }
525
+ return res;
508
526
  });
509
527
  }
510
528
  /**
@@ -618,11 +636,22 @@ class ChannelService {
618
636
  */
619
637
  sendMessage(text, attachments = [], mentionedUsers = [], parentId = undefined, quotedMessageId = undefined, customData = undefined) {
620
638
  return __awaiter(this, void 0, void 0, function* () {
621
- const preview = createMessagePreview(this.chatClientService.chatClient.user, text, attachments, mentionedUsers, parentId, quotedMessageId, customData);
639
+ let input = {
640
+ text,
641
+ attachments,
642
+ mentionedUsers,
643
+ parentId,
644
+ quotedMessageId,
645
+ customData,
646
+ };
647
+ if (this.beforeSendMessage) {
648
+ input = yield this.beforeSendMessage(input);
649
+ }
650
+ const preview = createMessagePreview(this.chatClientService.chatClient.user, input.text, input.attachments, input.mentionedUsers, input.parentId, input.quotedMessageId, input.customData);
622
651
  const channel = this.activeChannelSubject.getValue();
623
652
  preview.readBy = [];
624
653
  channel.state.addMessageSorted(preview, true);
625
- const response = yield this.sendMessageRequest(preview, customData);
654
+ const response = yield this.sendMessageRequest(preview, input.customData);
626
655
  return response;
627
656
  });
628
657
  }
@@ -644,8 +673,11 @@ class ChannelService {
644
673
  updateMessage(message) {
645
674
  var _a;
646
675
  return __awaiter(this, void 0, void 0, function* () {
647
- const messageToUpdate = Object.assign({}, message);
676
+ let messageToUpdate = Object.assign({}, message);
648
677
  delete messageToUpdate.i18n;
678
+ if (this.beforeUpdateMessage) {
679
+ messageToUpdate = yield this.beforeUpdateMessage(messageToUpdate);
680
+ }
649
681
  const response = yield this.chatClientService.chatClient.updateMessage(messageToUpdate);
650
682
  const channel = (_a = this.channelsSubject
651
683
  .getValue()) === null || _a === void 0 ? void 0 : _a.find((c) => c.cid === message.cid);
@@ -4404,12 +4436,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
4404
4436
  * The `Message` component displays a message with additional information such as sender and date, and enables [interaction with the message (i.e. edit or react)](../concepts/message-interactions.mdx).
4405
4437
  */
4406
4438
  class MessageComponent {
4407
- constructor(chatClientService, channelService, customTemplatesService, cdRef, themeService, dateParser) {
4439
+ constructor(chatClientService, channelService, customTemplatesService, cdRef, themeService, dateParser, ngZone) {
4408
4440
  this.chatClientService = chatClientService;
4409
4441
  this.channelService = channelService;
4410
4442
  this.customTemplatesService = customTemplatesService;
4411
4443
  this.cdRef = cdRef;
4412
4444
  this.dateParser = dateParser;
4445
+ this.ngZone = ngZone;
4413
4446
  /**
4414
4447
  * The list of [channel capabilities](https://getstream.io/chat/docs/javascript/channel_capabilities/?language=javascript) that are enabled for the current user, the list of [supported interactions](../concepts/message-interactions.mdx) can be found in our message interaction guide. Unathorized actions won't be displayed on the UI. The [`MessageList`](./MessageListComponent.mdx) component automatically sets this based on [channel capabilities](https://getstream.io/chat/docs/javascript/channel_capabilities/?language=javascript).
4415
4448
  */
@@ -4452,6 +4485,8 @@ class MessageComponent {
4452
4485
  };
4453
4486
  this.canDisplayReadStatus = false;
4454
4487
  this.subscriptions = [];
4488
+ this.urlRegexp = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/gim;
4489
+ this.emojiRegexp = new RegExp(emojiRegex(), 'g');
4455
4490
  this.themeVersion = themeService.themeVersion;
4456
4491
  }
4457
4492
  ngOnInit() {
@@ -4529,6 +4564,12 @@ class MessageComponent {
4529
4564
  : false;
4530
4565
  }
4531
4566
  }
4567
+ ngAfterViewInit() {
4568
+ this.ngZone.runOutsideAngular(() => {
4569
+ var _a;
4570
+ (_a = this.container) === null || _a === void 0 ? void 0 : _a.nativeElement.addEventListener('mouseleave', () => this.mouseLeft());
4571
+ });
4572
+ }
4532
4573
  ngOnDestroy() {
4533
4574
  this.subscriptions.forEach((s) => s.unsubscribe());
4534
4575
  }
@@ -4631,66 +4672,65 @@ class MessageComponent {
4631
4672
  displayOriginalMessage() {
4632
4673
  this.createMessageParts(false);
4633
4674
  }
4675
+ mouseLeft() {
4676
+ if (this.isActionBoxOpen) {
4677
+ this.ngZone.run(() => {
4678
+ this.isActionBoxOpen = false;
4679
+ });
4680
+ }
4681
+ }
4634
4682
  createMessageParts(shouldTranslate = true) {
4683
+ this.messageTextParts = undefined;
4684
+ this.messageText = undefined;
4635
4685
  let content = this.getMessageContent(shouldTranslate);
4686
+ if ((!this.message.mentioned_users ||
4687
+ this.message.mentioned_users.length === 0) &&
4688
+ !(content === null || content === void 0 ? void 0 : content.match(this.emojiRegexp)) &&
4689
+ !(content === null || content === void 0 ? void 0 : content.match(this.urlRegexp))) {
4690
+ this.messageTextParts = undefined;
4691
+ this.messageText = content;
4692
+ return;
4693
+ }
4636
4694
  if (!content) {
4637
- this.messageTextParts = [];
4695
+ return;
4696
+ }
4697
+ if (!this.message.mentioned_users ||
4698
+ this.message.mentioned_users.length === 0) {
4699
+ content = this.fixEmojiDisplay(content);
4700
+ content = this.wrapLinskWithAnchorTag(content);
4701
+ this.messageTextParts = [{ content, type: 'text' }];
4638
4702
  }
4639
4703
  else {
4640
- let isHTML = false;
4641
- // Backend will wrap HTML content with <p></p>\n
4642
- if (content.startsWith('<p>')) {
4643
- content = content.replace('<p>', '');
4644
- isHTML = true;
4645
- }
4646
- if (content.endsWith('</p>\n')) {
4647
- content = content.replace('</p>\n', '');
4648
- isHTML = true;
4649
- }
4650
- if (!this.message.mentioned_users ||
4651
- this.message.mentioned_users.length === 0) {
4652
- content = this.fixEmojiDisplay(content);
4653
- if (!isHTML) {
4654
- content = this.wrapLinskWithAnchorTag(content);
4655
- }
4656
- this.messageTextParts = [{ content, type: 'text' }];
4657
- }
4658
- else {
4659
- this.messageTextParts = [];
4660
- let text = content;
4661
- this.message.mentioned_users.forEach((user) => {
4662
- const mention = `@${user.name || user.id}`;
4663
- let precedingText = text.substring(0, text.indexOf(mention));
4664
- precedingText = this.fixEmojiDisplay(precedingText);
4665
- if (!isHTML) {
4666
- precedingText = this.wrapLinskWithAnchorTag(precedingText);
4667
- }
4668
- this.messageTextParts.push({
4669
- content: precedingText,
4670
- type: 'text',
4671
- });
4672
- this.messageTextParts.push({
4673
- content: mention,
4674
- type: 'mention',
4675
- user,
4676
- });
4677
- text = text.replace(precedingText + mention, '');
4704
+ this.messageTextParts = [];
4705
+ let text = content;
4706
+ this.message.mentioned_users.forEach((user) => {
4707
+ const mention = `@${user.name || user.id}`;
4708
+ const precedingText = text.substring(0, text.indexOf(mention));
4709
+ let formattedPrecedingText = this.fixEmojiDisplay(precedingText);
4710
+ formattedPrecedingText = this.wrapLinskWithAnchorTag(formattedPrecedingText);
4711
+ this.messageTextParts.push({
4712
+ content: formattedPrecedingText,
4713
+ type: 'text',
4678
4714
  });
4679
- if (text) {
4680
- text = this.fixEmojiDisplay(text);
4681
- if (!isHTML) {
4682
- text = this.wrapLinskWithAnchorTag(text);
4683
- }
4684
- this.messageTextParts.push({ content: text, type: 'text' });
4685
- }
4715
+ this.messageTextParts.push({
4716
+ content: mention,
4717
+ type: 'mention',
4718
+ user,
4719
+ });
4720
+ text = text.replace(precedingText + mention, '');
4721
+ });
4722
+ if (text) {
4723
+ text = this.fixEmojiDisplay(text);
4724
+ text = this.wrapLinskWithAnchorTag(text);
4725
+ this.messageTextParts.push({ content: text, type: 'text' });
4686
4726
  }
4687
4727
  }
4688
4728
  }
4689
4729
  getMessageContent(shouldTranslate) {
4690
- var _a, _b, _c;
4691
- const originalContent = ((_a = this.message) === null || _a === void 0 ? void 0 : _a.html) || ((_b = this.message) === null || _b === void 0 ? void 0 : _b.text);
4730
+ var _a, _b;
4731
+ const originalContent = (_a = this.message) === null || _a === void 0 ? void 0 : _a.text;
4692
4732
  if (shouldTranslate) {
4693
- const translation = (_c = this.message) === null || _c === void 0 ? void 0 : _c.translation;
4733
+ const translation = (_b = this.message) === null || _b === void 0 ? void 0 : _b.translation;
4694
4734
  if (translation) {
4695
4735
  this.shouldDisplayTranslationNotice = true;
4696
4736
  this.displayedMessageTextContent = 'translation';
@@ -4704,17 +4744,15 @@ class MessageComponent {
4704
4744
  }
4705
4745
  fixEmojiDisplay(content) {
4706
4746
  // Wrap emojis in span to display emojis correctly in Chrome https://bugs.chromium.org/p/chromium/issues/detail?id=596223
4707
- const regex = new RegExp(emojiRegex(), 'g');
4708
4747
  // Based on this: https://stackoverflow.com/questions/4565112/javascript-how-to-find-out-if-the-user-browser-is-chrome
4709
4748
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4710
4749
  const isChrome = !!window.chrome && typeof window.opr === 'undefined';
4711
4750
  /* eslint-enable @typescript-eslint/no-unsafe-member-access */
4712
- content = content.replace(regex, (match) => `<span ${isChrome ? 'class="str-chat__emoji-display-fix"' : ''}>${match}</span>`);
4751
+ content = content.replace(this.emojiRegexp, (match) => `<span ${isChrome ? 'class="str-chat__emoji-display-fix"' : ''}>${match}</span>`);
4713
4752
  return content;
4714
4753
  }
4715
4754
  wrapLinskWithAnchorTag(content) {
4716
- const urlRegexp = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#/%=~_|$?!:,.]*\)|[A-Z0-9+&@#/%=~_|$])/gim;
4717
- content = content.replace(urlRegexp, (match) => `<a href="${match}" rel="nofollow">${match}</a>`);
4755
+ content = content.replace(this.urlRegexp, (match) => `<a href="${match}" rel="nofollow">${match}</a>`);
4718
4756
  return content;
4719
4757
  }
4720
4758
  setIsSentByCurrentUser() {
@@ -4726,8 +4764,8 @@ class MessageComponent {
4726
4764
  this.lastReadUser = (_b = (_a = this.message) === null || _a === void 0 ? void 0 : _a.readBy) === null || _b === void 0 ? void 0 : _b.filter((u) => { var _a; return u.id !== ((_a = this.user) === null || _a === void 0 ? void 0 : _a.id); })[0];
4727
4765
  }
4728
4766
  }
4729
- MessageComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageComponent, deps: [{ token: ChatClientService }, { token: ChannelService }, { token: CustomTemplatesService }, { token: i0.ChangeDetectorRef }, { token: ThemeService }, { token: DateParserService }], target: i0.ɵɵFactoryTarget.Component });
4730
- MessageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageComponent, selector: "stream-message", inputs: { message: "message", enabledMessageActions: "enabledMessageActions", isLastSentMessage: "isLastSentMessage", mode: "mode", isHighlighted: "isHighlighted", customActions: "customActions" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n #container\n class=\"str-chat__message-simple str-chat__message str-chat__message--{{\n message?.type\n }} str-chat__message--{{ message?.status }} {{\n message?.text ? 'str-chat__message--has-text' : 'has-no-text'\n }}\"\n [class.str-chat__message--me]=\"isSentByCurrentUser\"\n [class.str-chat__message--other]=\"!isSentByCurrentUser\"\n [class.str-chat__message-simple--me]=\"isSentByCurrentUser\"\n [class.str-chat__message--has-attachment]=\"hasAttachment\"\n [class.str-chat__message--with-reactions]=\"hasReactions\"\n [class.str-chat__message--highlighted]=\"isHighlighted\"\n [class.str-chat__message-with-thread-link]=\"shouldDisplayThreadLink\"\n [class.str-chat__message-send-can-be-retried]=\"\n message?.status === 'failed' && message?.errorStatusCode !== 403\n \"\n data-testid=\"message-container\"\n (mouseleave)=\"isActionBoxOpen = false\"\n>\n <ng-container *ngIf=\"!message?.deleted_at; else deletedMessage\">\n <ng-container *ngIf=\"message?.type !== 'system'; else systemMessage\">\n <ng-container *ngIf=\"themeVersion === '1'\">\n <ng-container *ngTemplateOutlet=\"messageStatus\"></ng-container>\n </ng-container>\n <stream-avatar-placeholder\n data-testid=\"avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n [imageUrl]=\"message?.user?.image\"\n [name]=\"message?.user?.name || message?.user?.id\"\n type=\"user\"\n location=\"message-sender\"\n [user]=\"message?.user || undefined\"\n ></stream-avatar-placeholder>\n <div class=\"str-chat__message-inner\">\n <div\n class=\"str-chat__message-simple__actions str-chat__message-options\"\n data-testid=\"message-options\"\n [class.str-chat__message-edit-in-progress]=\"isEditing\"\n *ngIf=\"areOptionsVisible\"\n >\n <div\n data-testid=\"message-actions-container\"\n #messageActionsToggle\n class=\"\n str-chat__message-actions-container\n str-chat__message-simple__actions__action\n str-chat__message-simple__actions__action--options\n \"\n [class.str-chat-angular__message-simple__actions__action--options--editing]=\"\n isEditing\n \"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerClick\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"true\"\n [popperHideOnMouseLeave]=\"false\"\n [popperDisableAnimation]=\"true\"\n >\n <popper-content #popperContent>\n <ng-template\n #defaultMessageActionsBox\n let-isOpen=\"isOpen\"\n let-isMine=\"isMine\"\n let-enabledActions=\"enabledActions\"\n let-messageInput=\"message\"\n let-displayedActionsCountChangeHandler=\"displayedActionsCountChangeHandler\"\n let-isEditingChangeHandler=\"isEditingChangeHandler\"\n let-customActions=\"customActions\"\n >\n <stream-message-actions-box\n [isOpen]=\"isOpen\"\n [isMine]=\"isMine\"\n [enabledActions]=\"enabledActions\"\n [message]=\"messageInput\"\n [customActions]=\"customActions\"\n (displayedActionsCount)=\"\n displayedActionsCountChangeHandler($event)\n \"\n (isEditing)=\"isEditingChangeHandler($event)\"\n ></stream-message-actions-box>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageActionsBoxTemplate$ | async) ||\n defaultMessageActionsBox;\n context: getMessageActionsBoxContext()\n \"\n >\n </ng-container>\n </popper-content>\n <div\n class=\"str-chat__message-actions-box-button\"\n *ngIf=\"visibleMessageActionsCount > 0\"\n data-testid=\"action-icon\"\n (click)=\"isActionBoxOpen = !isActionBoxOpen\"\n (keyup.enter)=\"isActionBoxOpen = !isActionBoxOpen\"\n >\n <stream-icon-placeholder\n icon=\"action-icon\"\n class=\"str-chat__message-action-icon\"\n ></stream-icon-placeholder>\n </div>\n </div>\n <div\n *ngIf=\"\n enabledMessageActions.indexOf('send-reply') !== -1 &&\n mode === 'main'\n \"\n class=\"\n str-chat__message-simple__actions__action\n str-chat__message-simple__actions__action--thread\n str-chat__message-reply-in-thread-button\n \"\n data-testid=\"reply-in-thread\"\n (click)=\"setAsActiveParentMessage()\"\n (keyup.enter)=\"setAsActiveParentMessage()\"\n >\n <stream-icon-placeholder\n class=\"str-chat__message-action-icon\"\n icon=\"reply-in-thread\"\n ></stream-icon-placeholder>\n </div>\n <div\n *ngIf=\"canReactToMessage\"\n class=\"\n str-chat__message-simple__actions__action\n str-chat__message-simple__actions__action--reactions\n str-chat__message-reactions-button\n \"\n data-testid=\"reaction-icon\"\n (click)=\"isReactionSelectorOpen = !isReactionSelectorOpen\"\n (keyup.enter)=\"isReactionSelectorOpen = !isReactionSelectorOpen\"\n >\n <stream-icon-placeholder\n class=\"str-chat__message-action-icon\"\n icon=\"reaction-icon\"\n ></stream-icon-placeholder>\n </div>\n </div>\n <div class=\"str-chat__message-reactions-host\">\n <ng-template\n #defaultMessageReactions\n let-messageReactionCounts=\"messageReactionCounts\"\n let-latestReactions=\"latestReactions\"\n let-isSelectorOpen=\"isSelectorOpen\"\n let-isSelectorOpenChangeHandler=\"isSelectorOpenChangeHandler\"\n let-messageId=\"messageId\"\n let-ownReactions=\"ownReactions\"\n >\n <stream-message-reactions\n [messageReactionCounts]=\"messageReactionCounts\"\n [latestReactions]=\"latestReactions\"\n [isSelectorOpen]=\"isSelectorOpen\"\n (isSelectorOpenChange)=\"isSelectorOpenChangeHandler($event)\"\n [messageId]=\"messageId\"\n [ownReactions]=\"ownReactions\"\n ></stream-message-reactions>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageReactionsTemplate$ | async) ||\n defaultMessageReactions;\n context: getMessageReactionsContext()\n \"\n ></ng-container>\n </div>\n <!-- transform: translate3d(0, 0, 0) fixes scrolling issues on iOS, see: https://stackoverflow.com/questions/50105780/elements-disappear-when-scrolling-in-safari-webkit-transform-fix-only-works-t/50144295#50144295 -->\n <!-- transform: none is required when image carousel is open in order for the modal to be correctly positioned, see how the transform property changes the behavior of fixed positioned elements https://developer.mozilla.org/en-US/docs/Web/CSS/position -->\n <div\n class=\"str-chat__message-bubble\"\n style=\"transform: {{\n imageAttachmentModalState === 'opened'\n ? 'none'\n : 'translate3d(0, 0, 0)'\n }}\"\n >\n <ng-container *ngIf=\"hasAttachment && !message?.quoted_message\">\n <ng-container\n *ngTemplateOutlet=\"attachmentsTemplate\"\n ></ng-container>\n </ng-container>\n <div\n class=\"str-chat__message-text\"\n tabindex=\"0\"\n *ngIf=\"message?.text || (message?.quoted_message && hasAttachment)\"\n >\n <div\n data-testid=\"inner-message\"\n class=\"\n str-chat__message-text-inner str-chat__message-simple-text-inner\n \"\n [class.str-chat__message-light-text-inner--has-attachment]=\"\n hasAttachment\n \"\n (click)=\"\n message?.status === 'failed' && message?.errorStatusCode !== 403\n ? resendMessage()\n : undefined\n \"\n (keyup.enter)=\"\n message?.status === 'failed' && message?.errorStatusCode !== 403\n ? resendMessage()\n : undefined\n \"\n >\n <ng-container *ngTemplateOutlet=\"quotedMessage\"></ng-container>\n <ng-container *ngIf=\"hasAttachment && message?.quoted_message\">\n <ng-container\n *ngTemplateOutlet=\"attachmentsTemplate\"\n ></ng-container>\n </ng-container>\n <div\n data-testid=\"client-error-message\"\n *ngIf=\"message?.type === 'error'\"\n class=\"\n str-chat__simple-message--error-message\n str-chat__message--error-message\n \"\n >\n {{ \"streamChat.Error \u00B7 Unsent\" | translate }}\n </div>\n <div\n data-testid=\"error-message\"\n *ngIf=\"message?.status === 'failed'\"\n class=\"\n str-chat__simple-message--error-message\n str-chat__message--error-message\n \"\n >\n {{\n (message?.errorStatusCode === 403\n ? \"streamChat.Message Failed \u00B7 Unauthorized\"\n : \"streamChat.Message Failed \u00B7 Click to try again\"\n ) | translate\n }}\n </div>\n <div data-testid=\"text\">\n <p>\n <!-- eslint-disable-next-line @angular-eslint/template/use-track-by-function -->\n <ng-container *ngFor=\"let part of messageTextParts\">\n <span\n *ngIf=\"part.type === 'text'; else mention\"\n [innerHTML]=\"part.content\"\n ></span>\n <ng-template #mention>\n <ng-template #defaultMention let-content=\"content\">\n <span class=\"str-chat__message-mention\">{{\n content\n }}</span>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.mentionTemplate$ | async) ||\n defaultMention;\n context: getMentionContext(part)\n \"\n ></ng-container>\n </ng-template>\n </ng-container>\n </p>\n </div>\n </div>\n </div>\n <stream-icon-placeholder\n *ngIf=\"themeVersion === '2'\"\n class=\"str-chat__message-error-icon\"\n icon=\"error\"\n ></stream-icon-placeholder>\n </div>\n <ng-container\n *ngTemplateOutlet=\"\n replyCountButton;\n context: { position: 'inside-message-bubble' }\n \"\n ></ng-container>\n <ng-container\n *ngTemplateOutlet=\"\n messageDateAndSender;\n context: { position: 'inside-message-bubble' }\n \"\n ></ng-container>\n </div>\n <ng-container\n *ngTemplateOutlet=\"\n replyCountButton;\n context: { position: 'outside-message-bubble', message: message }\n \"\n ></ng-container>\n\n <ng-container\n *ngTemplateOutlet=\"\n messageDateAndSender;\n context: { position: 'outside-message-bubble' }\n \"\n ></ng-container>\n </ng-container>\n </ng-container>\n</div>\n\n<ng-template #deletedMessage>\n <div data-testid=\"message-deleted-component\">\n <div class=\"str-chat__message--deleted-inner\" translate>\n streamChat.This message was deleted...\n </div>\n </div>\n</ng-template>\n\n<ng-template #systemMessage>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.systemMessageTemplate$ | async) ||\n defaultSystemMessage;\n context: getMessageContext()\n \"\n ></ng-container>\n <ng-template #defaultSystemMessage let-messageInput=\"message\">\n <div data-testid=\"system-message\" class=\"str-chat__message--system\">\n <div class=\"str-chat__message--system__text\">\n <div class=\"str-chat__message--system__line\"></div>\n <p>{{ messageInput?.text }}</p>\n <div class=\"str-chat__message--system__line\"></div>\n </div>\n <div class=\"str-chat__message--system__date\">\n {{ parsedDate }}\n </div>\n </div>\n </ng-template>\n</ng-template>\n\n<ng-template #quotedMessage>\n <div\n *ngIf=\"message?.quoted_message\"\n class=\"quoted-message str-chat__quoted-message-preview\"\n data-testid=\"quoted-message-container\"\n [class.mine]=\"isSentByCurrentUser\"\n (click)=\"\n jumpToMessage(\n (message?.quoted_message)!.id,\n message?.quoted_message?.parent_id\n )\n \"\n (keyup.enter)=\"\n jumpToMessage(\n (message?.quoted_message)!.id,\n message?.quoted_message?.parent_id\n )\n \"\n >\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n [imageUrl]=\"message?.quoted_message?.user?.image\"\n [name]=\"\n message?.quoted_message?.user?.name || message?.quoted_message?.user?.id\n \"\n [size]=\"20\"\n type=\"user\"\n location=\"quoted-message-sender\"\n [user]=\"message?.quoted_message?.user || undefined\"\n ></stream-avatar-placeholder>\n <div class=\"quoted-message-inner str-chat__quoted-message-bubble\">\n <ng-container\n *ngIf=\"\n message?.quoted_message?.attachments &&\n message?.quoted_message?.attachments?.length\n \"\n >\n <ng-template\n #defaultAttachments\n let-messageId=\"messageId\"\n let-attachments=\"attachments\"\n let-parentMessageId=\"parentMessageId\"\n let-imageModalStateChangeHandler=\"imageModalStateChangeHandler\"\n >\n <stream-attachment-list\n [messageId]=\"messageId\"\n [attachments]=\"attachments\"\n [parentMessageId]=\"parentMessageId\"\n (imageModalStateChange)=\"imageModalStateChangeHandler($event)\"\n ></stream-attachment-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.attachmentListTemplate$ | async) ||\n defaultAttachments;\n context: getQuotedMessageAttachmentListContext()\n \"\n ></ng-container>\n </ng-container>\n <div\n data-testid=\"quoted-message-text\"\n [innerHTML]=\"\n message?.quoted_message?.translation ||\n message?.quoted_message?.html ||\n message?.quoted_message?.text\n \"\n ></div>\n </div>\n </div>\n</ng-template>\n\n<!-- We need these markups in slightly different positions in theme-v1 and theme-v2, this soultion makes that possible without duplicating the code -->\n<ng-template #messageDateAndSender let-position=\"position\">\n <ng-container\n *ngIf=\"\n (position === 'inside-message-bubble' && themeVersion === '1') ||\n (position === 'outside-message-bubble' && themeVersion === '2')\n \"\n >\n <div\n class=\"str-chat__translation-notice\"\n *ngIf=\"shouldDisplayTranslationNotice\"\n data-testid=\"translation-notice\"\n >\n <button\n data-testid=\"see-original\"\n *ngIf=\"displayedMessageTextContent === 'translation'\"\n (click)=\"displayOriginalMessage()\"\n (keyup.enter)=\"displayOriginalMessage()\"\n translate\n >\n streamChat.See original (automatically translated)\n </button>\n <button\n data-testid=\"see-translation\"\n *ngIf=\"displayedMessageTextContent === 'original'\"\n (click)=\"displayTranslatedMessage()\"\n (keyup.enter)=\"displayTranslatedMessage()\"\n translate\n >\n streamChat.See translation\n </button>\n </div>\n <div\n class=\"\n str-chat__message-data\n str-chat__message-simple-data\n str-chat__message-metadata\n \"\n >\n <ng-container *ngIf=\"themeVersion === '2'\">\n <ng-container *ngTemplateOutlet=\"messageStatus\"></ng-container>\n </ng-container>\n <span\n data-testid=\"sender\"\n *ngIf=\"!isSentByCurrentUser\"\n class=\"str-chat__message-simple-name str-chat__message-sender-name\"\n >\n {{ message?.user?.name ? message?.user?.name : message?.user?.id }}\n </span>\n <span data-testid=\"date\" class=\"str-chat__message-simple-timestamp\">\n {{ parsedDate }}\n </span>\n </div>\n </ng-container>\n</ng-template>\n\n<ng-template #messageStatus>\n <ng-container\n *ngIf=\"\n isSentByCurrentUser &&\n ((isLastSentMessage && message?.status === 'received') ||\n message?.status === 'sending')\n \"\n >\n <ng-container *ngIf=\"message?.status === 'sending'; else sentStatus\">\n <ng-container *ngTemplateOutlet=\"sendingStatus\"></ng-container>\n </ng-container>\n <ng-template #sentStatus>\n <ng-container\n *ngIf=\"\n mode === 'main' && isMessageDeliveredAndRead && canDisplayReadStatus;\n else deliveredStatus\n \"\n >\n <ng-container *ngTemplateOutlet=\"readStatus\"></ng-container>\n </ng-container>\n </ng-template>\n <ng-template #deliveredStatus>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.deliveredStatusTemplate$ | async) ||\n defaultDeliveredStatus;\n context: getDeliveredStatusContext()\n \"\n ></ng-container>\n </ng-template>\n <ng-template #defaultDeliveredStatus>\n <span\n *ngIf=\"mode === 'main'\"\n class=\"\n str-chat__message-simple-status\n str-chat__message-simple-status-angular\n str-chat__message-status\n \"\n data-testid=\"delivered-indicator\"\n tabindex=\"0\"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerHover\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"false\"\n (hover)=\"$event.stopPropagation()\"\n >\n <popper-content #popperContent>\n <div class=\"str-chat__tooltip str-chat__tooltip-angular\">\n {{ \"streamChat.Delivered\" | translate }}\n </div>\n </popper-content>\n <stream-icon-placeholder\n data-testid=\"delivered-icon\"\n icon=\"delivered-icon\"\n ></stream-icon-placeholder>\n </span>\n </ng-template>\n <ng-template #sendingStatus>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.sendingStatusTemplate$ | async) ||\n defaultSendingStatus;\n context: getSendingStatusContext()\n \"\n ></ng-container>\n </ng-template>\n <ng-template #defaultSendingStatus>\n <span\n class=\"\n str-chat__message-simple-status\n str-chat__message-simple-status-angular\n str-chat__message-status\n \"\n data-testid=\"sending-indicator\"\n tabindex=\"0\"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerHover\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"false\"\n (hover)=\"$event.stopPropagation()\"\n >\n <popper-content #popperContent>\n <div class=\"str-chat__tooltip str-chat__tooltip-angular\">\n {{ \"streamChat.Sending...\" | translate }}\n </div>\n </popper-content>\n <stream-loading-indicator-placeholder\n data-testid=\"loading-indicator\"\n ></stream-loading-indicator-placeholder>\n </span>\n </ng-template>\n <ng-template #readStatus>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.readStatusTemplate$ | async) ||\n defaultReadStatus;\n context: getReadStatusContext()\n \"\n ></ng-container>\n </ng-template>\n <ng-template #defaultReadStatus>\n <span\n class=\"\n str-chat__message-simple-status\n str-chat__message-simple-status-angular\n str-chat__message-status\n \"\n data-testid=\"read-indicator\"\n tabindex=\"0\"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerHover\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"false\"\n (hover)=\"$event.stopPropagation()\"\n >\n <popper-content #popperContent>\n <div\n class=\"str-chat__tooltip str-chat__tooltip-angular\"\n data-testid=\"read-by-tooltip\"\n >\n {{ readByText }}\n </div>\n </popper-content>\n <stream-avatar-placeholder\n class=\"str-chat-angular__avatar-host\"\n data-test-id=\"last-read-user-avatar\"\n [size]=\"15\"\n [imageUrl]=\"lastReadUser?.image\"\n [name]=\"lastReadUser?.name || lastReadUser?.id\"\n type=\"user\"\n location=\"message-reader\"\n [user]=\"lastReadUser\"\n ></stream-avatar-placeholder>\n <span\n data-test-id=\"read-by-length\"\n *ngIf=\"isReadByMultipleUsers\"\n class=\"str-chat__message-simple-status-number\"\n >\n {{ (message?.readBy)!.length }}\n </span>\n </span>\n </ng-template>\n </ng-container>\n</ng-template>\n\n<ng-template #replyCountButton let-position=\"position\">\n <div\n *ngIf=\"\n (position === 'inside-message-bubble' && themeVersion === '1') ||\n (position === 'outside-message-bubble' && themeVersion === '2')\n \"\n class=\"\n str-chat__message-simple-reply-button\n str-chat__message-replies-count-button-wrapper\n \"\n >\n <button\n *ngIf=\"shouldDisplayThreadLink\"\n class=\"str-chat__message-replies-count-button\"\n data-testid=\"reply-count-button\"\n (click)=\"setAsActiveParentMessage()\"\n >\n <stream-icon-placeholder\n *ngIf=\"themeVersion === '1'\"\n stream-icon-placeholder\n icon=\"reply\"\n ></stream-icon-placeholder>\n {{message?.reply_count === 1 ? ('streamChat.1 reply' | translate) : ('streamChat.{{ replyCount }}\n replies' | translate:replyCountParam)}}\n </button>\n </div>\n</ng-template>\n\n<ng-template #attachmentsTemplate>\n <ng-template\n #defaultAttachments\n let-messageId=\"messageId\"\n let-attachments=\"attachments\"\n let-parentMessageId=\"parentMessageId\"\n let-imageModalStateChangeHandler=\"imageModalStateChangeHandler\"\n >\n <stream-attachment-list\n [messageId]=\"messageId\"\n [attachments]=\"attachments\"\n [parentMessageId]=\"parentMessageId\"\n (imageModalStateChange)=\"imageModalStateChangeHandler($event)\"\n ></stream-attachment-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.attachmentListTemplate$ | async) ||\n defaultAttachments;\n context: getAttachmentListContext()\n \"\n ></ng-container>\n</ng-template>\n", components: [{ type: AvatarPlaceholderComponent, selector: "stream-avatar-placeholder", inputs: ["name", "imageUrl", "size", "location", "channel", "user", "type", "initialsType", "showOnlineIndicator"] }, { type: i3.NgxPopperjsContentComponent, selector: "popper-content", exportAs: ["ngxPopperjsContent"] }, { type: MessageActionsBoxComponent, selector: "stream-message-actions-box", inputs: ["isOpen", "isMine", "message", "enabledActions", "customActions"], outputs: ["displayedActionsCount", "isEditing"] }, { type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: MessageReactionsComponent, selector: "stream-message-reactions", inputs: ["messageId", "messageReactionCounts", "isSelectorOpen", "latestReactions", "ownReactions"], outputs: ["isSelectorOpenChange"] }, { type: AttachmentListComponent, selector: "stream-attachment-list", inputs: ["messageId", "parentMessageId", "attachments"], outputs: ["imageModalStateChange"] }, { type: LoadingIndicatorPlaceholderComponent, selector: "stream-loading-indicator-placeholder", inputs: ["size", "color"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i3.NgxPopperjsDirective, selector: "[popper]", inputs: ["popperTimeout", "popperTimeoutAfterShow", "popperApplyClass", "popper", "popperDisabled", "popperPlacement", "popperApplyArrowClass", "popperPreventOverflow", "popperHideOnClickOutside", "popperTrigger", "popperStyles", "popperAriaDescribeBy", "popperAriaRole", "popperBoundaries", "popperCloseOnClickOutside", "popperDisableAnimation", "popperDisableStyle", "popperHideOnMouseLeave", "popperHideOnScroll", "popperAppendTo", "popperModifiers", "popperPositionFixed", "popperDelay", "popperShowOnStart", "popperTarget"], outputs: ["popperOnHidden", "popperOnShown", "popperOnUpdate"], exportAs: ["popper"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i9.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }], pipes: { "async": i5.AsyncPipe, "translate": i9.TranslatePipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4767
+ MessageComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageComponent, deps: [{ token: ChatClientService }, { token: ChannelService }, { token: CustomTemplatesService }, { token: i0.ChangeDetectorRef }, { token: ThemeService }, { token: DateParserService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
4768
+ MessageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageComponent, selector: "stream-message", inputs: { message: "message", enabledMessageActions: "enabledMessageActions", isLastSentMessage: "isLastSentMessage", mode: "mode", isHighlighted: "isHighlighted", customActions: "customActions" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n #container\n class=\"str-chat__message-simple str-chat__message str-chat__message--{{\n message?.type\n }} str-chat__message--{{ message?.status }} {{\n message?.text ? 'str-chat__message--has-text' : 'has-no-text'\n }}\"\n [class.str-chat__message--me]=\"isSentByCurrentUser\"\n [class.str-chat__message--other]=\"!isSentByCurrentUser\"\n [class.str-chat__message-simple--me]=\"isSentByCurrentUser\"\n [class.str-chat__message--has-attachment]=\"hasAttachment\"\n [class.str-chat__message--with-reactions]=\"hasReactions\"\n [class.str-chat__message--highlighted]=\"isHighlighted\"\n [class.str-chat__message-with-thread-link]=\"shouldDisplayThreadLink\"\n [class.str-chat__message-send-can-be-retried]=\"\n message?.status === 'failed' && message?.errorStatusCode !== 403\n \"\n data-testid=\"message-container\"\n>\n <ng-container *ngIf=\"!message?.deleted_at; else deletedMessage\">\n <ng-container *ngIf=\"message?.type !== 'system'; else systemMessage\">\n <ng-container *ngIf=\"themeVersion === '1'\">\n <ng-container *ngTemplateOutlet=\"messageStatus\"></ng-container>\n </ng-container>\n <stream-avatar-placeholder\n data-testid=\"avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n [imageUrl]=\"message?.user?.image\"\n [name]=\"message?.user?.name || message?.user?.id\"\n type=\"user\"\n location=\"message-sender\"\n [user]=\"message?.user || undefined\"\n ></stream-avatar-placeholder>\n <div class=\"str-chat__message-inner\">\n <div\n class=\"str-chat__message-simple__actions str-chat__message-options\"\n data-testid=\"message-options\"\n [class.str-chat__message-edit-in-progress]=\"isEditing\"\n *ngIf=\"areOptionsVisible\"\n >\n <div\n data-testid=\"message-actions-container\"\n #messageActionsToggle\n class=\"\n str-chat__message-actions-container\n str-chat__message-simple__actions__action\n str-chat__message-simple__actions__action--options\n \"\n [class.str-chat-angular__message-simple__actions__action--options--editing]=\"\n isEditing\n \"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerClick\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"true\"\n [popperHideOnMouseLeave]=\"false\"\n [popperDisableAnimation]=\"true\"\n >\n <popper-content #popperContent>\n <ng-template\n #defaultMessageActionsBox\n let-isOpen=\"isOpen\"\n let-isMine=\"isMine\"\n let-enabledActions=\"enabledActions\"\n let-messageInput=\"message\"\n let-displayedActionsCountChangeHandler=\"displayedActionsCountChangeHandler\"\n let-isEditingChangeHandler=\"isEditingChangeHandler\"\n let-customActions=\"customActions\"\n >\n <stream-message-actions-box\n [isOpen]=\"isOpen\"\n [isMine]=\"isMine\"\n [enabledActions]=\"enabledActions\"\n [message]=\"messageInput\"\n [customActions]=\"customActions\"\n (displayedActionsCount)=\"\n displayedActionsCountChangeHandler($event)\n \"\n (isEditing)=\"isEditingChangeHandler($event)\"\n ></stream-message-actions-box>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageActionsBoxTemplate$ | async) ||\n defaultMessageActionsBox;\n context: getMessageActionsBoxContext()\n \"\n >\n </ng-container>\n </popper-content>\n <div\n class=\"str-chat__message-actions-box-button\"\n *ngIf=\"visibleMessageActionsCount > 0\"\n data-testid=\"action-icon\"\n (click)=\"isActionBoxOpen = !isActionBoxOpen\"\n (keyup.enter)=\"isActionBoxOpen = !isActionBoxOpen\"\n >\n <stream-icon-placeholder\n icon=\"action-icon\"\n class=\"str-chat__message-action-icon\"\n ></stream-icon-placeholder>\n </div>\n </div>\n <div\n *ngIf=\"\n enabledMessageActions.indexOf('send-reply') !== -1 &&\n mode === 'main'\n \"\n class=\"\n str-chat__message-simple__actions__action\n str-chat__message-simple__actions__action--thread\n str-chat__message-reply-in-thread-button\n \"\n data-testid=\"reply-in-thread\"\n (click)=\"setAsActiveParentMessage()\"\n (keyup.enter)=\"setAsActiveParentMessage()\"\n >\n <stream-icon-placeholder\n class=\"str-chat__message-action-icon\"\n icon=\"reply-in-thread\"\n ></stream-icon-placeholder>\n </div>\n <div\n *ngIf=\"canReactToMessage\"\n class=\"\n str-chat__message-simple__actions__action\n str-chat__message-simple__actions__action--reactions\n str-chat__message-reactions-button\n \"\n data-testid=\"reaction-icon\"\n (click)=\"isReactionSelectorOpen = !isReactionSelectorOpen\"\n (keyup.enter)=\"isReactionSelectorOpen = !isReactionSelectorOpen\"\n >\n <stream-icon-placeholder\n class=\"str-chat__message-action-icon\"\n icon=\"reaction-icon\"\n ></stream-icon-placeholder>\n </div>\n </div>\n <div class=\"str-chat__message-reactions-host\">\n <ng-template\n #defaultMessageReactions\n let-messageReactionCounts=\"messageReactionCounts\"\n let-latestReactions=\"latestReactions\"\n let-isSelectorOpen=\"isSelectorOpen\"\n let-isSelectorOpenChangeHandler=\"isSelectorOpenChangeHandler\"\n let-messageId=\"messageId\"\n let-ownReactions=\"ownReactions\"\n >\n <stream-message-reactions\n [messageReactionCounts]=\"messageReactionCounts\"\n [latestReactions]=\"latestReactions\"\n [isSelectorOpen]=\"isSelectorOpen\"\n (isSelectorOpenChange)=\"isSelectorOpenChangeHandler($event)\"\n [messageId]=\"messageId\"\n [ownReactions]=\"ownReactions\"\n ></stream-message-reactions>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageReactionsTemplate$ | async) ||\n defaultMessageReactions;\n context: getMessageReactionsContext()\n \"\n ></ng-container>\n </div>\n <!-- transform: translate3d(0, 0, 0) fixes scrolling issues on iOS, see: https://stackoverflow.com/questions/50105780/elements-disappear-when-scrolling-in-safari-webkit-transform-fix-only-works-t/50144295#50144295 -->\n <!-- transform: none is required when image carousel is open in order for the modal to be correctly positioned, see how the transform property changes the behavior of fixed positioned elements https://developer.mozilla.org/en-US/docs/Web/CSS/position -->\n <div\n class=\"str-chat__message-bubble\"\n style=\"transform: {{\n imageAttachmentModalState === 'opened'\n ? 'none'\n : 'translate3d(0, 0, 0)'\n }}\"\n >\n <ng-container *ngIf=\"hasAttachment && !message?.quoted_message\">\n <ng-container\n *ngTemplateOutlet=\"attachmentsTemplate\"\n ></ng-container>\n </ng-container>\n <div\n class=\"str-chat__message-text\"\n tabindex=\"0\"\n *ngIf=\"message?.text || (message?.quoted_message && hasAttachment)\"\n >\n <div\n data-testid=\"inner-message\"\n class=\"\n str-chat__message-text-inner str-chat__message-simple-text-inner\n \"\n [class.str-chat__message-light-text-inner--has-attachment]=\"\n hasAttachment\n \"\n (click)=\"\n message?.status === 'failed' && message?.errorStatusCode !== 403\n ? resendMessage()\n : undefined\n \"\n (keyup.enter)=\"\n message?.status === 'failed' && message?.errorStatusCode !== 403\n ? resendMessage()\n : undefined\n \"\n >\n <ng-container *ngTemplateOutlet=\"quotedMessage\"></ng-container>\n <ng-container *ngIf=\"hasAttachment && message?.quoted_message\">\n <ng-container\n *ngTemplateOutlet=\"attachmentsTemplate\"\n ></ng-container>\n </ng-container>\n <div\n data-testid=\"client-error-message\"\n *ngIf=\"message?.type === 'error'\"\n class=\"\n str-chat__simple-message--error-message\n str-chat__message--error-message\n \"\n >\n {{ \"streamChat.Error \u00B7 Unsent\" | translate }}\n </div>\n <div\n data-testid=\"error-message\"\n *ngIf=\"message?.status === 'failed'\"\n class=\"\n str-chat__simple-message--error-message\n str-chat__message--error-message\n \"\n >\n {{\n (message?.errorStatusCode === 403\n ? \"streamChat.Message Failed \u00B7 Unauthorized\"\n : \"streamChat.Message Failed \u00B7 Click to try again\"\n ) | translate\n }}\n </div>\n <div data-testid=\"text\">\n <p>\n <ng-container *ngIf=\"messageTextParts; else defaultContent\">\n <!-- eslint-disable-next-line @angular-eslint/template/use-track-by-function -->\n <ng-container *ngFor=\"let part of messageTextParts\">\n <span\n *ngIf=\"part.type === 'text'; else mention\"\n [innerHTML]=\"part.content\"\n ></span>\n <ng-template #mention>\n <ng-template #defaultMention let-content=\"content\">\n <span class=\"str-chat__message-mention\">{{\n content\n }}</span>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.mentionTemplate$ | async) ||\n defaultMention;\n context: getMentionContext(part)\n \"\n ></ng-container>\n </ng-template>\n </ng-container>\n </ng-container>\n <ng-template #defaultContent>\n {{ messageText || \"\" }}\n </ng-template>\n </p>\n </div>\n </div>\n </div>\n <stream-icon-placeholder\n *ngIf=\"themeVersion === '2'\"\n class=\"str-chat__message-error-icon\"\n icon=\"error\"\n ></stream-icon-placeholder>\n </div>\n <ng-container\n *ngTemplateOutlet=\"\n replyCountButton;\n context: { position: 'inside-message-bubble' }\n \"\n ></ng-container>\n <ng-container\n *ngTemplateOutlet=\"\n messageDateAndSender;\n context: { position: 'inside-message-bubble' }\n \"\n ></ng-container>\n </div>\n <ng-container\n *ngTemplateOutlet=\"\n replyCountButton;\n context: { position: 'outside-message-bubble', message: message }\n \"\n ></ng-container>\n\n <ng-container\n *ngTemplateOutlet=\"\n messageDateAndSender;\n context: { position: 'outside-message-bubble' }\n \"\n ></ng-container>\n </ng-container>\n </ng-container>\n</div>\n\n<ng-template #deletedMessage>\n <div data-testid=\"message-deleted-component\">\n <div class=\"str-chat__message--deleted-inner\" translate>\n streamChat.This message was deleted...\n </div>\n </div>\n</ng-template>\n\n<ng-template #systemMessage>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.systemMessageTemplate$ | async) ||\n defaultSystemMessage;\n context: getMessageContext()\n \"\n ></ng-container>\n <ng-template #defaultSystemMessage let-messageInput=\"message\">\n <div data-testid=\"system-message\" class=\"str-chat__message--system\">\n <div class=\"str-chat__message--system__text\">\n <div class=\"str-chat__message--system__line\"></div>\n <p>{{ messageInput?.text }}</p>\n <div class=\"str-chat__message--system__line\"></div>\n </div>\n <div class=\"str-chat__message--system__date\">\n {{ parsedDate }}\n </div>\n </div>\n </ng-template>\n</ng-template>\n\n<ng-template #quotedMessage>\n <div\n *ngIf=\"message?.quoted_message\"\n class=\"quoted-message str-chat__quoted-message-preview\"\n data-testid=\"quoted-message-container\"\n [class.mine]=\"isSentByCurrentUser\"\n (click)=\"\n jumpToMessage(\n (message?.quoted_message)!.id,\n message?.quoted_message?.parent_id\n )\n \"\n (keyup.enter)=\"\n jumpToMessage(\n (message?.quoted_message)!.id,\n message?.quoted_message?.parent_id\n )\n \"\n >\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n [imageUrl]=\"message?.quoted_message?.user?.image\"\n [name]=\"\n message?.quoted_message?.user?.name || message?.quoted_message?.user?.id\n \"\n [size]=\"20\"\n type=\"user\"\n location=\"quoted-message-sender\"\n [user]=\"message?.quoted_message?.user || undefined\"\n ></stream-avatar-placeholder>\n <div class=\"quoted-message-inner str-chat__quoted-message-bubble\">\n <ng-container\n *ngIf=\"\n message?.quoted_message?.attachments &&\n message?.quoted_message?.attachments?.length\n \"\n >\n <ng-template\n #defaultAttachments\n let-messageId=\"messageId\"\n let-attachments=\"attachments\"\n let-parentMessageId=\"parentMessageId\"\n let-imageModalStateChangeHandler=\"imageModalStateChangeHandler\"\n >\n <stream-attachment-list\n [messageId]=\"messageId\"\n [attachments]=\"attachments\"\n [parentMessageId]=\"parentMessageId\"\n (imageModalStateChange)=\"imageModalStateChangeHandler($event)\"\n ></stream-attachment-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.attachmentListTemplate$ | async) ||\n defaultAttachments;\n context: getQuotedMessageAttachmentListContext()\n \"\n ></ng-container>\n </ng-container>\n <div\n data-testid=\"quoted-message-text\"\n [innerHTML]=\"\n message?.quoted_message?.translation ||\n message?.quoted_message?.html ||\n message?.quoted_message?.text\n \"\n ></div>\n </div>\n </div>\n</ng-template>\n\n<!-- We need these markups in slightly different positions in theme-v1 and theme-v2, this soultion makes that possible without duplicating the code -->\n<ng-template #messageDateAndSender let-position=\"position\">\n <ng-container\n *ngIf=\"\n (position === 'inside-message-bubble' && themeVersion === '1') ||\n (position === 'outside-message-bubble' && themeVersion === '2')\n \"\n >\n <div\n class=\"str-chat__translation-notice\"\n *ngIf=\"shouldDisplayTranslationNotice\"\n data-testid=\"translation-notice\"\n >\n <button\n data-testid=\"see-original\"\n *ngIf=\"displayedMessageTextContent === 'translation'\"\n (click)=\"displayOriginalMessage()\"\n (keyup.enter)=\"displayOriginalMessage()\"\n translate\n >\n streamChat.See original (automatically translated)\n </button>\n <button\n data-testid=\"see-translation\"\n *ngIf=\"displayedMessageTextContent === 'original'\"\n (click)=\"displayTranslatedMessage()\"\n (keyup.enter)=\"displayTranslatedMessage()\"\n translate\n >\n streamChat.See translation\n </button>\n </div>\n <div\n class=\"\n str-chat__message-data\n str-chat__message-simple-data\n str-chat__message-metadata\n \"\n >\n <ng-container *ngIf=\"themeVersion === '2'\">\n <ng-container *ngTemplateOutlet=\"messageStatus\"></ng-container>\n </ng-container>\n <span\n data-testid=\"sender\"\n *ngIf=\"!isSentByCurrentUser\"\n class=\"str-chat__message-simple-name str-chat__message-sender-name\"\n >\n {{ message?.user?.name ? message?.user?.name : message?.user?.id }}\n </span>\n <span data-testid=\"date\" class=\"str-chat__message-simple-timestamp\">\n {{ parsedDate }}\n </span>\n </div>\n </ng-container>\n</ng-template>\n\n<ng-template #messageStatus>\n <ng-container\n *ngIf=\"\n isSentByCurrentUser &&\n ((isLastSentMessage && message?.status === 'received') ||\n message?.status === 'sending')\n \"\n >\n <ng-container *ngIf=\"message?.status === 'sending'; else sentStatus\">\n <ng-container *ngTemplateOutlet=\"sendingStatus\"></ng-container>\n </ng-container>\n <ng-template #sentStatus>\n <ng-container\n *ngIf=\"\n mode === 'main' && isMessageDeliveredAndRead && canDisplayReadStatus;\n else deliveredStatus\n \"\n >\n <ng-container *ngTemplateOutlet=\"readStatus\"></ng-container>\n </ng-container>\n </ng-template>\n <ng-template #deliveredStatus>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.deliveredStatusTemplate$ | async) ||\n defaultDeliveredStatus;\n context: getDeliveredStatusContext()\n \"\n ></ng-container>\n </ng-template>\n <ng-template #defaultDeliveredStatus>\n <span\n *ngIf=\"mode === 'main'\"\n class=\"\n str-chat__message-simple-status\n str-chat__message-simple-status-angular\n str-chat__message-status\n \"\n data-testid=\"delivered-indicator\"\n tabindex=\"0\"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerHover\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"false\"\n (hover)=\"$event.stopPropagation()\"\n >\n <popper-content #popperContent>\n <div class=\"str-chat__tooltip str-chat__tooltip-angular\">\n {{ \"streamChat.Delivered\" | translate }}\n </div>\n </popper-content>\n <stream-icon-placeholder\n data-testid=\"delivered-icon\"\n icon=\"delivered-icon\"\n ></stream-icon-placeholder>\n </span>\n </ng-template>\n <ng-template #sendingStatus>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.sendingStatusTemplate$ | async) ||\n defaultSendingStatus;\n context: getSendingStatusContext()\n \"\n ></ng-container>\n </ng-template>\n <ng-template #defaultSendingStatus>\n <span\n class=\"\n str-chat__message-simple-status\n str-chat__message-simple-status-angular\n str-chat__message-status\n \"\n data-testid=\"sending-indicator\"\n tabindex=\"0\"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerHover\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"false\"\n (hover)=\"$event.stopPropagation()\"\n >\n <popper-content #popperContent>\n <div class=\"str-chat__tooltip str-chat__tooltip-angular\">\n {{ \"streamChat.Sending...\" | translate }}\n </div>\n </popper-content>\n <stream-loading-indicator-placeholder\n data-testid=\"loading-indicator\"\n ></stream-loading-indicator-placeholder>\n </span>\n </ng-template>\n <ng-template #readStatus>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.readStatusTemplate$ | async) ||\n defaultReadStatus;\n context: getReadStatusContext()\n \"\n ></ng-container>\n </ng-template>\n <ng-template #defaultReadStatus>\n <span\n class=\"\n str-chat__message-simple-status\n str-chat__message-simple-status-angular\n str-chat__message-status\n \"\n data-testid=\"read-indicator\"\n tabindex=\"0\"\n [popper]=\"popperContent\"\n [popperTrigger]=\"popperTriggerHover\"\n [popperPlacement]=\"popperPlacementAuto\"\n [popperHideOnScroll]=\"false\"\n [popperHideOnClickOutside]=\"false\"\n (hover)=\"$event.stopPropagation()\"\n >\n <popper-content #popperContent>\n <div\n class=\"str-chat__tooltip str-chat__tooltip-angular\"\n data-testid=\"read-by-tooltip\"\n >\n {{ readByText }}\n </div>\n </popper-content>\n <stream-avatar-placeholder\n class=\"str-chat-angular__avatar-host\"\n data-test-id=\"last-read-user-avatar\"\n [size]=\"15\"\n [imageUrl]=\"lastReadUser?.image\"\n [name]=\"lastReadUser?.name || lastReadUser?.id\"\n type=\"user\"\n location=\"message-reader\"\n [user]=\"lastReadUser\"\n ></stream-avatar-placeholder>\n <span\n data-test-id=\"read-by-length\"\n *ngIf=\"isReadByMultipleUsers\"\n class=\"str-chat__message-simple-status-number\"\n >\n {{ (message?.readBy)!.length }}\n </span>\n </span>\n </ng-template>\n </ng-container>\n</ng-template>\n\n<ng-template #replyCountButton let-position=\"position\">\n <div\n *ngIf=\"\n (position === 'inside-message-bubble' && themeVersion === '1') ||\n (position === 'outside-message-bubble' && themeVersion === '2')\n \"\n class=\"\n str-chat__message-simple-reply-button\n str-chat__message-replies-count-button-wrapper\n \"\n >\n <button\n *ngIf=\"shouldDisplayThreadLink\"\n class=\"str-chat__message-replies-count-button\"\n data-testid=\"reply-count-button\"\n (click)=\"setAsActiveParentMessage()\"\n >\n <stream-icon-placeholder\n *ngIf=\"themeVersion === '1'\"\n stream-icon-placeholder\n icon=\"reply\"\n ></stream-icon-placeholder>\n {{message?.reply_count === 1 ? ('streamChat.1 reply' | translate) : ('streamChat.{{ replyCount }}\n replies' | translate:replyCountParam)}}\n </button>\n </div>\n</ng-template>\n\n<ng-template #attachmentsTemplate>\n <ng-template\n #defaultAttachments\n let-messageId=\"messageId\"\n let-attachments=\"attachments\"\n let-parentMessageId=\"parentMessageId\"\n let-imageModalStateChangeHandler=\"imageModalStateChangeHandler\"\n >\n <stream-attachment-list\n [messageId]=\"messageId\"\n [attachments]=\"attachments\"\n [parentMessageId]=\"parentMessageId\"\n (imageModalStateChange)=\"imageModalStateChangeHandler($event)\"\n ></stream-attachment-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.attachmentListTemplate$ | async) ||\n defaultAttachments;\n context: getAttachmentListContext()\n \"\n ></ng-container>\n</ng-template>\n", components: [{ type: AvatarPlaceholderComponent, selector: "stream-avatar-placeholder", inputs: ["name", "imageUrl", "size", "location", "channel", "user", "type", "initialsType", "showOnlineIndicator"] }, { type: i3.NgxPopperjsContentComponent, selector: "popper-content", exportAs: ["ngxPopperjsContent"] }, { type: MessageActionsBoxComponent, selector: "stream-message-actions-box", inputs: ["isOpen", "isMine", "message", "enabledActions", "customActions"], outputs: ["displayedActionsCount", "isEditing"] }, { type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: MessageReactionsComponent, selector: "stream-message-reactions", inputs: ["messageId", "messageReactionCounts", "isSelectorOpen", "latestReactions", "ownReactions"], outputs: ["isSelectorOpenChange"] }, { type: AttachmentListComponent, selector: "stream-attachment-list", inputs: ["messageId", "parentMessageId", "attachments"], outputs: ["imageModalStateChange"] }, { type: LoadingIndicatorPlaceholderComponent, selector: "stream-loading-indicator-placeholder", inputs: ["size", "color"] }], directives: [{ type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i3.NgxPopperjsDirective, selector: "[popper]", inputs: ["popperTimeout", "popperTimeoutAfterShow", "popperApplyClass", "popper", "popperDisabled", "popperPlacement", "popperApplyArrowClass", "popperPreventOverflow", "popperHideOnClickOutside", "popperTrigger", "popperStyles", "popperAriaDescribeBy", "popperAriaRole", "popperBoundaries", "popperCloseOnClickOutside", "popperDisableAnimation", "popperDisableStyle", "popperHideOnMouseLeave", "popperHideOnScroll", "popperAppendTo", "popperModifiers", "popperPositionFixed", "popperDelay", "popperShowOnStart", "popperTarget"], outputs: ["popperOnHidden", "popperOnShown", "popperOnUpdate"], exportAs: ["popper"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i9.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }], pipes: { "async": i5.AsyncPipe, "translate": i9.TranslatePipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4731
4769
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageComponent, decorators: [{
4732
4770
  type: Component,
4733
4771
  args: [{
@@ -4736,7 +4774,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
4736
4774
  styles: [],
4737
4775
  changeDetection: ChangeDetectionStrategy.OnPush,
4738
4776
  }]
4739
- }], ctorParameters: function () { return [{ type: ChatClientService }, { type: ChannelService }, { type: CustomTemplatesService }, { type: i0.ChangeDetectorRef }, { type: ThemeService }, { type: DateParserService }]; }, propDecorators: { message: [{
4777
+ }], ctorParameters: function () { return [{ type: ChatClientService }, { type: ChannelService }, { type: CustomTemplatesService }, { type: i0.ChangeDetectorRef }, { type: ThemeService }, { type: DateParserService }, { type: i0.NgZone }]; }, propDecorators: { message: [{
4740
4778
  type: Input
4741
4779
  }], enabledMessageActions: [{
4742
4780
  type: Input
@@ -5167,11 +5205,12 @@ const isOnSameDay = (date1, date2) => {
5167
5205
  * The `MessageList` component renders a scrollable list of messages.
5168
5206
  */
5169
5207
  class MessageListComponent {
5170
- constructor(channelService, chatClientService, customTemplatesService, dateParser) {
5208
+ constructor(channelService, chatClientService, customTemplatesService, dateParser, ngZone) {
5171
5209
  this.channelService = channelService;
5172
5210
  this.chatClientService = chatClientService;
5173
5211
  this.customTemplatesService = customTemplatesService;
5174
5212
  this.dateParser = dateParser;
5213
+ this.ngZone = ngZone;
5175
5214
  /**
5176
5215
  * Determines if the message list should display channel messages or [thread messages](https://getstream.io/chat/docs/javascript/threads/?language=javascript).
5177
5216
  */
@@ -5215,16 +5254,19 @@ class MessageListComponent {
5215
5254
  this.class = 'str-chat-angular__main-panel-inner str-chat-angular__message-list-host str-chat__main-panel-inner';
5216
5255
  this.unreadMessageCount = 0;
5217
5256
  this.groupStyles = [];
5257
+ this.isNextMessageOnSeparateDate = [];
5218
5258
  this.isLoading = false;
5219
5259
  this.isScrollInProgress = false;
5220
5260
  this.isJumpingToLatestUnreadMessage = false;
5221
5261
  this.subscriptions = [];
5222
5262
  this.isLatestMessageInList = true;
5263
+ this.parsedDates = new Map();
5223
5264
  this.subscriptions.push(this.channelService.activeChannel$.subscribe((channel) => {
5224
5265
  var _a, _b, _c, _d, _e, _f, _g;
5225
5266
  (_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}` });
5226
5267
  if (this.channelId !== (channel === null || channel === void 0 ? void 0 : channel.id)) {
5227
5268
  (_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}` });
5269
+ this.parsedDates = new Map();
5228
5270
  this.resetScrollState();
5229
5271
  this.channelId = channel === null || channel === void 0 ? void 0 : channel.id;
5230
5272
  if (this.openMessageListAt === 'last-read-message' &&
@@ -5292,6 +5334,9 @@ class MessageListComponent {
5292
5334
  }
5293
5335
  }
5294
5336
  ngAfterViewInit() {
5337
+ this.ngZone.runOutsideAngular(() => {
5338
+ this.scrollContainer.nativeElement.addEventListener('scroll', () => this.scrolled());
5339
+ });
5295
5340
  this.subscriptions.push(this.channelService.jumpToMessage$
5296
5341
  .pipe(filter((config) => !!config.id))
5297
5342
  .subscribe((config) => {
@@ -5387,13 +5432,15 @@ class MessageListComponent {
5387
5432
  this.scrollContainer.nativeElement.scrollTop = 0;
5388
5433
  }
5389
5434
  scrolled() {
5390
- var _a, _b, _c, _d;
5435
+ var _a, _b;
5391
5436
  this.isScrollInProgress = true;
5392
5437
  if (this.scrollEndTimeout) {
5393
5438
  clearTimeout(this.scrollEndTimeout);
5394
5439
  }
5395
5440
  this.scrollEndTimeout = setTimeout(() => {
5396
- this.isScrollInProgress = false;
5441
+ this.ngZone.run(() => {
5442
+ this.isScrollInProgress = false;
5443
+ });
5397
5444
  }, 100);
5398
5445
  if (this.scrollContainer.nativeElement.scrollHeight ===
5399
5446
  this.scrollContainer.nativeElement.clientHeight) {
@@ -5401,27 +5448,36 @@ class MessageListComponent {
5401
5448
  }
5402
5449
  const scrollPosition = this.getScrollPosition();
5403
5450
  (_b = (_a = this.chatClientService.chatClient) === null || _a === void 0 ? void 0 : _a.logger) === null || _b === void 0 ? void 0 : _b.call(_a, 'info', `Scrolled - scroll position: ${scrollPosition}, container height: ${this.scrollContainer.nativeElement.scrollHeight}`, { tags: `message list ${this.mode}` });
5404
- this.isUserScrolled =
5405
- (this.direction === 'bottom-to-top'
5406
- ? scrollPosition !== 'bottom'
5407
- : scrollPosition !== 'top') || !this.isLatestMessageInList;
5408
- if (!this.isUserScrolled) {
5409
- this.unreadMessageCount = 0;
5451
+ const isUserScrolled = (this.direction === 'bottom-to-top'
5452
+ ? scrollPosition !== 'bottom'
5453
+ : scrollPosition !== 'top') || !this.isLatestMessageInList;
5454
+ if (this.isUserScrolled !== isUserScrolled) {
5455
+ this.ngZone.run(() => {
5456
+ this.isUserScrolled = isUserScrolled;
5457
+ if (!this.isUserScrolled) {
5458
+ this.unreadMessageCount = 0;
5459
+ }
5460
+ });
5410
5461
  }
5411
5462
  if (this.shouldLoadMoreMessages(scrollPosition)) {
5412
- this.containerHeight = this.scrollContainer.nativeElement.scrollHeight;
5413
- let direction;
5414
- if (this.direction === 'top-to-bottom') {
5415
- direction = scrollPosition === 'top' ? 'newer' : 'older';
5416
- }
5417
- else {
5418
- direction = scrollPosition === 'top' ? 'older' : 'newer';
5419
- }
5420
- this.mode === 'main'
5421
- ? void this.channelService.loadMoreMessages(direction)
5422
- : void this.channelService.loadMoreThreadReplies(direction);
5423
- (_d = (_c = this.chatClientService.chatClient) === null || _c === void 0 ? void 0 : _c.logger) === null || _d === void 0 ? void 0 : _d.call(_c, 'info', `Displaying loading indicator`, { tags: `message list ${this.mode}` });
5424
- this.isLoading = true;
5463
+ this.ngZone.run(() => {
5464
+ var _a, _b;
5465
+ this.containerHeight = this.scrollContainer.nativeElement.scrollHeight;
5466
+ let direction;
5467
+ if (this.direction === 'top-to-bottom') {
5468
+ direction = scrollPosition === 'top' ? 'newer' : 'older';
5469
+ }
5470
+ else {
5471
+ direction = scrollPosition === 'top' ? 'older' : 'newer';
5472
+ }
5473
+ const result = this.mode === 'main'
5474
+ ? this.channelService.loadMoreMessages(direction)
5475
+ : this.channelService.loadMoreThreadReplies(direction);
5476
+ if (result) {
5477
+ (_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}` });
5478
+ this.isLoading = true;
5479
+ }
5480
+ });
5425
5481
  }
5426
5482
  this.prevScrollTop = this.scrollContainer.nativeElement.scrollTop;
5427
5483
  }
@@ -5434,20 +5490,6 @@ class MessageListComponent {
5434
5490
  const text = listUsers(users);
5435
5491
  return text;
5436
5492
  }
5437
- areOnSeparateDates(message, nextMessage) {
5438
- if (!message || !nextMessage) {
5439
- return false;
5440
- }
5441
- if (message.created_at.getDate() !== nextMessage.created_at.getDate()) {
5442
- return true;
5443
- }
5444
- else if (message.created_at.getFullYear() !==
5445
- nextMessage.created_at.getFullYear() ||
5446
- message.created_at.getMonth() !== nextMessage.created_at.getMonth()) {
5447
- return true;
5448
- }
5449
- return false;
5450
- }
5451
5493
  isSentByCurrentUser(message) {
5452
5494
  var _a, _b;
5453
5495
  if (!message) {
@@ -5456,7 +5498,12 @@ class MessageListComponent {
5456
5498
  return ((_a = message.user) === null || _a === void 0 ? void 0 : _a.id) === ((_b = this.chatClientService.chatClient.user) === null || _b === void 0 ? void 0 : _b.id);
5457
5499
  }
5458
5500
  parseDate(date) {
5459
- return this.dateParser.parseDate(date);
5501
+ if (this.parsedDates.has(date)) {
5502
+ return this.parsedDates.get(date);
5503
+ }
5504
+ const parsedDate = this.dateParser.parseDate(date);
5505
+ this.parsedDates.set(date, parsedDate);
5506
+ return parsedDate;
5460
5507
  }
5461
5508
  get replyCountParam() {
5462
5509
  var _a;
@@ -5547,6 +5594,7 @@ class MessageListComponent {
5547
5594
  }
5548
5595
  }), map((messages) => this.direction === 'bottom-to-top' ? messages : [...messages].reverse()), tap((messages) => {
5549
5596
  this.groupStyles = messages.map((m, i) => getGroupStyles(m, messages[i - 1], messages[i + 1]));
5597
+ this.isNextMessageOnSeparateDate = messages.map((m, i) => this.checkIfOnSeparateDates(m, messages[i + 1]));
5550
5598
  }));
5551
5599
  }
5552
5600
  resetScrollState() {
@@ -5607,9 +5655,23 @@ class MessageListComponent {
5607
5655
  }
5608
5656
  }
5609
5657
  }
5658
+ checkIfOnSeparateDates(message, nextMessage) {
5659
+ if (!message || !nextMessage) {
5660
+ return false;
5661
+ }
5662
+ if (message.created_at.getDate() !== nextMessage.created_at.getDate()) {
5663
+ return true;
5664
+ }
5665
+ else if (message.created_at.getFullYear() !==
5666
+ nextMessage.created_at.getFullYear() ||
5667
+ message.created_at.getMonth() !== nextMessage.created_at.getMonth()) {
5668
+ return true;
5669
+ }
5670
+ return false;
5671
+ }
5610
5672
  }
5611
- 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 }], target: i0.ɵɵFactoryTarget.Component });
5612
- 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", dateSeparatorTextPos: "dateSeparatorTextPos", openMessageListAt: "openMessageListAt", 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: "<div\n #scrollContainer\n data-testid=\"scroll-container\"\n class=\"str-chat__list\"\n (scroll)=\"scrolled()\"\n style=\"overscroll-behavior: none\"\n>\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 <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 isNewMessage: false\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 !isLast &&\n direction === 'bottom-to-top' &&\n !isSentByCurrentUser(messages[i + 1]) &&\n (!displayDateSeparator ||\n !areOnSeparateDates(message, messages[i + 1]))) ||\n (direction === 'top-to-bottom' &&\n !isLast &&\n !isSentByCurrentUser(message) &&\n lastReadMessageId === messages[i + 1].id)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customnewMessagesIndicatorTemplate ||\n defaultNewMessagesIndicator\n \"\n ></ng-container>\n </ng-container>\n <ng-container *ngIf=\"areOnSeparateDates(message, messages[i + 1])\">\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 isNewMessage:\n (direction === 'bottom-to-top' &&\n message.id === lastReadMessageId &&\n !isSentByCurrentUser(messages[i + 1])) ||\n (direction === 'top-to-bottom' && false)\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=\"\n isUserScrolled &&\n (!isScrollInProgress || !hideJumpToLatestButtonDuringScroll)\n \"\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=\"unreadMessageCount > 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 {{ unreadMessageCount }}\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:\n message?.id === highlightedMessageId &&\n !isJumpingToLatestUnreadMessage,\n customActions: customMessageActions\n }\n \"\n ></ng-container>\n</ng-template>\n\n<ng-template\n #dateSeparator\n let-date=\"date\"\n let-parsedDate=\"parsedDate\"\n let-isNewMessage=\"isNewMessage\"\n>\n <ng-container *ngIf=\"displayDateSeparator\">\n <ng-container\n *ngTemplateOutlet=\"\n customDateSeparatorTemplate || defaultDateSeparator;\n context: {\n date: date,\n parsedDate: parsedDate,\n isNewMessage: isNewMessage\n }\n \"\n ></ng-container>\n </ng-container>\n\n <ng-template\n #defaultDateSeparator\n let-date=\"date\"\n let-parsedDate=\"parsedDate\"\n let-isNewMessage=\"isNewMessage\"\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 <span\n *ngIf=\"isNewMessage\"\n data-testid=\"new-messages-indicator-date-separator\"\n >\u2022 {{ \"streamChat.New\" | translate }}</span\n >\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>\n <div data-testid=\"new-messages-indicator\" 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\" translate>streamChat.New</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", components: [{ 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: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i9.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }], pipes: { "translate": i9.TranslatePipe, "async": i5.AsyncPipe } });
5673
+ 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 }], target: i0.ɵɵFactoryTarget.Component });
5674
+ 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", dateSeparatorTextPos: "dateSeparatorTextPos", openMessageListAt: "openMessageListAt", 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: "<div\n #scrollContainer\n data-testid=\"scroll-container\"\n class=\"str-chat__list\"\n style=\"overscroll-behavior: none\"\n>\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 <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 isNewMessage: false\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 !isLast &&\n direction === 'bottom-to-top' &&\n !isSentByCurrentUser(messages[i + 1]) &&\n (!displayDateSeparator || !isNextMessageOnSeparateDate[i])) ||\n (direction === 'top-to-bottom' &&\n !isLast &&\n !isSentByCurrentUser(message) &&\n lastReadMessageId === messages[i + 1].id)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customnewMessagesIndicatorTemplate ||\n defaultNewMessagesIndicator\n \"\n ></ng-container>\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 isNewMessage:\n (direction === 'bottom-to-top' &&\n message.id === lastReadMessageId &&\n !isSentByCurrentUser(messages[i + 1])) ||\n (direction === 'top-to-bottom' && false)\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=\"\n isUserScrolled &&\n (!isScrollInProgress || !hideJumpToLatestButtonDuringScroll)\n \"\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=\"unreadMessageCount > 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 {{ unreadMessageCount }}\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:\n message?.id === highlightedMessageId &&\n !isJumpingToLatestUnreadMessage,\n customActions: customMessageActions\n }\n \"\n ></ng-container>\n</ng-template>\n\n<ng-template\n #dateSeparator\n let-date=\"date\"\n let-parsedDate=\"parsedDate\"\n let-isNewMessage=\"isNewMessage\"\n>\n <ng-container *ngIf=\"displayDateSeparator\">\n <ng-container\n *ngTemplateOutlet=\"\n customDateSeparatorTemplate || defaultDateSeparator;\n context: {\n date: date,\n parsedDate: parsedDate,\n isNewMessage: isNewMessage\n }\n \"\n ></ng-container>\n </ng-container>\n\n <ng-template\n #defaultDateSeparator\n let-date=\"date\"\n let-parsedDate=\"parsedDate\"\n let-isNewMessage=\"isNewMessage\"\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 <span\n *ngIf=\"isNewMessage\"\n data-testid=\"new-messages-indicator-date-separator\"\n >\u2022 {{ \"streamChat.New\" | translate }}</span\n >\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>\n <div data-testid=\"new-messages-indicator\" 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\" translate>streamChat.New</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", components: [{ 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: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i9.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }], pipes: { "translate": i9.TranslatePipe, "async": i5.AsyncPipe } });
5613
5675
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageListComponent, decorators: [{
5614
5676
  type: Component,
5615
5677
  args: [{
@@ -5617,7 +5679,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
5617
5679
  templateUrl: './message-list.component.html',
5618
5680
  styles: [],
5619
5681
  }]
5620
- }], ctorParameters: function () { return [{ type: ChannelService }, { type: ChatClientService }, { type: CustomTemplatesService }, { type: DateParserService }]; }, propDecorators: { mode: [{
5682
+ }], ctorParameters: function () { return [{ type: ChannelService }, { type: ChatClientService }, { type: CustomTemplatesService }, { type: DateParserService }, { type: i0.NgZone }]; }, propDecorators: { mode: [{
5621
5683
  type: Input
5622
5684
  }], direction: [{
5623
5685
  type: Input