stream-chat-angular 6.3.1 → 6.4.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.
- package/assets/version.d.ts +1 -1
- package/esm2020/assets/version.mjs +2 -2
- package/esm2020/lib/channel.service.mjs +12 -1
- package/esm2020/lib/message-input/message-input.component.mjs +101 -4
- package/fesm2015/stream-chat-angular.mjs +116 -6
- package/fesm2015/stream-chat-angular.mjs.map +1 -1
- package/fesm2020/stream-chat-angular.mjs +114 -6
- package/fesm2020/stream-chat-angular.mjs.map +1 -1
- package/lib/channel.service.d.ts +8 -1
- package/lib/message-input/message-input.component.d.ts +29 -2
- package/package.json +1 -1
- package/src/assets/version.ts +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, Component, Input, EventEmitter, Output, ViewChild, HostBinding, TemplateRef, ContentChild, ChangeDetectionStrategy, InjectionToken, Directive, NgModule, Inject, Optional } from '@angular/core';
|
|
3
|
-
import { BehaviorSubject, ReplaySubject, combineLatest, map as map$1, shareReplay as shareReplay$1, take as take$1, pairwise, Subject, timer, merge, switchMap, distinctUntilChanged, filter as filter$1, of } from 'rxjs';
|
|
3
|
+
import { BehaviorSubject, ReplaySubject, combineLatest, map as map$1, shareReplay as shareReplay$1, take as take$1, pairwise, Subject, timer, merge, switchMap, distinctUntilChanged as distinctUntilChanged$1, filter as filter$1, of } from 'rxjs';
|
|
4
4
|
import { StreamChat, VotingVisibility, isVoteAnswer } from 'stream-chat';
|
|
5
|
-
import { take, shareReplay, map, first, filter, tap, debounceTime, throttleTime } from 'rxjs/operators';
|
|
5
|
+
import { take, shareReplay, map, first, filter, distinctUntilChanged, tap, debounceTime, throttleTime } from 'rxjs/operators';
|
|
6
6
|
import { v4 } from 'uuid';
|
|
7
7
|
import * as i6 from '@ngx-translate/core';
|
|
8
8
|
import { TranslateModule } from '@ngx-translate/core';
|
|
@@ -22,7 +22,7 @@ import { MentionModule } from 'angular-mentions';
|
|
|
22
22
|
import * as i1$1 from '@angular/forms';
|
|
23
23
|
import { FormGroup, FormControl, Validators, FormArray, ReactiveFormsModule } from '@angular/forms';
|
|
24
24
|
|
|
25
|
-
const version = '6.
|
|
25
|
+
const version = '6.4.0';
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* The `NotificationService` can be used to add or remove notifications. By default the [`NotificationList`](/chat/docs/sdk/angular/components/NotificationListComponent/) component displays the currently active notifications.
|
|
@@ -479,6 +479,7 @@ class ChannelService {
|
|
|
479
479
|
this._shouldMarkActiveChannelAsRead = true;
|
|
480
480
|
this.isStateRecoveryInProgress = false;
|
|
481
481
|
this.channelQueryStateSubject = new BehaviorSubject(undefined);
|
|
482
|
+
this.channelSwitchStateSubject = new BehaviorSubject('end');
|
|
482
483
|
this.channelListSetter = (channels, shouldStopWatchingRemovedChannels = true) => {
|
|
483
484
|
const currentChannels = this.channelsSubject.getValue() || [];
|
|
484
485
|
const deletedChannels = currentChannels.filter((c) => !channels?.find((channel) => channel.cid === c.cid));
|
|
@@ -580,6 +581,9 @@ class ChannelService {
|
|
|
580
581
|
this.channelQueryState$ = this.channelQueryStateSubject
|
|
581
582
|
.asObservable()
|
|
582
583
|
.pipe(shareReplay(1));
|
|
584
|
+
this.channelSwitchState$ = this.channelSwitchStateSubject
|
|
585
|
+
.asObservable()
|
|
586
|
+
.pipe(shareReplay(1));
|
|
583
587
|
}
|
|
584
588
|
/**
|
|
585
589
|
* 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.
|
|
@@ -618,6 +622,7 @@ class ChannelService {
|
|
|
618
622
|
* @param channel
|
|
619
623
|
*/
|
|
620
624
|
setAsActiveChannel(channel) {
|
|
625
|
+
this.channelSwitchStateSubject.next('start');
|
|
621
626
|
const prevActiveChannel = this.activeChannelSubject.getValue();
|
|
622
627
|
if (prevActiveChannel?.cid === channel.cid) {
|
|
623
628
|
return;
|
|
@@ -641,11 +646,13 @@ class ChannelService {
|
|
|
641
646
|
channel.state.latestMessages = channel.state.latestMessages.slice(channelStateLength - 2 * this.messagePageSize);
|
|
642
647
|
}
|
|
643
648
|
this.setChannelState(channel);
|
|
649
|
+
this.channelSwitchStateSubject.next('end');
|
|
644
650
|
}
|
|
645
651
|
/**
|
|
646
652
|
* Deselects the currently active (if any) channel
|
|
647
653
|
*/
|
|
648
654
|
deselectActiveChannel() {
|
|
655
|
+
this.channelSwitchStateSubject.next('start');
|
|
649
656
|
const activeChannel = this.activeChannelSubject.getValue();
|
|
650
657
|
if (!activeChannel) {
|
|
651
658
|
return;
|
|
@@ -666,6 +673,7 @@ class ChannelService {
|
|
|
666
673
|
this.activeChannelUnreadCount = undefined;
|
|
667
674
|
this.areReadEventsPaused = false;
|
|
668
675
|
this.isMessageLoadingInProgress = false;
|
|
676
|
+
this.channelSwitchStateSubject.next('end');
|
|
669
677
|
}
|
|
670
678
|
/**
|
|
671
679
|
* Sets the given `message` as an active parent message. If `undefined` is provided, it will deleselect the current parent message.
|
|
@@ -1093,6 +1101,9 @@ class ChannelService {
|
|
|
1093
1101
|
* @param message The message to select, if called with `undefined`, it deselects the message
|
|
1094
1102
|
*/
|
|
1095
1103
|
selectMessageToQuote(message) {
|
|
1104
|
+
if (message && !this.isStreamMessage(message)) {
|
|
1105
|
+
message = this.transformToStreamMessage(message);
|
|
1106
|
+
}
|
|
1096
1107
|
this.messageToQuoteSubject.next(message);
|
|
1097
1108
|
}
|
|
1098
1109
|
/**
|
|
@@ -6666,6 +6677,20 @@ class MessageInputComponent {
|
|
|
6666
6677
|
* Emits when a message was successfuly sent or updated
|
|
6667
6678
|
*/
|
|
6668
6679
|
this.messageUpdate = new EventEmitter();
|
|
6680
|
+
/**
|
|
6681
|
+
* Emits the messsage draft whenever the composed message changes.
|
|
6682
|
+
* - If the user clears the message input, or sends the message, undefined is emitted.
|
|
6683
|
+
* - If active channel changes, nothing is emitted.
|
|
6684
|
+
*
|
|
6685
|
+
* To save and fetch message drafts, you can use the [Stream message drafts API](https://getstream.io/chat/docs/javascript/drafts/).
|
|
6686
|
+
*
|
|
6687
|
+
* Message draft only works for new messages, nothing is emitted when input is in edit mode (if `message` input is set).
|
|
6688
|
+
*
|
|
6689
|
+
* To load a message draft into the input, use the `loadDraft` method.
|
|
6690
|
+
* - If channel id doesn't match the active channel id, the draft is ignored.
|
|
6691
|
+
* - If a thread message is loaded, and the input isn't in thread mode or parent ids don't match, the draft is ignored.
|
|
6692
|
+
*/
|
|
6693
|
+
this.messageDraftChange = new EventEmitter();
|
|
6669
6694
|
this.class = 'str-chat__message-input-angular-host';
|
|
6670
6695
|
this.isVoiceRecording = true;
|
|
6671
6696
|
this.textareaValue = '';
|
|
@@ -6678,12 +6703,16 @@ class MessageInputComponent {
|
|
|
6678
6703
|
this.isViewInited = false;
|
|
6679
6704
|
this.defaultTextareaPlaceholder = 'streamChat.Type your message';
|
|
6680
6705
|
this.slowModeTextareaPlaceholder = 'streamChat.Slow Mode ON';
|
|
6706
|
+
this.isChannelChangeResetInProgress = false;
|
|
6707
|
+
this.isSendingMessage = false;
|
|
6708
|
+
this.isLoadingDraft = false;
|
|
6681
6709
|
this.closePollComposer = () => {
|
|
6682
6710
|
this.isComposerOpen = false;
|
|
6683
6711
|
};
|
|
6684
6712
|
this.addPoll = (pollId) => {
|
|
6685
6713
|
this.isComposerOpen = false;
|
|
6686
6714
|
this.pollId = pollId;
|
|
6715
|
+
this.updateMessageDraft();
|
|
6687
6716
|
void this.messageSent();
|
|
6688
6717
|
};
|
|
6689
6718
|
this.textareaPlaceholder = this.defaultTextareaPlaceholder;
|
|
@@ -6693,12 +6722,23 @@ class MessageInputComponent {
|
|
|
6693
6722
|
this.hideNotification = undefined;
|
|
6694
6723
|
}
|
|
6695
6724
|
}));
|
|
6725
|
+
this.subscriptions.push(this.attachmentService.attachmentUploads$
|
|
6726
|
+
.pipe(distinctUntilChanged((prev, current) => prev.filter((v) => v.state === 'success').length ===
|
|
6727
|
+
current.filter((v) => v.state === 'success').length))
|
|
6728
|
+
.subscribe(() => {
|
|
6729
|
+
this.updateMessageDraft();
|
|
6730
|
+
}));
|
|
6731
|
+
this.subscriptions.push(this.attachmentService.customAttachments$.subscribe(() => {
|
|
6732
|
+
this.updateMessageDraft();
|
|
6733
|
+
}));
|
|
6696
6734
|
this.subscriptions.push(this.channelService.activeChannel$.subscribe((channel) => {
|
|
6697
6735
|
if (channel && this.channel && channel.id !== this.channel.id) {
|
|
6698
6736
|
this.textareaValue = '';
|
|
6699
6737
|
this.attachmentService.resetAttachmentUploads();
|
|
6700
6738
|
this.pollId = undefined;
|
|
6701
6739
|
this.voiceRecorderService.isRecorderVisible$.next(false);
|
|
6740
|
+
// Preemptively deselect quoted message, to avoid unwanted draft emission
|
|
6741
|
+
this.channelService.selectMessageToQuote(undefined);
|
|
6702
6742
|
}
|
|
6703
6743
|
const capabilities = channel?.data?.own_capabilities;
|
|
6704
6744
|
if (capabilities) {
|
|
@@ -6710,12 +6750,16 @@ class MessageInputComponent {
|
|
|
6710
6750
|
this.setCanSendMessages();
|
|
6711
6751
|
}
|
|
6712
6752
|
}));
|
|
6753
|
+
this.subscriptions.push(this.channelService.channelSwitchState$.subscribe((state) => {
|
|
6754
|
+
this.isChannelChangeResetInProgress = state === 'start';
|
|
6755
|
+
}));
|
|
6713
6756
|
this.subscriptions.push(this.channelService.messageToQuote$.subscribe((m) => {
|
|
6714
6757
|
const isThreadReply = m && m.parent_id;
|
|
6715
6758
|
if ((this.mode === 'thread' && isThreadReply) ||
|
|
6716
6759
|
(this.mode === 'thread' && this.quotedMessage && !m) ||
|
|
6717
6760
|
(this.mode === 'main' && !isThreadReply)) {
|
|
6718
6761
|
this.quotedMessage = m;
|
|
6762
|
+
this.updateMessageDraft();
|
|
6719
6763
|
}
|
|
6720
6764
|
}));
|
|
6721
6765
|
this.subscriptions.push(this.messageActionsService.messageToEdit$.subscribe((message) => {
|
|
@@ -6825,6 +6869,7 @@ class MessageInputComponent {
|
|
|
6825
6869
|
if (this.isCooldownInProgress) {
|
|
6826
6870
|
return;
|
|
6827
6871
|
}
|
|
6872
|
+
this.isSendingMessage = true;
|
|
6828
6873
|
let attachmentUploadInProgressCounter;
|
|
6829
6874
|
this.attachmentService.attachmentUploadInProgressCounter$
|
|
6830
6875
|
.pipe(first())
|
|
@@ -6875,10 +6920,15 @@ class MessageInputComponent {
|
|
|
6875
6920
|
}
|
|
6876
6921
|
}
|
|
6877
6922
|
catch (error) {
|
|
6923
|
+
this.isSendingMessage = false;
|
|
6878
6924
|
if (this.isUpdate) {
|
|
6879
6925
|
this.notificationService.addTemporaryNotification('streamChat.Edit message request failed');
|
|
6880
6926
|
}
|
|
6881
6927
|
}
|
|
6928
|
+
finally {
|
|
6929
|
+
this.isSendingMessage = false;
|
|
6930
|
+
this.updateMessageDraft();
|
|
6931
|
+
}
|
|
6882
6932
|
void this.channelService.typingStopped(this.parentMessageId);
|
|
6883
6933
|
if (this.quotedMessage) {
|
|
6884
6934
|
this.deselectMessageToQuote();
|
|
@@ -6957,6 +7007,39 @@ class MessageInputComponent {
|
|
|
6957
7007
|
openPollComposer() {
|
|
6958
7008
|
this.isComposerOpen = true;
|
|
6959
7009
|
}
|
|
7010
|
+
userMentionsChanged(userMentions) {
|
|
7011
|
+
if (userMentions.map((u) => u.id).join(',') !==
|
|
7012
|
+
this.mentionedUsers.map((u) => u.id).join(',')) {
|
|
7013
|
+
this.mentionedUsers = userMentions;
|
|
7014
|
+
this.updateMessageDraft();
|
|
7015
|
+
}
|
|
7016
|
+
}
|
|
7017
|
+
updateMessageDraft() {
|
|
7018
|
+
if (this.isLoadingDraft ||
|
|
7019
|
+
this.isSendingMessage ||
|
|
7020
|
+
this.isChannelChangeResetInProgress ||
|
|
7021
|
+
this.isUpdate) {
|
|
7022
|
+
return;
|
|
7023
|
+
}
|
|
7024
|
+
const attachments = this.attachmentService.mapToAttachments();
|
|
7025
|
+
if (!this.textareaValue &&
|
|
7026
|
+
!this.mentionedUsers.length &&
|
|
7027
|
+
!attachments?.length &&
|
|
7028
|
+
!this.pollId &&
|
|
7029
|
+
!this.quotedMessage?.id) {
|
|
7030
|
+
this.messageDraftChange.emit(undefined);
|
|
7031
|
+
}
|
|
7032
|
+
else {
|
|
7033
|
+
this.messageDraftChange.emit({
|
|
7034
|
+
text: this.textareaValue,
|
|
7035
|
+
attachments: this.attachmentService.mapToAttachments(),
|
|
7036
|
+
mentioned_users: this.mentionedUsers.map((user) => user.id),
|
|
7037
|
+
poll_id: this.pollId,
|
|
7038
|
+
parent_id: this.parentMessageId,
|
|
7039
|
+
quoted_message_id: this.quotedMessage?.id,
|
|
7040
|
+
});
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
6960
7043
|
async voiceRecordingReady(recording) {
|
|
6961
7044
|
try {
|
|
6962
7045
|
await this.attachmentService.uploadVoiceRecording(recording);
|
|
@@ -6971,6 +7054,29 @@ class MessageInputComponent {
|
|
|
6971
7054
|
get isUpdate() {
|
|
6972
7055
|
return !!this.message;
|
|
6973
7056
|
}
|
|
7057
|
+
/**
|
|
7058
|
+
*
|
|
7059
|
+
* @param draft DraftResponse to load into the message input.
|
|
7060
|
+
* - Draft messages are only supported for new messages, input is ignored in edit mode (if `message` input is set).
|
|
7061
|
+
* - If channel id doesn't match the active channel id, the draft is ignored.
|
|
7062
|
+
* - If a thread message is loaded, and the input isn't in thread mode or parent ids don't match, the draft is ignored.
|
|
7063
|
+
*/
|
|
7064
|
+
loadDraft(draft) {
|
|
7065
|
+
if (this.channel?.cid !== draft.channel_cid ||
|
|
7066
|
+
draft?.message?.parent_id !== this.parentMessageId ||
|
|
7067
|
+
this.isUpdate) {
|
|
7068
|
+
return;
|
|
7069
|
+
}
|
|
7070
|
+
this.isLoadingDraft = true;
|
|
7071
|
+
this.textareaValue = draft.message?.text || '';
|
|
7072
|
+
this.mentionedUsers =
|
|
7073
|
+
draft?.message?.mentioned_users?.map((id) => ({ id })) || [];
|
|
7074
|
+
this.pollId = draft?.message?.poll_id;
|
|
7075
|
+
this.attachmentService.resetAttachmentUploads();
|
|
7076
|
+
this.attachmentService.createFromAttachments(draft?.message?.attachments || []);
|
|
7077
|
+
this.channelService.selectMessageToQuote(draft.quoted_message);
|
|
7078
|
+
this.isLoadingDraft = false;
|
|
7079
|
+
}
|
|
6974
7080
|
deleteUpload(upload) {
|
|
6975
7081
|
if (this.isUpdate) {
|
|
6976
7082
|
// Delay delete to avoid modal detecting this click as outside click
|
|
@@ -7074,10 +7180,10 @@ class MessageInputComponent {
|
|
|
7074
7180
|
}
|
|
7075
7181
|
}
|
|
7076
7182
|
MessageInputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: MessageInputComponent, deps: [{ token: ChannelService }, { token: NotificationService }, { token: AttachmentService }, { token: MessageInputConfigService }, { token: textareaInjectionToken }, { token: i0.ComponentFactoryResolver }, { token: i0.ChangeDetectorRef }, { token: EmojiInputService }, { token: CustomTemplatesService }, { token: MessageActionsService }, { token: VoiceRecorderService }, { token: AudioRecorderService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
7077
|
-
MessageInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.4", type: MessageInputComponent, selector: "stream-message-input", inputs: { isFileUploadEnabled: "isFileUploadEnabled", areMentionsEnabled: "areMentionsEnabled", mentionScope: "mentionScope", mode: "mode", isMultipleFileUploadEnabled: "isMultipleFileUploadEnabled", message: "message", sendMessage$: "sendMessage$", inputMode: "inputMode", autoFocus: "autoFocus", watchForMessageToEdit: "watchForMessageToEdit", displaySendButton: "displaySendButton", displayVoiceRecordingButton: "displayVoiceRecordingButton", displayPollCreateButton: "displayPollCreateButton" }, outputs: { messageUpdate: "messageUpdate" }, host: { properties: { "class": "this.class" } }, providers: [AttachmentService, EmojiInputService, VoiceRecorderService], queries: [{ propertyName: "voiceRecorderRef", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }, { propertyName: "textareaAnchor", first: true, predicate: TextareaDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"str-chat__message-input str-chat-angular__message-input\"\n [style.display]=\"isVoiceRecording ? 'none' : 'flex'\"\n>\n <div *ngIf=\"quotedMessage\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Reply to Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToQuote()\"\n (keyup.enter)=\"deselectMessageToQuote()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <div *ngIf=\"isUpdate\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Edit Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToEdit()\"\n (keyup.enter)=\"deselectMessageToEdit()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <ng-container *ngIf=\"canSendMessages; else notAllowed\">\n <div\n class=\"str-chat__message-input-inner str-chat-angular__message-input-inner\"\n >\n <ng-content select=\"[message-input-start]\"></ng-content>\n <ng-container\n *ngIf=\"isFileUploadEnabled && isFileUploadAuthorized && canSendMessages\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customAttachmentUploadTemplate || defaultAttachmentUpload;\n context: getAttachmentUploadContext()\n \"\n ></ng-container>\n <ng-template #defaultAttachmentUpload>\n <div\n class=\"str-chat__file-input-container\"\n data-testid=\"file-upload-button\"\n >\n <input\n #fileInput\n type=\"file\"\n class=\"str-chat__file-input\"\n data-testid=\"file-input\"\n [multiple]=\"isMultipleFileUploadEnabled\"\n id=\"{{ fileInputId }}\"\n [disabled]=\"\n (attachmentService.attachmentsCounter$ | async)! >=\n attachmentService.maxNumberOfAttachments\n \"\n (change)=\"filesSelected(fileInput.files)\"\n />\n <label class=\"str-chat__file-input-label\" for=\"{{ fileInputId }}\">\n <stream-icon-placeholder icon=\"attach\"></stream-icon-placeholder>\n </label>\n </div>\n </ng-template>\n </ng-container>\n <ng-container\n *ngIf=\"canSendPolls && displayPollCreateButton && mode !== 'thread'\"\n >\n <button\n class=\"str-chat-angular__create-poll\"\n (click)=\"openPollComposer()\"\n >\n <stream-icon-placeholder icon=\"poll\"></stream-icon-placeholder>\n </button>\n </ng-container>\n <div class=\"str-chat__message-textarea-container\">\n <div\n *ngIf=\"quotedMessage\"\n data-testid=\"quoted-message-container\"\n class=\"str-chat__quoted-message-preview\"\n >\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n type=\"user\"\n location=\"quoted-message-sender\"\n [imageUrl]=\"quotedMessage.user?.image\"\n [name]=\"quotedMessage.user?.name || quotedMessage.user?.id\"\n [user]=\"quotedMessage.user || undefined\"\n ></stream-avatar-placeholder>\n <div\n class=\"quoted-message-preview-content-inner str-chat__quoted-message-bubble\"\n >\n <stream-attachment-list\n *ngIf=\"\n quotedMessage?.attachments && quotedMessage?.attachments?.length\n \"\n [attachments]=\"quotedMessageAttachments\"\n [messageId]=\"quotedMessage.id\"\n ></stream-attachment-list>\n <ng-container\n *ngIf=\"\n quotedMessage?.poll_id &&\n (customTemplatesService.pollTemplate$ | async)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.pollTemplate$ | async)!;\n context: {\n pollId: quotedMessage?.poll_id,\n messageId: quotedMessage?.id,\n isQuote: true\n }\n \"\n ></ng-container>\n </ng-container>\n <div class=\"str-chat__quoted-message-text\">\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageTextTemplate$ | async) ||\n defaultText;\n context: getQuotedMessageTextContext()\n \"\n ></ng-container>\n <ng-template\n #defaultText\n let-message=\"message\"\n let-isQuoted=\"isQuoted\"\n let-shouldTranslate=\"shouldTranslate\"\n >\n <stream-message-text\n [message]=\"message\"\n [isQuoted]=\"isQuoted\"\n [shouldTranslate]=\"shouldTranslate\"\n data-testid=\"quoted-message-text\"\n ></stream-message-text>\n </ng-template>\n </div>\n </div>\n </div>\n <ng-template\n #defaultAttachmentsPreview\n let-attachmentUploads$=\"attachmentUploads$\"\n let-retryUploadHandler=\"retryUploadHandler\"\n let-deleteUploadHandler=\"deleteUploadHandler\"\n >\n <stream-attachment-preview-list\n class=\"str-chat__attachment-preview-list-angular-host\"\n [attachmentUploads$]=\"attachmentUploads$\"\n (retryAttachmentUpload)=\"retryUploadHandler($event)\"\n (deleteAttachment)=\"deleteUploadHandler($event)\"\n ></stream-attachment-preview-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n attachmentPreviewListTemplate || defaultAttachmentsPreview;\n context: getAttachmentPreviewListContext()\n \"\n ></ng-container>\n <div class=\"str-chat__message-textarea-with-emoji-picker\">\n <ng-container\n streamTextarea\n [componentRef]=\"textareaRef\"\n [areMentionsEnabled]=\"areMentionsEnabled\"\n [mentionScope]=\"mentionScope\"\n [inputMode]=\"inputMode\"\n [autoFocus]=\"autoFocus\"\n [placeholder]=\"textareaPlaceholder\"\n [(value)]=\"textareaValue\"\n (valueChange)=\"typingStart$.next()\"\n (send)=\"messageSent()\"\n (userMentions)=\"mentionedUsers = $event\"\n (pasteFromClipboard)=\"itemsPasted($event)\"\n ></ng-container>\n <ng-container *ngIf=\"emojiPickerTemplate\" data-testid=\"emoji-picker\">\n <ng-container\n *ngTemplateOutlet=\"\n emojiPickerTemplate;\n context: getEmojiPickerContext()\n \"\n ></ng-container>\n </ng-container>\n </div>\n </div>\n <button\n *ngIf=\"canSendMessages && !isCooldownInProgress && displaySendButton\"\n data-testid=\"send-button\"\n class=\"str-chat__send-button\"\n [disabled]=\"\n (attachmentUploadInProgressCounter$ | async)! > 0 ||\n (attachmentService.attachmentsCounter$ | async)! >\n attachmentService.maxNumberOfAttachments ||\n (!textareaValue &&\n (attachmentUploads$ | async)!.length === 0 &&\n (customAttachments$ | async)!.length === 0)\n \"\n (click)=\"messageSent()\"\n (keyup.enter)=\"messageSent()\"\n >\n <stream-icon-placeholder icon=\"send\"></stream-icon-placeholder>\n </button>\n <div\n *ngIf=\"isCooldownInProgress\"\n class=\"str-chat__message-input-cooldown\"\n data-testid=\"cooldown-timer\"\n >\n {{ cooldown$ | async }}\n </div>\n <button\n *ngIf=\"displayVoiceRecordingButton\"\n class=\"str-chat__start-recording-audio-button\"\n data-testid=\"start-voice-recording\"\n [disabled]=\"\n voiceRecorderService.isRecorderVisible$.value ||\n audioRecorder?.isRecording ||\n (attachmentService.attachmentsCounter$ | async)! >=\n attachmentService.maxNumberOfAttachments\n \"\n (click)=\"startVoiceRecording()\"\n (keyup.enter)=\"startVoiceRecording()\"\n >\n <stream-icon-placeholder icon=\"mic\"></stream-icon-placeholder>\n </button>\n <ng-content select=\"[message-input-end]\"></ng-content>\n </div>\n </ng-container>\n <ng-template #notAllowed>\n <div\n class=\"str-chat__message-input-not-allowed\"\n data-testid=\"disabled-textarea\"\n >\n {{ disabledTextareaText | translate }}\n </div>\n </ng-template>\n</div>\n<ng-template\n *ngIf=\"voiceRecorderRef\"\n [ngTemplateOutlet]=\"voiceRecorderRef\"\n [ngTemplateOutletContext]=\"{ service: voiceRecorderService }\"\n></ng-template>\n<ng-template\n *ngIf=\"\n isComposerOpen && (customTemplatesService.pollComposerTemplate$ | async)\n \"\n [ngTemplateOutlet]=\"(customTemplatesService.pollComposerTemplate$ | async)!\"\n [ngTemplateOutletContext]=\"{\n pollCompose: addPoll,\n cancel: closePollComposer\n }\"\n></ng-template>\n", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AvatarPlaceholderComponent, selector: "stream-avatar-placeholder", inputs: ["name", "imageUrl", "location", "channel", "user", "type", "initialsType", "showOnlineIndicator"] }, { kind: "component", type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon"] }, { kind: "component", type: AttachmentListComponent, selector: "stream-attachment-list", inputs: ["messageId", "parentMessageId", "attachments"], outputs: ["imageModalStateChange"] }, { kind: "component", type: AttachmentPreviewListComponent, selector: "stream-attachment-preview-list", inputs: ["attachmentUploads$"], outputs: ["retryAttachmentUpload", "deleteAttachment"] }, { kind: "directive", type: TextareaDirective, selector: "[streamTextarea]", inputs: ["componentRef", "areMentionsEnabled", "mentionScope", "inputMode", "value", "placeholder", "autoFocus"], outputs: ["valueChange", "send", "userMentions", "pasteFromClipboard"] }, { kind: "component", type: MessageTextComponent, selector: "stream-message-text", inputs: ["message", "isQuoted", "shouldTranslate"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.TranslatePipe, name: "translate" }] });
|
|
7183
|
+
MessageInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.4", type: MessageInputComponent, selector: "stream-message-input", inputs: { isFileUploadEnabled: "isFileUploadEnabled", areMentionsEnabled: "areMentionsEnabled", mentionScope: "mentionScope", mode: "mode", isMultipleFileUploadEnabled: "isMultipleFileUploadEnabled", message: "message", sendMessage$: "sendMessage$", inputMode: "inputMode", autoFocus: "autoFocus", watchForMessageToEdit: "watchForMessageToEdit", displaySendButton: "displaySendButton", displayVoiceRecordingButton: "displayVoiceRecordingButton", displayPollCreateButton: "displayPollCreateButton" }, outputs: { messageUpdate: "messageUpdate", messageDraftChange: "messageDraftChange" }, host: { properties: { "class": "this.class" } }, providers: [AttachmentService, EmojiInputService, VoiceRecorderService], queries: [{ propertyName: "voiceRecorderRef", first: true, predicate: TemplateRef, descendants: true }], viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }, { propertyName: "textareaAnchor", first: true, predicate: TextareaDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"str-chat__message-input str-chat-angular__message-input\"\n [style.display]=\"isVoiceRecording ? 'none' : 'flex'\"\n>\n <div *ngIf=\"quotedMessage\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Reply to Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToQuote()\"\n (keyup.enter)=\"deselectMessageToQuote()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <div *ngIf=\"isUpdate\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Edit Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToEdit()\"\n (keyup.enter)=\"deselectMessageToEdit()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <ng-container *ngIf=\"canSendMessages; else notAllowed\">\n <div\n class=\"str-chat__message-input-inner str-chat-angular__message-input-inner\"\n >\n <ng-content select=\"[message-input-start]\"></ng-content>\n <ng-container\n *ngIf=\"isFileUploadEnabled && isFileUploadAuthorized && canSendMessages\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customAttachmentUploadTemplate || defaultAttachmentUpload;\n context: getAttachmentUploadContext()\n \"\n ></ng-container>\n <ng-template #defaultAttachmentUpload>\n <div\n class=\"str-chat__file-input-container\"\n data-testid=\"file-upload-button\"\n >\n <input\n #fileInput\n type=\"file\"\n class=\"str-chat__file-input\"\n data-testid=\"file-input\"\n [multiple]=\"isMultipleFileUploadEnabled\"\n id=\"{{ fileInputId }}\"\n [disabled]=\"\n (attachmentService.attachmentsCounter$ | async)! >=\n attachmentService.maxNumberOfAttachments\n \"\n (change)=\"filesSelected(fileInput.files)\"\n />\n <label class=\"str-chat__file-input-label\" for=\"{{ fileInputId }}\">\n <stream-icon-placeholder icon=\"attach\"></stream-icon-placeholder>\n </label>\n </div>\n </ng-template>\n </ng-container>\n <ng-container\n *ngIf=\"canSendPolls && displayPollCreateButton && mode !== 'thread'\"\n >\n <button\n class=\"str-chat-angular__create-poll\"\n (click)=\"openPollComposer()\"\n >\n <stream-icon-placeholder icon=\"poll\"></stream-icon-placeholder>\n </button>\n </ng-container>\n <div class=\"str-chat__message-textarea-container\">\n <div\n *ngIf=\"quotedMessage\"\n data-testid=\"quoted-message-container\"\n class=\"str-chat__quoted-message-preview\"\n >\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n type=\"user\"\n location=\"quoted-message-sender\"\n [imageUrl]=\"quotedMessage.user?.image\"\n [name]=\"quotedMessage.user?.name || quotedMessage.user?.id\"\n [user]=\"quotedMessage.user || undefined\"\n ></stream-avatar-placeholder>\n <div\n class=\"quoted-message-preview-content-inner str-chat__quoted-message-bubble\"\n >\n <stream-attachment-list\n *ngIf=\"\n quotedMessage?.attachments && quotedMessage?.attachments?.length\n \"\n [attachments]=\"quotedMessageAttachments\"\n [messageId]=\"quotedMessage.id\"\n ></stream-attachment-list>\n <ng-container\n *ngIf=\"\n quotedMessage?.poll_id &&\n (customTemplatesService.pollTemplate$ | async)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.pollTemplate$ | async)!;\n context: {\n pollId: quotedMessage?.poll_id,\n messageId: quotedMessage?.id,\n isQuote: true\n }\n \"\n ></ng-container>\n </ng-container>\n <div class=\"str-chat__quoted-message-text\">\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageTextTemplate$ | async) ||\n defaultText;\n context: getQuotedMessageTextContext()\n \"\n ></ng-container>\n <ng-template\n #defaultText\n let-message=\"message\"\n let-isQuoted=\"isQuoted\"\n let-shouldTranslate=\"shouldTranslate\"\n >\n <stream-message-text\n [message]=\"message\"\n [isQuoted]=\"isQuoted\"\n [shouldTranslate]=\"shouldTranslate\"\n data-testid=\"quoted-message-text\"\n ></stream-message-text>\n </ng-template>\n </div>\n </div>\n </div>\n <ng-template\n #defaultAttachmentsPreview\n let-attachmentUploads$=\"attachmentUploads$\"\n let-retryUploadHandler=\"retryUploadHandler\"\n let-deleteUploadHandler=\"deleteUploadHandler\"\n >\n <stream-attachment-preview-list\n class=\"str-chat__attachment-preview-list-angular-host\"\n [attachmentUploads$]=\"attachmentUploads$\"\n (retryAttachmentUpload)=\"retryUploadHandler($event)\"\n (deleteAttachment)=\"deleteUploadHandler($event)\"\n ></stream-attachment-preview-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n attachmentPreviewListTemplate || defaultAttachmentsPreview;\n context: getAttachmentPreviewListContext()\n \"\n ></ng-container>\n <div class=\"str-chat__message-textarea-with-emoji-picker\">\n <ng-container\n streamTextarea\n [componentRef]=\"textareaRef\"\n [areMentionsEnabled]=\"areMentionsEnabled\"\n [mentionScope]=\"mentionScope\"\n [inputMode]=\"inputMode\"\n [autoFocus]=\"autoFocus\"\n [placeholder]=\"textareaPlaceholder\"\n [(value)]=\"textareaValue\"\n (valueChange)=\"typingStart$.next(); updateMessageDraft()\"\n (send)=\"messageSent()\"\n (userMentions)=\"userMentionsChanged($event)\"\n (pasteFromClipboard)=\"itemsPasted($event)\"\n ></ng-container>\n <ng-container *ngIf=\"emojiPickerTemplate\" data-testid=\"emoji-picker\">\n <ng-container\n *ngTemplateOutlet=\"\n emojiPickerTemplate;\n context: getEmojiPickerContext()\n \"\n ></ng-container>\n </ng-container>\n </div>\n </div>\n <button\n *ngIf=\"canSendMessages && !isCooldownInProgress && displaySendButton\"\n data-testid=\"send-button\"\n class=\"str-chat__send-button\"\n [disabled]=\"\n (attachmentUploadInProgressCounter$ | async)! > 0 ||\n (attachmentService.attachmentsCounter$ | async)! >\n attachmentService.maxNumberOfAttachments ||\n (!textareaValue &&\n (attachmentUploads$ | async)!.length === 0 &&\n (customAttachments$ | async)!.length === 0)\n \"\n (click)=\"messageSent()\"\n (keyup.enter)=\"messageSent()\"\n >\n <stream-icon-placeholder icon=\"send\"></stream-icon-placeholder>\n </button>\n <div\n *ngIf=\"isCooldownInProgress\"\n class=\"str-chat__message-input-cooldown\"\n data-testid=\"cooldown-timer\"\n >\n {{ cooldown$ | async }}\n </div>\n <button\n *ngIf=\"displayVoiceRecordingButton\"\n class=\"str-chat__start-recording-audio-button\"\n data-testid=\"start-voice-recording\"\n [disabled]=\"\n voiceRecorderService.isRecorderVisible$.value ||\n audioRecorder?.isRecording ||\n (attachmentService.attachmentsCounter$ | async)! >=\n attachmentService.maxNumberOfAttachments\n \"\n (click)=\"startVoiceRecording()\"\n (keyup.enter)=\"startVoiceRecording()\"\n >\n <stream-icon-placeholder icon=\"mic\"></stream-icon-placeholder>\n </button>\n <ng-content select=\"[message-input-end]\"></ng-content>\n </div>\n </ng-container>\n <ng-template #notAllowed>\n <div\n class=\"str-chat__message-input-not-allowed\"\n data-testid=\"disabled-textarea\"\n >\n {{ disabledTextareaText | translate }}\n </div>\n </ng-template>\n</div>\n<ng-template\n *ngIf=\"voiceRecorderRef\"\n [ngTemplateOutlet]=\"voiceRecorderRef\"\n [ngTemplateOutletContext]=\"{ service: voiceRecorderService }\"\n></ng-template>\n<ng-template\n *ngIf=\"\n isComposerOpen && (customTemplatesService.pollComposerTemplate$ | async)\n \"\n [ngTemplateOutlet]=\"(customTemplatesService.pollComposerTemplate$ | async)!\"\n [ngTemplateOutletContext]=\"{\n pollCompose: addPoll,\n cancel: closePollComposer\n }\"\n></ng-template>\n", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AvatarPlaceholderComponent, selector: "stream-avatar-placeholder", inputs: ["name", "imageUrl", "location", "channel", "user", "type", "initialsType", "showOnlineIndicator"] }, { kind: "component", type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon"] }, { kind: "component", type: AttachmentListComponent, selector: "stream-attachment-list", inputs: ["messageId", "parentMessageId", "attachments"], outputs: ["imageModalStateChange"] }, { kind: "component", type: AttachmentPreviewListComponent, selector: "stream-attachment-preview-list", inputs: ["attachmentUploads$"], outputs: ["retryAttachmentUpload", "deleteAttachment"] }, { kind: "directive", type: TextareaDirective, selector: "[streamTextarea]", inputs: ["componentRef", "areMentionsEnabled", "mentionScope", "inputMode", "value", "placeholder", "autoFocus"], outputs: ["valueChange", "send", "userMentions", "pasteFromClipboard"] }, { kind: "component", type: MessageTextComponent, selector: "stream-message-text", inputs: ["message", "isQuoted", "shouldTranslate"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.TranslatePipe, name: "translate" }] });
|
|
7078
7184
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.4", ngImport: i0, type: MessageInputComponent, decorators: [{
|
|
7079
7185
|
type: Component,
|
|
7080
|
-
args: [{ selector: 'stream-message-input', providers: [AttachmentService, EmojiInputService, VoiceRecorderService], template: "<div\n class=\"str-chat__message-input str-chat-angular__message-input\"\n [style.display]=\"isVoiceRecording ? 'none' : 'flex'\"\n>\n <div *ngIf=\"quotedMessage\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Reply to Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToQuote()\"\n (keyup.enter)=\"deselectMessageToQuote()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <div *ngIf=\"isUpdate\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Edit Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToEdit()\"\n (keyup.enter)=\"deselectMessageToEdit()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <ng-container *ngIf=\"canSendMessages; else notAllowed\">\n <div\n class=\"str-chat__message-input-inner str-chat-angular__message-input-inner\"\n >\n <ng-content select=\"[message-input-start]\"></ng-content>\n <ng-container\n *ngIf=\"isFileUploadEnabled && isFileUploadAuthorized && canSendMessages\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customAttachmentUploadTemplate || defaultAttachmentUpload;\n context: getAttachmentUploadContext()\n \"\n ></ng-container>\n <ng-template #defaultAttachmentUpload>\n <div\n class=\"str-chat__file-input-container\"\n data-testid=\"file-upload-button\"\n >\n <input\n #fileInput\n type=\"file\"\n class=\"str-chat__file-input\"\n data-testid=\"file-input\"\n [multiple]=\"isMultipleFileUploadEnabled\"\n id=\"{{ fileInputId }}\"\n [disabled]=\"\n (attachmentService.attachmentsCounter$ | async)! >=\n attachmentService.maxNumberOfAttachments\n \"\n (change)=\"filesSelected(fileInput.files)\"\n />\n <label class=\"str-chat__file-input-label\" for=\"{{ fileInputId }}\">\n <stream-icon-placeholder icon=\"attach\"></stream-icon-placeholder>\n </label>\n </div>\n </ng-template>\n </ng-container>\n <ng-container\n *ngIf=\"canSendPolls && displayPollCreateButton && mode !== 'thread'\"\n >\n <button\n class=\"str-chat-angular__create-poll\"\n (click)=\"openPollComposer()\"\n >\n <stream-icon-placeholder icon=\"poll\"></stream-icon-placeholder>\n </button>\n </ng-container>\n <div class=\"str-chat__message-textarea-container\">\n <div\n *ngIf=\"quotedMessage\"\n data-testid=\"quoted-message-container\"\n class=\"str-chat__quoted-message-preview\"\n >\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n type=\"user\"\n location=\"quoted-message-sender\"\n [imageUrl]=\"quotedMessage.user?.image\"\n [name]=\"quotedMessage.user?.name || quotedMessage.user?.id\"\n [user]=\"quotedMessage.user || undefined\"\n ></stream-avatar-placeholder>\n <div\n class=\"quoted-message-preview-content-inner str-chat__quoted-message-bubble\"\n >\n <stream-attachment-list\n *ngIf=\"\n quotedMessage?.attachments && quotedMessage?.attachments?.length\n \"\n [attachments]=\"quotedMessageAttachments\"\n [messageId]=\"quotedMessage.id\"\n ></stream-attachment-list>\n <ng-container\n *ngIf=\"\n quotedMessage?.poll_id &&\n (customTemplatesService.pollTemplate$ | async)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.pollTemplate$ | async)!;\n context: {\n pollId: quotedMessage?.poll_id,\n messageId: quotedMessage?.id,\n isQuote: true\n }\n \"\n ></ng-container>\n </ng-container>\n <div class=\"str-chat__quoted-message-text\">\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageTextTemplate$ | async) ||\n defaultText;\n context: getQuotedMessageTextContext()\n \"\n ></ng-container>\n <ng-template\n #defaultText\n let-message=\"message\"\n let-isQuoted=\"isQuoted\"\n let-shouldTranslate=\"shouldTranslate\"\n >\n <stream-message-text\n [message]=\"message\"\n [isQuoted]=\"isQuoted\"\n [shouldTranslate]=\"shouldTranslate\"\n data-testid=\"quoted-message-text\"\n ></stream-message-text>\n </ng-template>\n </div>\n </div>\n </div>\n <ng-template\n #defaultAttachmentsPreview\n let-attachmentUploads$=\"attachmentUploads$\"\n let-retryUploadHandler=\"retryUploadHandler\"\n let-deleteUploadHandler=\"deleteUploadHandler\"\n >\n <stream-attachment-preview-list\n class=\"str-chat__attachment-preview-list-angular-host\"\n [attachmentUploads$]=\"attachmentUploads$\"\n (retryAttachmentUpload)=\"retryUploadHandler($event)\"\n (deleteAttachment)=\"deleteUploadHandler($event)\"\n ></stream-attachment-preview-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n attachmentPreviewListTemplate || defaultAttachmentsPreview;\n context: getAttachmentPreviewListContext()\n \"\n ></ng-container>\n <div class=\"str-chat__message-textarea-with-emoji-picker\">\n <ng-container\n streamTextarea\n [componentRef]=\"textareaRef\"\n [areMentionsEnabled]=\"areMentionsEnabled\"\n [mentionScope]=\"mentionScope\"\n [inputMode]=\"inputMode\"\n [autoFocus]=\"autoFocus\"\n [placeholder]=\"textareaPlaceholder\"\n [(value)]=\"textareaValue\"\n (valueChange)=\"typingStart$.next()\"\n (send)=\"messageSent()\"\n (userMentions)=\"
|
|
7186
|
+
args: [{ selector: 'stream-message-input', providers: [AttachmentService, EmojiInputService, VoiceRecorderService], template: "<div\n class=\"str-chat__message-input str-chat-angular__message-input\"\n [style.display]=\"isVoiceRecording ? 'none' : 'flex'\"\n>\n <div *ngIf=\"quotedMessage\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Reply to Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToQuote()\"\n (keyup.enter)=\"deselectMessageToQuote()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <div *ngIf=\"isUpdate\" class=\"str-chat__quoted-message-preview-header\">\n <div class=\"str-chat__quoted-message-reply-to-message\">\n {{ \"streamChat.Edit Message\" | translate }}\n </div>\n <button\n class=\"str-chat__quoted-message-remove\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToEdit()\"\n (keyup.enter)=\"deselectMessageToEdit()\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </button>\n </div>\n <ng-container *ngIf=\"canSendMessages; else notAllowed\">\n <div\n class=\"str-chat__message-input-inner str-chat-angular__message-input-inner\"\n >\n <ng-content select=\"[message-input-start]\"></ng-content>\n <ng-container\n *ngIf=\"isFileUploadEnabled && isFileUploadAuthorized && canSendMessages\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n customAttachmentUploadTemplate || defaultAttachmentUpload;\n context: getAttachmentUploadContext()\n \"\n ></ng-container>\n <ng-template #defaultAttachmentUpload>\n <div\n class=\"str-chat__file-input-container\"\n data-testid=\"file-upload-button\"\n >\n <input\n #fileInput\n type=\"file\"\n class=\"str-chat__file-input\"\n data-testid=\"file-input\"\n [multiple]=\"isMultipleFileUploadEnabled\"\n id=\"{{ fileInputId }}\"\n [disabled]=\"\n (attachmentService.attachmentsCounter$ | async)! >=\n attachmentService.maxNumberOfAttachments\n \"\n (change)=\"filesSelected(fileInput.files)\"\n />\n <label class=\"str-chat__file-input-label\" for=\"{{ fileInputId }}\">\n <stream-icon-placeholder icon=\"attach\"></stream-icon-placeholder>\n </label>\n </div>\n </ng-template>\n </ng-container>\n <ng-container\n *ngIf=\"canSendPolls && displayPollCreateButton && mode !== 'thread'\"\n >\n <button\n class=\"str-chat-angular__create-poll\"\n (click)=\"openPollComposer()\"\n >\n <stream-icon-placeholder icon=\"poll\"></stream-icon-placeholder>\n </button>\n </ng-container>\n <div class=\"str-chat__message-textarea-container\">\n <div\n *ngIf=\"quotedMessage\"\n data-testid=\"quoted-message-container\"\n class=\"str-chat__quoted-message-preview\"\n >\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host str-chat__message-sender-avatar\"\n type=\"user\"\n location=\"quoted-message-sender\"\n [imageUrl]=\"quotedMessage.user?.image\"\n [name]=\"quotedMessage.user?.name || quotedMessage.user?.id\"\n [user]=\"quotedMessage.user || undefined\"\n ></stream-avatar-placeholder>\n <div\n class=\"quoted-message-preview-content-inner str-chat__quoted-message-bubble\"\n >\n <stream-attachment-list\n *ngIf=\"\n quotedMessage?.attachments && quotedMessage?.attachments?.length\n \"\n [attachments]=\"quotedMessageAttachments\"\n [messageId]=\"quotedMessage.id\"\n ></stream-attachment-list>\n <ng-container\n *ngIf=\"\n quotedMessage?.poll_id &&\n (customTemplatesService.pollTemplate$ | async)\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.pollTemplate$ | async)!;\n context: {\n pollId: quotedMessage?.poll_id,\n messageId: quotedMessage?.id,\n isQuote: true\n }\n \"\n ></ng-container>\n </ng-container>\n <div class=\"str-chat__quoted-message-text\">\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.messageTextTemplate$ | async) ||\n defaultText;\n context: getQuotedMessageTextContext()\n \"\n ></ng-container>\n <ng-template\n #defaultText\n let-message=\"message\"\n let-isQuoted=\"isQuoted\"\n let-shouldTranslate=\"shouldTranslate\"\n >\n <stream-message-text\n [message]=\"message\"\n [isQuoted]=\"isQuoted\"\n [shouldTranslate]=\"shouldTranslate\"\n data-testid=\"quoted-message-text\"\n ></stream-message-text>\n </ng-template>\n </div>\n </div>\n </div>\n <ng-template\n #defaultAttachmentsPreview\n let-attachmentUploads$=\"attachmentUploads$\"\n let-retryUploadHandler=\"retryUploadHandler\"\n let-deleteUploadHandler=\"deleteUploadHandler\"\n >\n <stream-attachment-preview-list\n class=\"str-chat__attachment-preview-list-angular-host\"\n [attachmentUploads$]=\"attachmentUploads$\"\n (retryAttachmentUpload)=\"retryUploadHandler($event)\"\n (deleteAttachment)=\"deleteUploadHandler($event)\"\n ></stream-attachment-preview-list>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n attachmentPreviewListTemplate || defaultAttachmentsPreview;\n context: getAttachmentPreviewListContext()\n \"\n ></ng-container>\n <div class=\"str-chat__message-textarea-with-emoji-picker\">\n <ng-container\n streamTextarea\n [componentRef]=\"textareaRef\"\n [areMentionsEnabled]=\"areMentionsEnabled\"\n [mentionScope]=\"mentionScope\"\n [inputMode]=\"inputMode\"\n [autoFocus]=\"autoFocus\"\n [placeholder]=\"textareaPlaceholder\"\n [(value)]=\"textareaValue\"\n (valueChange)=\"typingStart$.next(); updateMessageDraft()\"\n (send)=\"messageSent()\"\n (userMentions)=\"userMentionsChanged($event)\"\n (pasteFromClipboard)=\"itemsPasted($event)\"\n ></ng-container>\n <ng-container *ngIf=\"emojiPickerTemplate\" data-testid=\"emoji-picker\">\n <ng-container\n *ngTemplateOutlet=\"\n emojiPickerTemplate;\n context: getEmojiPickerContext()\n \"\n ></ng-container>\n </ng-container>\n </div>\n </div>\n <button\n *ngIf=\"canSendMessages && !isCooldownInProgress && displaySendButton\"\n data-testid=\"send-button\"\n class=\"str-chat__send-button\"\n [disabled]=\"\n (attachmentUploadInProgressCounter$ | async)! > 0 ||\n (attachmentService.attachmentsCounter$ | async)! >\n attachmentService.maxNumberOfAttachments ||\n (!textareaValue &&\n (attachmentUploads$ | async)!.length === 0 &&\n (customAttachments$ | async)!.length === 0)\n \"\n (click)=\"messageSent()\"\n (keyup.enter)=\"messageSent()\"\n >\n <stream-icon-placeholder icon=\"send\"></stream-icon-placeholder>\n </button>\n <div\n *ngIf=\"isCooldownInProgress\"\n class=\"str-chat__message-input-cooldown\"\n data-testid=\"cooldown-timer\"\n >\n {{ cooldown$ | async }}\n </div>\n <button\n *ngIf=\"displayVoiceRecordingButton\"\n class=\"str-chat__start-recording-audio-button\"\n data-testid=\"start-voice-recording\"\n [disabled]=\"\n voiceRecorderService.isRecorderVisible$.value ||\n audioRecorder?.isRecording ||\n (attachmentService.attachmentsCounter$ | async)! >=\n attachmentService.maxNumberOfAttachments\n \"\n (click)=\"startVoiceRecording()\"\n (keyup.enter)=\"startVoiceRecording()\"\n >\n <stream-icon-placeholder icon=\"mic\"></stream-icon-placeholder>\n </button>\n <ng-content select=\"[message-input-end]\"></ng-content>\n </div>\n </ng-container>\n <ng-template #notAllowed>\n <div\n class=\"str-chat__message-input-not-allowed\"\n data-testid=\"disabled-textarea\"\n >\n {{ disabledTextareaText | translate }}\n </div>\n </ng-template>\n</div>\n<ng-template\n *ngIf=\"voiceRecorderRef\"\n [ngTemplateOutlet]=\"voiceRecorderRef\"\n [ngTemplateOutletContext]=\"{ service: voiceRecorderService }\"\n></ng-template>\n<ng-template\n *ngIf=\"\n isComposerOpen && (customTemplatesService.pollComposerTemplate$ | async)\n \"\n [ngTemplateOutlet]=\"(customTemplatesService.pollComposerTemplate$ | async)!\"\n [ngTemplateOutletContext]=\"{\n pollCompose: addPoll,\n cancel: closePollComposer\n }\"\n></ng-template>\n" }]
|
|
7081
7187
|
}], ctorParameters: function () { return [{ type: ChannelService }, { type: NotificationService }, { type: AttachmentService }, { type: MessageInputConfigService }, { type: i0.Type, decorators: [{
|
|
7082
7188
|
type: Inject,
|
|
7083
7189
|
args: [textareaInjectionToken]
|
|
@@ -7111,6 +7217,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.4", ngImpor
|
|
|
7111
7217
|
type: Input
|
|
7112
7218
|
}], messageUpdate: [{
|
|
7113
7219
|
type: Output
|
|
7220
|
+
}], messageDraftChange: [{
|
|
7221
|
+
type: Output
|
|
7114
7222
|
}], voiceRecorderRef: [{
|
|
7115
7223
|
type: ContentChild,
|
|
7116
7224
|
args: [TemplateRef]
|
|
@@ -7728,7 +7836,7 @@ class VirtualizedListService {
|
|
|
7728
7836
|
}
|
|
7729
7837
|
}));
|
|
7730
7838
|
this.subscriptions.push(this.scrollPosition$
|
|
7731
|
-
.pipe(distinctUntilChanged())
|
|
7839
|
+
.pipe(distinctUntilChanged$1())
|
|
7732
7840
|
.subscribe((position) => {
|
|
7733
7841
|
if (this.queryStateSubject.getValue().state === `loading-${position}`) {
|
|
7734
7842
|
return;
|