stream-chat-angular 3.0.0-beta.7 → 3.0.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/README.md +44 -12
- package/assets/version.d.ts +1 -1
- package/bundles/stream-chat-angular.umd.js +175 -113
- package/bundles/stream-chat-angular.umd.js.map +1 -1
- package/esm2015/assets/version.js +2 -2
- package/esm2015/lib/attachment-list/attachment-list.component.js +9 -2
- package/esm2015/lib/attachment-preview-list/attachment-preview-list.component.js +2 -2
- package/esm2015/lib/attachment.service.js +20 -5
- package/esm2015/lib/channel.service.js +42 -17
- package/esm2015/lib/message/message.component.js +7 -2
- package/esm2015/lib/message-actions-box/message-actions-box.component.js +7 -8
- package/esm2015/lib/message-input/message-input.component.js +15 -2
- package/esm2015/lib/types.js +1 -1
- package/fesm2015/stream-chat-angular.js +166 -102
- package/fesm2015/stream-chat-angular.js.map +1 -1
- package/lib/attachment-list/attachment-list.component.d.ts +1 -0
- package/lib/channel.service.d.ts +28 -2
- package/lib/message-actions-box/message-actions-box.component.d.ts +3 -2
- package/lib/message-input/message-input.component.d.ts +6 -1
- package/lib/types.d.ts +2 -1
- package/package.json +2 -2
- package/src/assets/styles/css/index.css +1 -1
- package/src/assets/styles/css/index.css.map +1 -1
- package/src/assets/styles/scss/Avatar.scss +1 -1
- package/src/assets/styles/scss/ChannelHeader.scss +2 -0
- package/src/assets/styles/scss/VirtualMessage.scss +5 -0
- package/src/assets/version.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { Injectable, Component, Input,
|
|
3
|
+
import { Injectable, Component, Input, EventEmitter, Output, ViewChild, InjectionToken, Directive, Inject, HostBinding, NgModule } from '@angular/core';
|
|
4
4
|
import { BehaviorSubject, ReplaySubject, combineLatest, Subject, timer, of } from 'rxjs';
|
|
5
5
|
import { StreamChat } from 'stream-chat';
|
|
6
6
|
import { map, shareReplay, filter, first, take, tap, catchError, startWith, distinctUntilChanged, debounceTime } from 'rxjs/operators';
|
|
@@ -17,7 +17,7 @@ import transliterate from '@stream-io/transliterate';
|
|
|
17
17
|
import * as i7 from 'angular-mentions';
|
|
18
18
|
import { MentionModule } from 'angular-mentions';
|
|
19
19
|
|
|
20
|
-
const version = '3.0.0
|
|
20
|
+
const version = '3.0.0';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* The `NotificationService` can be used to add or remove notifications. By default the [`NotificationList`](../components/NotificationListComponent.mdx) component displays the currently active notifications.
|
|
@@ -253,7 +253,7 @@ const getReadBy = (message, channel) => {
|
|
|
253
253
|
};
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
|
-
* The `ChannelService` provides data and interaction for the channel list and message list.
|
|
256
|
+
* The `ChannelService` provides data and interaction for the channel list and message list.
|
|
257
257
|
*/
|
|
258
258
|
class ChannelService {
|
|
259
259
|
constructor(chatClientService, ngZone) {
|
|
@@ -346,6 +346,21 @@ class ChannelService {
|
|
|
346
346
|
this.activeThreadMessagesSubject.next([]);
|
|
347
347
|
this.messageToQuoteSubject.next(undefined);
|
|
348
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Deselects the currently active (if any) channel
|
|
351
|
+
*/
|
|
352
|
+
deselectActiveChannel() {
|
|
353
|
+
const activeChannel = this.activeChannelSubject.getValue();
|
|
354
|
+
if (!activeChannel) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
this.activeChannelMessagesSubject.next([]);
|
|
358
|
+
this.activeChannelSubject.next(undefined);
|
|
359
|
+
this.activeParentMessageIdSubject.next(undefined);
|
|
360
|
+
this.activeThreadMessagesSubject.next([]);
|
|
361
|
+
this.latestMessageDateByUserByChannelsSubject.next({});
|
|
362
|
+
this.selectMessageToQuote(undefined);
|
|
363
|
+
}
|
|
349
364
|
/**
|
|
350
365
|
* Sets the given `message` as an active parent message. If `undefined` is provided, it will deleselect the current parent message.
|
|
351
366
|
* @param message
|
|
@@ -416,8 +431,10 @@ class ChannelService {
|
|
|
416
431
|
* @param filters
|
|
417
432
|
* @param sort
|
|
418
433
|
* @param options
|
|
434
|
+
* @param shouldSetActiveChannel Decides if the first channel in the result should be made as an active channel, or no channel should be marked as active
|
|
435
|
+
* @returns the list of channels found by the query
|
|
419
436
|
*/
|
|
420
|
-
init(filters, sort, options) {
|
|
437
|
+
init(filters, sort, options, shouldSetActiveChannel = true) {
|
|
421
438
|
return __awaiter(this, void 0, void 0, function* () {
|
|
422
439
|
this.filters = filters;
|
|
423
440
|
this.options = options || {
|
|
@@ -429,21 +446,17 @@ class ChannelService {
|
|
|
429
446
|
message_limit: this.messagePageSize,
|
|
430
447
|
};
|
|
431
448
|
this.sort = sort || { last_message_at: -1, updated_at: -1 };
|
|
432
|
-
yield this.queryChannels();
|
|
449
|
+
const result = yield this.queryChannels(shouldSetActiveChannel);
|
|
433
450
|
this.chatClientService.events$.subscribe((notification) => void this.handleNotification(notification));
|
|
451
|
+
return result;
|
|
434
452
|
});
|
|
435
453
|
}
|
|
436
454
|
/**
|
|
437
455
|
* Resets the `activeChannel$`, `channels$` and `activeChannelMessages$` Observables. Useful when disconnecting a chat user, use in combination with [`disconnectUser`](./ChatClientService.mdx/#disconnectuser).
|
|
438
456
|
*/
|
|
439
457
|
reset() {
|
|
440
|
-
this.
|
|
441
|
-
this.activeChannelSubject.next(undefined);
|
|
442
|
-
this.activeParentMessageIdSubject.next(undefined);
|
|
443
|
-
this.activeThreadMessagesSubject.next([]);
|
|
458
|
+
this.deselectActiveChannel();
|
|
444
459
|
this.channelsSubject.next(undefined);
|
|
445
|
-
this.latestMessageDateByUserByChannelsSubject.next({});
|
|
446
|
-
this.selectMessageToQuote(undefined);
|
|
447
460
|
}
|
|
448
461
|
/**
|
|
449
462
|
* Loads the next page of channels. The page size can be set in the [query option](https://getstream.io/chat/docs/javascript/query_channels/?language=javascript#query-options) object.
|
|
@@ -451,7 +464,7 @@ class ChannelService {
|
|
|
451
464
|
loadMoreChannels() {
|
|
452
465
|
return __awaiter(this, void 0, void 0, function* () {
|
|
453
466
|
this.options.offset = this.channels.length;
|
|
454
|
-
yield this.queryChannels();
|
|
467
|
+
yield this.queryChannels(false);
|
|
455
468
|
});
|
|
456
469
|
}
|
|
457
470
|
/**
|
|
@@ -535,8 +548,12 @@ class ChannelService {
|
|
|
535
548
|
const result = [];
|
|
536
549
|
const channel = this.activeChannelSubject.getValue();
|
|
537
550
|
const uploadResults = yield Promise.allSettled(uploads.map((upload) => upload.type === 'image'
|
|
538
|
-
?
|
|
539
|
-
|
|
551
|
+
? this.customImageUploadRequest
|
|
552
|
+
? this.customImageUploadRequest(upload.file, channel)
|
|
553
|
+
: channel.sendImage(upload.file)
|
|
554
|
+
: this.customFileUploadRequest
|
|
555
|
+
? this.customFileUploadRequest(upload.file, channel)
|
|
556
|
+
: channel.sendFile(upload.file)));
|
|
540
557
|
uploadResults.forEach((uploadResult, i) => {
|
|
541
558
|
const file = uploads[i].file;
|
|
542
559
|
const type = uploads[i].type;
|
|
@@ -563,8 +580,12 @@ class ChannelService {
|
|
|
563
580
|
return __awaiter(this, void 0, void 0, function* () {
|
|
564
581
|
const channel = this.activeChannelSubject.getValue();
|
|
565
582
|
yield (attachmentUpload.type === 'image'
|
|
566
|
-
?
|
|
567
|
-
|
|
583
|
+
? this.customImageDeleteRequest
|
|
584
|
+
? this.customImageDeleteRequest(attachmentUpload.url, channel)
|
|
585
|
+
: channel.deleteImage(attachmentUpload.url)
|
|
586
|
+
: this.customFileDeleteRequest
|
|
587
|
+
? this.customFileDeleteRequest(attachmentUpload.url, channel)
|
|
588
|
+
: channel.deleteFile(attachmentUpload.url));
|
|
568
589
|
});
|
|
569
590
|
}
|
|
570
591
|
/**
|
|
@@ -865,20 +886,24 @@ class ChannelService {
|
|
|
865
886
|
this.activeChannelSubscriptions.forEach((s) => s.unsubscribe());
|
|
866
887
|
this.activeChannelSubscriptions = [];
|
|
867
888
|
}
|
|
868
|
-
queryChannels() {
|
|
889
|
+
queryChannels(shouldSetActiveChannel) {
|
|
869
890
|
return __awaiter(this, void 0, void 0, function* () {
|
|
870
891
|
try {
|
|
871
892
|
const channels = yield this.chatClientService.chatClient.queryChannels(this.filters, this.sort, this.options);
|
|
872
893
|
channels.forEach((c) => this.watchForChannelEvents(c));
|
|
873
894
|
const prevChannels = this.channelsSubject.getValue() || [];
|
|
874
895
|
this.channelsSubject.next([...prevChannels, ...channels]);
|
|
875
|
-
if (channels.length > 0 &&
|
|
896
|
+
if (channels.length > 0 &&
|
|
897
|
+
!this.activeChannelSubject.getValue() &&
|
|
898
|
+
shouldSetActiveChannel) {
|
|
876
899
|
this.setAsActiveChannel(channels[0]);
|
|
877
900
|
}
|
|
878
901
|
this.hasMoreChannelsSubject.next(channels.length >= this.options.limit);
|
|
902
|
+
return channels;
|
|
879
903
|
}
|
|
880
904
|
catch (error) {
|
|
881
905
|
this.channelsSubject.error(error);
|
|
906
|
+
throw error;
|
|
882
907
|
}
|
|
883
908
|
});
|
|
884
909
|
}
|
|
@@ -1244,10 +1269,14 @@ class AttachmentService {
|
|
|
1244
1269
|
}
|
|
1245
1270
|
const imageFiles = [];
|
|
1246
1271
|
const dataFiles = [];
|
|
1272
|
+
const videoFiles = [];
|
|
1247
1273
|
Array.from(fileList).forEach((file) => {
|
|
1248
1274
|
if (isImageFile(file)) {
|
|
1249
1275
|
imageFiles.push(file);
|
|
1250
1276
|
}
|
|
1277
|
+
else if (file.type.startsWith('video/')) {
|
|
1278
|
+
videoFiles.push(file);
|
|
1279
|
+
}
|
|
1251
1280
|
else {
|
|
1252
1281
|
dataFiles.push(file);
|
|
1253
1282
|
}
|
|
@@ -1259,6 +1288,11 @@ class AttachmentService {
|
|
|
1259
1288
|
state: 'uploading',
|
|
1260
1289
|
type: 'image',
|
|
1261
1290
|
})),
|
|
1291
|
+
...videoFiles.map((file) => ({
|
|
1292
|
+
file,
|
|
1293
|
+
state: 'uploading',
|
|
1294
|
+
type: 'video',
|
|
1295
|
+
})),
|
|
1262
1296
|
...dataFiles.map((file) => ({
|
|
1263
1297
|
file,
|
|
1264
1298
|
state: 'uploading',
|
|
@@ -1296,19 +1330,25 @@ class AttachmentService {
|
|
|
1296
1330
|
deleteAttachment(upload) {
|
|
1297
1331
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1298
1332
|
const attachmentUploads = this.attachmentUploadsSubject.getValue();
|
|
1333
|
+
let result;
|
|
1299
1334
|
if (upload.state === 'success') {
|
|
1300
1335
|
try {
|
|
1301
1336
|
yield this.channelService.deleteAttachment(upload);
|
|
1302
|
-
attachmentUploads
|
|
1337
|
+
result = [...attachmentUploads];
|
|
1338
|
+
const index = attachmentUploads.indexOf(upload);
|
|
1339
|
+
result.splice(index, 1);
|
|
1303
1340
|
}
|
|
1304
1341
|
catch (error) {
|
|
1342
|
+
result = attachmentUploads;
|
|
1305
1343
|
this.notificationService.addTemporaryNotification('streamChat.Error deleting attachment');
|
|
1306
1344
|
}
|
|
1307
1345
|
}
|
|
1308
1346
|
else {
|
|
1309
|
-
attachmentUploads
|
|
1347
|
+
result = [...attachmentUploads];
|
|
1348
|
+
const index = attachmentUploads.indexOf(upload);
|
|
1349
|
+
result.splice(index, 1);
|
|
1310
1350
|
}
|
|
1311
|
-
this.attachmentUploadsSubject.next([...
|
|
1351
|
+
this.attachmentUploadsSubject.next([...result]);
|
|
1312
1352
|
});
|
|
1313
1353
|
}
|
|
1314
1354
|
/**
|
|
@@ -1390,9 +1430,9 @@ class AttachmentService {
|
|
|
1390
1430
|
}
|
|
1391
1431
|
uploadAttachments(uploads) {
|
|
1392
1432
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1393
|
-
const attachmentUploads = this.attachmentUploadsSubject.getValue();
|
|
1394
1433
|
this.attachmentUploadInProgressCounterSubject.next(this.attachmentUploadInProgressCounterSubject.getValue() + 1);
|
|
1395
1434
|
const result = yield this.channelService.uploadAttachments(uploads);
|
|
1435
|
+
const attachmentUploads = this.attachmentUploadsSubject.getValue();
|
|
1396
1436
|
result.forEach((r) => {
|
|
1397
1437
|
const upload = attachmentUploads.find((upload) => upload.file === r.file);
|
|
1398
1438
|
if (!upload) {
|
|
@@ -1861,6 +1901,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
|
|
|
1861
1901
|
type: Input
|
|
1862
1902
|
}] } });
|
|
1863
1903
|
|
|
1904
|
+
/**
|
|
1905
|
+
* The `Modal` component displays its content in an overlay. The modal can be closed with a close button, if the user clicks outside of the modal content, or if the escape button is pressed. The modal can also be closed from outside.
|
|
1906
|
+
*/
|
|
1907
|
+
class ModalComponent {
|
|
1908
|
+
constructor() {
|
|
1909
|
+
/**
|
|
1910
|
+
* If `true` the modal will be displayed, if `false` the modal will be hidden
|
|
1911
|
+
*/
|
|
1912
|
+
this.isOpen = false;
|
|
1913
|
+
/**
|
|
1914
|
+
* Emits `true` if the modal becomes visible, and `false` if the modal is closed.
|
|
1915
|
+
*/
|
|
1916
|
+
this.isOpenChange = new EventEmitter();
|
|
1917
|
+
this.watchForEscPress = (event) => {
|
|
1918
|
+
if (event.key === 'Escape') {
|
|
1919
|
+
this.close();
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
this.stopWatchForEscPress = () => {
|
|
1923
|
+
window.removeEventListener('keyup', this.watchForEscPress);
|
|
1924
|
+
};
|
|
1925
|
+
this.watchForOutsideClicks = (event) => {
|
|
1926
|
+
var _a;
|
|
1927
|
+
if (!((_a = this.innerContainer) === null || _a === void 0 ? void 0 : _a.nativeElement.contains(event.target))) {
|
|
1928
|
+
this.close();
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
ngOnChanges(changes) {
|
|
1933
|
+
if (changes.isOpen) {
|
|
1934
|
+
if (this.isOpen) {
|
|
1935
|
+
window.addEventListener('keyup', this.watchForEscPress);
|
|
1936
|
+
setTimeout(() => window.addEventListener('click', this.watchForOutsideClicks), 0);
|
|
1937
|
+
}
|
|
1938
|
+
else {
|
|
1939
|
+
this.stopWatchForOutsideClicks();
|
|
1940
|
+
this.stopWatchForEscPress();
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
close() {
|
|
1945
|
+
this.isOpen = false;
|
|
1946
|
+
this.isOpenChange.emit(false);
|
|
1947
|
+
this.stopWatchForOutsideClicks();
|
|
1948
|
+
this.stopWatchForEscPress();
|
|
1949
|
+
}
|
|
1950
|
+
stopWatchForOutsideClicks() {
|
|
1951
|
+
window.removeEventListener('click', this.watchForOutsideClicks);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
ModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1955
|
+
ModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: ModalComponent, selector: "stream-modal", inputs: { isOpen: "isOpen", content: "content" }, outputs: { isOpenChange: "isOpenChange" }, viewQueries: [{ propertyName: "innerContainer", first: true, predicate: ["modalInner"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n data-testid=\"modal\"\n class=\"str-chat__modal str-chat__modal--{{ isOpen ? 'open' : 'close' }}\"\n>\n <div\n data-testid=\"close\"\n class=\"str-chat__modal__close-button\"\n (click)=\"close()\"\n (keyup.enter)=\"close()\"\n translate\n >\n streamChat.Close\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </div>\n <div class=\"str-chat__modal__inner\" #modalInner>\n <ng-container *ngIf=\"content; else elseContent\">\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </ng-container>\n <ng-template #elseContent>\n <ng-content></ng-content>\n </ng-template>\n </div>\n</div>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }], directives: [{ type: i2.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }] });
|
|
1956
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: ModalComponent, decorators: [{
|
|
1957
|
+
type: Component,
|
|
1958
|
+
args: [{
|
|
1959
|
+
selector: 'stream-modal',
|
|
1960
|
+
templateUrl: './modal.component.html',
|
|
1961
|
+
styles: [],
|
|
1962
|
+
}]
|
|
1963
|
+
}], ctorParameters: function () { return []; }, propDecorators: { isOpen: [{
|
|
1964
|
+
type: Input
|
|
1965
|
+
}], content: [{
|
|
1966
|
+
type: Input
|
|
1967
|
+
}], isOpenChange: [{
|
|
1968
|
+
type: Output
|
|
1969
|
+
}], innerContainer: [{
|
|
1970
|
+
type: ViewChild,
|
|
1971
|
+
args: ['modalInner']
|
|
1972
|
+
}] } });
|
|
1973
|
+
|
|
1864
1974
|
const textareaInjectionToken = new InjectionToken('textareaInjectionToken');
|
|
1865
1975
|
|
|
1866
1976
|
class TextareaDirective {
|
|
@@ -2003,76 +2113,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
|
|
|
2003
2113
|
}]
|
|
2004
2114
|
}], ctorParameters: function () { return []; } });
|
|
2005
2115
|
|
|
2006
|
-
/**
|
|
2007
|
-
* The `Modal` component displays its content in an overlay. The modal can be closed with a close button, if the user clicks outside of the modal content, or if the escape button is pressed. The modal can also be closed from outside.
|
|
2008
|
-
*/
|
|
2009
|
-
class ModalComponent {
|
|
2010
|
-
constructor() {
|
|
2011
|
-
/**
|
|
2012
|
-
* If `true` the modal will be displayed, if `false` the modal will be hidden
|
|
2013
|
-
*/
|
|
2014
|
-
this.isOpen = false;
|
|
2015
|
-
/**
|
|
2016
|
-
* Emits `true` if the modal becomes visible, and `false` if the modal is closed.
|
|
2017
|
-
*/
|
|
2018
|
-
this.isOpenChange = new EventEmitter();
|
|
2019
|
-
this.watchForEscPress = (event) => {
|
|
2020
|
-
if (event.key === 'Escape') {
|
|
2021
|
-
this.close();
|
|
2022
|
-
}
|
|
2023
|
-
};
|
|
2024
|
-
this.stopWatchForEscPress = () => {
|
|
2025
|
-
window.removeEventListener('keyup', this.watchForEscPress);
|
|
2026
|
-
};
|
|
2027
|
-
this.watchForOutsideClicks = (event) => {
|
|
2028
|
-
var _a;
|
|
2029
|
-
if (!((_a = this.innerContainer) === null || _a === void 0 ? void 0 : _a.nativeElement.contains(event.target))) {
|
|
2030
|
-
this.close();
|
|
2031
|
-
}
|
|
2032
|
-
};
|
|
2033
|
-
}
|
|
2034
|
-
ngOnChanges(changes) {
|
|
2035
|
-
if (changes.isOpen) {
|
|
2036
|
-
if (this.isOpen) {
|
|
2037
|
-
window.addEventListener('keyup', this.watchForEscPress);
|
|
2038
|
-
setTimeout(() => window.addEventListener('click', this.watchForOutsideClicks), 0);
|
|
2039
|
-
}
|
|
2040
|
-
else {
|
|
2041
|
-
this.stopWatchForOutsideClicks();
|
|
2042
|
-
this.stopWatchForEscPress();
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
close() {
|
|
2047
|
-
this.isOpen = false;
|
|
2048
|
-
this.isOpenChange.emit(false);
|
|
2049
|
-
this.stopWatchForOutsideClicks();
|
|
2050
|
-
this.stopWatchForEscPress();
|
|
2051
|
-
}
|
|
2052
|
-
stopWatchForOutsideClicks() {
|
|
2053
|
-
window.removeEventListener('click', this.watchForOutsideClicks);
|
|
2054
|
-
}
|
|
2055
|
-
}
|
|
2056
|
-
ModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2057
|
-
ModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: ModalComponent, selector: "stream-modal", inputs: { isOpen: "isOpen", content: "content" }, outputs: { isOpenChange: "isOpenChange" }, viewQueries: [{ propertyName: "innerContainer", first: true, predicate: ["modalInner"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n data-testid=\"modal\"\n class=\"str-chat__modal str-chat__modal--{{ isOpen ? 'open' : 'close' }}\"\n>\n <div\n data-testid=\"close\"\n class=\"str-chat__modal__close-button\"\n (click)=\"close()\"\n (keyup.enter)=\"close()\"\n translate\n >\n streamChat.Close\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </div>\n <div class=\"str-chat__modal__inner\" #modalInner>\n <ng-container *ngIf=\"content; else elseContent\">\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </ng-container>\n <ng-template #elseContent>\n <ng-content></ng-content>\n </ng-template>\n </div>\n</div>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }], directives: [{ type: i2.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }] });
|
|
2058
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: ModalComponent, decorators: [{
|
|
2059
|
-
type: Component,
|
|
2060
|
-
args: [{
|
|
2061
|
-
selector: 'stream-modal',
|
|
2062
|
-
templateUrl: './modal.component.html',
|
|
2063
|
-
styles: [],
|
|
2064
|
-
}]
|
|
2065
|
-
}], ctorParameters: function () { return []; }, propDecorators: { isOpen: [{
|
|
2066
|
-
type: Input
|
|
2067
|
-
}], content: [{
|
|
2068
|
-
type: Input
|
|
2069
|
-
}], isOpenChange: [{
|
|
2070
|
-
type: Output
|
|
2071
|
-
}], innerContainer: [{
|
|
2072
|
-
type: ViewChild,
|
|
2073
|
-
args: ['modalInner']
|
|
2074
|
-
}] } });
|
|
2075
|
-
|
|
2076
2116
|
/**
|
|
2077
2117
|
* The `AttachmentList` compontent displays the attachments of a message
|
|
2078
2118
|
*/
|
|
@@ -2094,6 +2134,7 @@ class AttachmentListComponent {
|
|
|
2094
2134
|
const containsGallery = images.length >= 2;
|
|
2095
2135
|
this.orderedAttachments = [
|
|
2096
2136
|
...(containsGallery ? this.createGallery(images) : images),
|
|
2137
|
+
...this.attachments.filter((a) => this.isVideo(a)),
|
|
2097
2138
|
...this.attachments.filter((a) => this.isFile(a)),
|
|
2098
2139
|
...this.attachments.filter((a) => this.isCard(a)),
|
|
2099
2140
|
];
|
|
@@ -2110,6 +2151,12 @@ class AttachmentListComponent {
|
|
|
2110
2151
|
isGallery(attachment) {
|
|
2111
2152
|
return attachment.type === 'gallery';
|
|
2112
2153
|
}
|
|
2154
|
+
isVideo(attachment) {
|
|
2155
|
+
return (attachment.type === 'video' &&
|
|
2156
|
+
attachment.asset_url &&
|
|
2157
|
+
!attachment.og_scrape_url // links from video share services (such as YouTube or Facebook) are can't be played
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
2113
2160
|
isCard(attachment) {
|
|
2114
2161
|
return (!attachment.type ||
|
|
2115
2162
|
(attachment.type === 'image' && !this.isImage(attachment)) ||
|
|
@@ -2177,7 +2224,7 @@ class AttachmentListComponent {
|
|
|
2177
2224
|
}
|
|
2178
2225
|
}
|
|
2179
2226
|
AttachmentListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentListComponent, deps: [{ token: CustomTemplatesService }, { token: ImageLoadService }, { token: ChannelService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2180
|
-
AttachmentListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: AttachmentListComponent, selector: "stream-attachment-list", inputs: { messageId: "messageId", attachments: "attachments" }, viewQueries: [{ propertyName: "modalContent", first: true, predicate: ["modalContent"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngFor=\"let attachment of orderedAttachments; trackBy: trackById\">\n <div\n data-testclass=\"attachment-container\"\n class=\"str-chat__message-attachment str-chat__message-attachment--{{\n attachment.type\n }}\"\n [class.str-chat__message-attachment--card]=\"isCard(attachment)\"\n [class.str-chat-angular__message-attachment-file-single]=\"\n isFile(attachment)\n \"\n >\n <img\n *ngIf=\"isImage(attachment)\"\n class=\"str-chat__message-attachment--img\"\n data-testclass=\"image\"\n [src]=\"attachment.img_url || attachment.thumb_url || attachment.image_url\"\n [alt]=\"attachment?.fallback\"\n (load)=\"imageLoaded()\"\n (click)=\"openImageModal([attachment])\"\n (keyup.enter)=\"openImageModal([attachment])\"\n />\n <div\n class=\"str-chat__gallery\"\n data-testid=\"image-gallery\"\n *ngIf=\"isGallery(attachment)\"\n [class.str-chat__gallery--square]=\"(attachment?.images)!.length > 3\"\n >\n <ng-container\n *ngFor=\"\n let galleryImage of attachment.images;\n let index = index;\n let isLast = last;\n trackBy: trackByImageUrl\n \"\n >\n <button\n *ngIf=\"index < 3 || (index === 3 && isLast)\"\n class=\"str-chat__gallery-image\"\n data-testclass=\"gallery-image\"\n (click)=\"openImageModal(attachment.images!, index)\"\n (keyup.enter)=\"openImageModal(attachment.images!, index)\"\n >\n <img\n [src]=\"\n galleryImage.img_url ||\n galleryImage.thumb_url ||\n galleryImage.image_url\n \"\n [alt]=\"galleryImage.fallback\"\n (load)=\"imageLoaded()\"\n />\n </button>\n <button\n *ngIf=\"index === 3 && !isLast\"\n class=\"str-chat__gallery-placeholder\"\n data-testclass=\"gallery-image\"\n (click)=\"openImageModal(attachment.images!, index)\"\n (keyup.enter)=\"openImageModal(attachment.images!, index)\"\n [ngStyle]=\"{\n 'background-image':\n 'url(' +\n (galleryImage.img_url ||\n galleryImage.thumb_url ||\n galleryImage.image_url) +\n ')'\n }\"\n >\n <p\n [innerHTML]=\"\n 'streamChat.{{ imageCount }} more'\n | translate: { imageCount: attachment!.images!.length - 4 }\n \"\n ></p>\n </button>\n </ng-container>\n </div>\n <div\n *ngIf=\"isFile(attachment)\"\n class=\"\n str-chat__message-attachment-file--item\n str-chat-angular__message-attachment-file-single\n \"\n >\n <stream-icon-placeholder\n icon=\"file\"\n [size]=\"30\"\n ></stream-icon-placeholder>\n <div class=\"str-chat__message-attachment-file--item-text\">\n <a\n data-testclass=\"file-link\"\n download\n href=\"{{ attachment.asset_url }}\"\n target=\"_blank\"\n >\n {{ attachment.title }}\n </a>\n <span data-testclass=\"size\" *ngIf=\"hasFileSize(attachment)\">{{\n getFileSize(attachment)\n }}</span>\n </div>\n </div>\n <div\n *ngIf=\"isCard(attachment)\"\n class=\"str-chat__message-attachment-card str-chat__message-attachment-card--{{\n attachment.type\n }}\"\n >\n <div\n *ngIf=\"attachment.image_url || attachment.thumb_url\"\n class=\"str-chat__message-attachment-card--header\"\n >\n <img\n data-testclass=\"card-img\"\n alt=\"{{ attachment.image_url || attachment.thumb_url }}\"\n src=\"{{ attachment.image_url || attachment.thumb_url }}\"\n />\n </div>\n <div class=\"str-chat__message-attachment-card--content\">\n <div class=\"str-chat__message-attachment-card--flex\">\n <div\n *ngIf=\"attachment.title\"\n data-testclass=\"card-title\"\n class=\"str-chat__message-attachment-card--title\"\n >\n {{ attachment.title }}\n </div>\n <div\n *ngIf=\"attachment.text\"\n class=\"str-chat__message-attachment-card--text\"\n data-testclass=\"card-text\"\n >\n {{ attachment.text }}\n </div>\n <a\n class=\"str-chat__message-attachment-card--url\"\n *ngIf=\"attachment.title_link || attachment.og_scrape_url\"\n data-testclass=\"url-link\"\n noopener\n noreferrer\n href=\"{{ attachment.title_link || attachment.og_scrape_url }}\"\n target=\"_blank\"\n >\n {{ trimUrl(attachment.title_link || attachment.og_scrape_url) }}\n </a>\n </div>\n </div>\n </div>\n <div\n class=\"str-chat__message-attachment-actions\"\n *ngIf=\"attachment.actions && attachment.actions.length > 0\"\n >\n <div class=\"str-chat__message-attachment-actions-form\">\n <button\n *ngFor=\"let action of attachment.actions; trackBy: trackByActionValue\"\n class=\"str-chat__message-attachment-actions-button str-chat__message-attachment-actions-button--{{\n action.style\n }}\"\n data-testclass=\"attachment-action\"\n (click)=\"sendAction(action)\"\n (keyup.enter)=\"sendAction(action)\"\n >\n {{ action.text }}\n </button>\n </div>\n </div>\n </div>\n</ng-container>\n\n<ng-container *ngIf=\"imagesToView && imagesToView.length > 0\">\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.modalTemplate$ | async) || defaultModal;\n context: getModalContext()\n \"\n ></ng-container>\n</ng-container>\n\n<ng-template\n #defaultModal\n let-isOpen=\"isOpen\"\n let-isOpenChangeHandler=\"isOpenChangeHandler\"\n let-content=\"content\"\n>\n <stream-modal\n [isOpen]=\"isOpen\"\n (isOpenChange)=\"isOpenChangeHandler($event)\"\n [content]=\"content\"\n >\n </stream-modal>\n</ng-template>\n\n<ng-template #modalContent>\n <div class=\"stream-chat-angular__image-modal\">\n <button\n class=\"stream-chat-angular__image-modal-stepper\"\n [ngStyle]=\"{\n visibility: isImageModalPrevButtonVisible ? 'visible' : 'hidden'\n }\"\n data-testid=\"image-modal-prev\"\n type=\"button\"\n (click)=\"stepImages(-1)\"\n (keyup.enter)=\"stepImages(-1)\"\n >\n <stream-icon-placeholder icon=\"arrow-left\"></stream-icon-placeholder>\n </button>\n <img\n class=\"stream-chat-angular__image-modal-image\"\n data-testid=\"modal-image\"\n [src]=\"\n imagesToView[imagesToViewCurrentIndex].img_url ||\n imagesToView[imagesToViewCurrentIndex].thumb_url ||\n imagesToView[imagesToViewCurrentIndex].image_url\n \"\n [alt]=\"imagesToView[imagesToViewCurrentIndex].fallback\"\n />\n <button\n class=\"stream-chat-angular__image-modal-stepper\"\n type=\"button\"\n [ngStyle]=\"{\n visibility: isImageModalNextButtonVisible ? 'visible' : 'hidden'\n }\"\n data-testid=\"image-modal-next\"\n (click)=\"stepImages(1)\"\n (keyup.enter)=\"stepImages(1)\"\n >\n <stream-icon-placeholder icon=\"arrow-right\"></stream-icon-placeholder>\n </button>\n </div>\n</ng-template>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: ModalComponent, selector: "stream-modal", inputs: ["isOpen", "content"], outputs: ["isOpenChange"] }], directives: [{ type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], pipes: { "translate": i2.TranslatePipe, "async": i3.AsyncPipe } });
|
|
2227
|
+
AttachmentListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: AttachmentListComponent, selector: "stream-attachment-list", inputs: { messageId: "messageId", attachments: "attachments" }, viewQueries: [{ propertyName: "modalContent", first: true, predicate: ["modalContent"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngFor=\"let attachment of orderedAttachments; trackBy: trackById\">\n <div\n data-testclass=\"attachment-container\"\n class=\"str-chat__message-attachment str-chat__message-attachment--{{\n attachment.type\n }}\"\n [class.str-chat__message-attachment--card]=\"isCard(attachment)\"\n [class.str-chat-angular__message-attachment-file-single]=\"\n isFile(attachment)\n \"\n >\n <img\n *ngIf=\"isImage(attachment)\"\n class=\"str-chat__message-attachment--img\"\n data-testclass=\"image\"\n [src]=\"attachment.img_url || attachment.thumb_url || attachment.image_url\"\n [alt]=\"attachment?.fallback\"\n (load)=\"imageLoaded()\"\n (click)=\"openImageModal([attachment])\"\n (keyup.enter)=\"openImageModal([attachment])\"\n />\n <div\n class=\"str-chat__gallery\"\n data-testid=\"image-gallery\"\n *ngIf=\"isGallery(attachment)\"\n [class.str-chat__gallery--square]=\"(attachment?.images)!.length > 3\"\n >\n <ng-container\n *ngFor=\"\n let galleryImage of attachment.images;\n let index = index;\n let isLast = last;\n trackBy: trackByImageUrl\n \"\n >\n <button\n *ngIf=\"index < 3 || (index === 3 && isLast)\"\n class=\"str-chat__gallery-image\"\n data-testclass=\"gallery-image\"\n (click)=\"openImageModal(attachment.images!, index)\"\n (keyup.enter)=\"openImageModal(attachment.images!, index)\"\n >\n <img\n [src]=\"\n galleryImage.img_url ||\n galleryImage.thumb_url ||\n galleryImage.image_url\n \"\n [alt]=\"galleryImage.fallback\"\n (load)=\"imageLoaded()\"\n />\n </button>\n <button\n *ngIf=\"index === 3 && !isLast\"\n class=\"str-chat__gallery-placeholder\"\n data-testclass=\"gallery-image\"\n (click)=\"openImageModal(attachment.images!, index)\"\n (keyup.enter)=\"openImageModal(attachment.images!, index)\"\n [ngStyle]=\"{\n 'background-image':\n 'url(' +\n (galleryImage.img_url ||\n galleryImage.thumb_url ||\n galleryImage.image_url) +\n ')'\n }\"\n >\n <p\n [innerHTML]=\"\n 'streamChat.{{ imageCount }} more'\n | translate: { imageCount: attachment!.images!.length - 4 }\n \"\n ></p>\n </button>\n </ng-container>\n </div>\n <video\n *ngIf=\"isVideo(attachment)\"\n controls\n data-testclass=\"video-attachment\"\n [src]=\"attachment.asset_url\"\n style=\"\n width: 100%;\n max-width: 400px;\n height: 300px;\n border-radius: inherit;\n \"\n ></video>\n <div\n *ngIf=\"isFile(attachment)\"\n class=\"\n str-chat__message-attachment-file--item\n str-chat-angular__message-attachment-file-single\n \"\n >\n <stream-icon-placeholder\n icon=\"file\"\n [size]=\"30\"\n ></stream-icon-placeholder>\n <div class=\"str-chat__message-attachment-file--item-text\">\n <a\n data-testclass=\"file-link\"\n download\n href=\"{{ attachment.asset_url }}\"\n target=\"_blank\"\n >\n {{ attachment.title }}\n </a>\n <span data-testclass=\"size\" *ngIf=\"hasFileSize(attachment)\">{{\n getFileSize(attachment)\n }}</span>\n </div>\n </div>\n <div\n *ngIf=\"isCard(attachment)\"\n class=\"str-chat__message-attachment-card str-chat__message-attachment-card--{{\n attachment.type\n }}\"\n >\n <div\n *ngIf=\"attachment.image_url || attachment.thumb_url\"\n class=\"str-chat__message-attachment-card--header\"\n >\n <img\n data-testclass=\"card-img\"\n alt=\"{{ attachment.image_url || attachment.thumb_url }}\"\n src=\"{{ attachment.image_url || attachment.thumb_url }}\"\n />\n </div>\n <div class=\"str-chat__message-attachment-card--content\">\n <div class=\"str-chat__message-attachment-card--flex\">\n <div\n *ngIf=\"attachment.title\"\n data-testclass=\"card-title\"\n class=\"str-chat__message-attachment-card--title\"\n >\n {{ attachment.title }}\n </div>\n <div\n *ngIf=\"attachment.text\"\n class=\"str-chat__message-attachment-card--text\"\n data-testclass=\"card-text\"\n >\n {{ attachment.text }}\n </div>\n <a\n class=\"str-chat__message-attachment-card--url\"\n *ngIf=\"attachment.title_link || attachment.og_scrape_url\"\n data-testclass=\"url-link\"\n noopener\n noreferrer\n href=\"{{ attachment.title_link || attachment.og_scrape_url }}\"\n target=\"_blank\"\n >\n {{ trimUrl(attachment.title_link || attachment.og_scrape_url) }}\n </a>\n </div>\n </div>\n </div>\n <div\n class=\"str-chat__message-attachment-actions\"\n *ngIf=\"attachment.actions && attachment.actions.length > 0\"\n >\n <div class=\"str-chat__message-attachment-actions-form\">\n <button\n *ngFor=\"let action of attachment.actions; trackBy: trackByActionValue\"\n class=\"str-chat__message-attachment-actions-button str-chat__message-attachment-actions-button--{{\n action.style\n }}\"\n data-testclass=\"attachment-action\"\n (click)=\"sendAction(action)\"\n (keyup.enter)=\"sendAction(action)\"\n >\n {{ action.text }}\n </button>\n </div>\n </div>\n </div>\n</ng-container>\n\n<ng-container *ngIf=\"imagesToView && imagesToView.length > 0\">\n <ng-container\n *ngTemplateOutlet=\"\n (customTemplatesService.modalTemplate$ | async) || defaultModal;\n context: getModalContext()\n \"\n ></ng-container>\n</ng-container>\n\n<ng-template\n #defaultModal\n let-isOpen=\"isOpen\"\n let-isOpenChangeHandler=\"isOpenChangeHandler\"\n let-content=\"content\"\n>\n <stream-modal\n [isOpen]=\"isOpen\"\n (isOpenChange)=\"isOpenChangeHandler($event)\"\n [content]=\"content\"\n >\n </stream-modal>\n</ng-template>\n\n<ng-template #modalContent>\n <div class=\"stream-chat-angular__image-modal\">\n <button\n class=\"stream-chat-angular__image-modal-stepper\"\n [ngStyle]=\"{\n visibility: isImageModalPrevButtonVisible ? 'visible' : 'hidden'\n }\"\n data-testid=\"image-modal-prev\"\n type=\"button\"\n (click)=\"stepImages(-1)\"\n (keyup.enter)=\"stepImages(-1)\"\n >\n <stream-icon-placeholder icon=\"arrow-left\"></stream-icon-placeholder>\n </button>\n <img\n class=\"stream-chat-angular__image-modal-image\"\n data-testid=\"modal-image\"\n [src]=\"\n imagesToView[imagesToViewCurrentIndex].img_url ||\n imagesToView[imagesToViewCurrentIndex].thumb_url ||\n imagesToView[imagesToViewCurrentIndex].image_url\n \"\n [alt]=\"imagesToView[imagesToViewCurrentIndex].fallback\"\n />\n <button\n class=\"stream-chat-angular__image-modal-stepper\"\n type=\"button\"\n [ngStyle]=\"{\n visibility: isImageModalNextButtonVisible ? 'visible' : 'hidden'\n }\"\n data-testid=\"image-modal-next\"\n (click)=\"stepImages(1)\"\n (keyup.enter)=\"stepImages(1)\"\n >\n <stream-icon-placeholder icon=\"arrow-right\"></stream-icon-placeholder>\n </button>\n </div>\n</ng-template>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: ModalComponent, selector: "stream-modal", inputs: ["isOpen", "content"], outputs: ["isOpenChange"] }], directives: [{ type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], pipes: { "translate": i2.TranslatePipe, "async": i3.AsyncPipe } });
|
|
2181
2228
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentListComponent, decorators: [{
|
|
2182
2229
|
type: Component,
|
|
2183
2230
|
args: [{
|
|
@@ -2219,7 +2266,7 @@ class AttachmentPreviewListComponent {
|
|
|
2219
2266
|
}
|
|
2220
2267
|
}
|
|
2221
2268
|
AttachmentPreviewListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentPreviewListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2222
|
-
AttachmentPreviewListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: AttachmentPreviewListComponent, selector: "stream-attachment-preview-list", inputs: { attachmentUploads$: "attachmentUploads$" }, outputs: { retryAttachmentUpload: "retryAttachmentUpload", deleteAttachment: "deleteAttachment" }, ngImport: i0, template: "<div class=\"rfu-image-previewer\" *ngIf=\"(attachmentUploads$ | async)?.length\">\n <ng-container\n *ngFor=\"\n let attachmentUpload of attachmentUploads$ | async;\n trackBy: trackByFile\n \"\n >\n <div\n *ngIf=\"attachmentUpload.type === 'image'\"\n class=\"rfu-image-previewer__image\"\n [class.rfu-image-previewer__image--loaded]=\"\n attachmentUpload.state === 'success'\n \"\n data-testclass=\"attachment-image-preview\"\n >\n <div\n *ngIf=\"attachmentUpload.state === 'error'\"\n class=\"rfu-image-previewer__retry\"\n (click)=\"attachmentUploadRetried(attachmentUpload.file)\"\n (keyup.enter)=\"attachmentUploadRetried(attachmentUpload.file)\"\n data-testclass=\"upload-error\"\n >\n <stream-icon-placeholder icon=\"retry\"></stream-icon-placeholder>\n </div>\n <div class=\"rfu-thumbnail__wrapper\" style=\"width: 100; height: 100\">\n <div class=\"rfu-thumbnail__overlay\">\n <div\n class=\"rfu-icon-button\"\n data-testclass=\"delete-attachment\"\n role=\"button\"\n (click)=\"attachmentDeleted(attachmentUpload)\"\n (keyup.enter)=\"attachmentDeleted(attachmentUpload)\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </div>\n </div>\n <img\n *ngIf=\"attachmentUpload.url || attachmentUpload.previewUri\"\n src=\"{{\n attachmentUpload.url\n ? attachmentUpload.url\n : attachmentUpload.previewUri\n }}\"\n alt=\"attachmentUpload.file.name\"\n class=\"rfu-thumbnail__image\"\n data-testclass=\"attachment-image\"\n />\n </div>\n <stream-loading-indicator-placeholder\n data-testclass=\"loading-indicator\"\n color=\"rgba(255,255,255,0.7)\"\n *ngIf=\"attachmentUpload.state === 'uploading'\"\n ></stream-loading-indicator-placeholder>\n </div>\n <div\n class=\"rfu-file-previewer\"\n *ngIf=\"attachmentUpload.type === 'file'\"\n data-testclass=\"attachment-file-preview\"\n >\n <ol>\n <li\n class=\"rfu-file-previewer__file\"\n [class.rfu-file-previewer__file--uploading]=\"\n attachmentUpload.state === 'uploading'\n \"\n [class.rfu-file-previewer__file--failed]=\"\n attachmentUpload.state === 'error'\n \"\n >\n <stream-icon-placeholder icon=\"file\"></stream-icon-placeholder>\n\n <a\n data-testclass=\"file-download-link\"\n href=\"{{ attachmentUpload.url }}\"\n (click)=\"attachmentUpload.url ? null : $event.preventDefault()\"\n (keyup.enter)=\"\n attachmentUpload.url ? null : $event.preventDefault()\n \"\n download\n >\n {{ attachmentUpload.file.name }}\n <ng-container *ngIf=\"attachmentUpload.state === 'error'\">\n <div\n data-testclass=\"file-upload-retry\"\n class=\"rfu-file-previewer__failed\"\n (click)=\"attachmentUploadRetried(attachmentUpload.file)\"\n (keyup.enter)=\"attachmentUploadRetried(attachmentUpload.file)\"\n translate\n >\n streamChat.failed\n </div>\n <div\n class=\"rfu-file-previewer__retry\"\n (click)=\"attachmentUploadRetried(attachmentUpload.file)\"\n (keyup.enter)=\"attachmentUploadRetried(attachmentUpload.file)\"\n translate\n >\n streamChat.retry\n </div>\n </ng-container>\n </a>\n\n <span\n data-testclass=\"file-delete\"\n class=\"rfu-file-previewer__close-button\"\n (click)=\"attachmentDeleted(attachmentUpload)\"\n (keyup.enter)=\"attachmentDeleted(attachmentUpload)\"\n >\n \u2718\n </span>\n <div\n *ngIf=\"attachmentUpload.state === 'uploading'\"\n class=\"rfu-file-previewer__loading-indicator\"\n >\n <stream-loading-indicator-placeholder></stream-loading-indicator-placeholder>\n </div>\n </li>\n </ol>\n </div>\n </ng-container>\n</div>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: LoadingIndicatorPlaceholderComponent, selector: "stream-loading-indicator-placeholder", inputs: ["size", "color"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }], pipes: { "async": i3.AsyncPipe } });
|
|
2269
|
+
AttachmentPreviewListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: AttachmentPreviewListComponent, selector: "stream-attachment-preview-list", inputs: { attachmentUploads$: "attachmentUploads$" }, outputs: { retryAttachmentUpload: "retryAttachmentUpload", deleteAttachment: "deleteAttachment" }, ngImport: i0, template: "<div class=\"rfu-image-previewer\" *ngIf=\"(attachmentUploads$ | async)?.length\">\n <ng-container\n *ngFor=\"\n let attachmentUpload of attachmentUploads$ | async;\n trackBy: trackByFile\n \"\n >\n <div\n *ngIf=\"attachmentUpload.type === 'image'\"\n class=\"rfu-image-previewer__image\"\n [class.rfu-image-previewer__image--loaded]=\"\n attachmentUpload.state === 'success'\n \"\n data-testclass=\"attachment-image-preview\"\n >\n <div\n *ngIf=\"attachmentUpload.state === 'error'\"\n class=\"rfu-image-previewer__retry\"\n (click)=\"attachmentUploadRetried(attachmentUpload.file)\"\n (keyup.enter)=\"attachmentUploadRetried(attachmentUpload.file)\"\n data-testclass=\"upload-error\"\n >\n <stream-icon-placeholder icon=\"retry\"></stream-icon-placeholder>\n </div>\n <div class=\"rfu-thumbnail__wrapper\" style=\"width: 100; height: 100\">\n <div class=\"rfu-thumbnail__overlay\">\n <div\n class=\"rfu-icon-button\"\n data-testclass=\"delete-attachment\"\n role=\"button\"\n (click)=\"attachmentDeleted(attachmentUpload)\"\n (keyup.enter)=\"attachmentDeleted(attachmentUpload)\"\n >\n <stream-icon-placeholder icon=\"close\"></stream-icon-placeholder>\n </div>\n </div>\n <img\n *ngIf=\"attachmentUpload.url || attachmentUpload.previewUri\"\n src=\"{{\n attachmentUpload.url\n ? attachmentUpload.url\n : attachmentUpload.previewUri\n }}\"\n alt=\"attachmentUpload.file.name\"\n class=\"rfu-thumbnail__image\"\n data-testclass=\"attachment-image\"\n />\n </div>\n <stream-loading-indicator-placeholder\n data-testclass=\"loading-indicator\"\n color=\"rgba(255,255,255,0.7)\"\n *ngIf=\"attachmentUpload.state === 'uploading'\"\n ></stream-loading-indicator-placeholder>\n </div>\n <div\n class=\"rfu-file-previewer\"\n *ngIf=\"\n attachmentUpload.type === 'file' || attachmentUpload.type === 'video'\n \"\n data-testclass=\"attachment-file-preview\"\n >\n <ol>\n <li\n class=\"rfu-file-previewer__file\"\n [class.rfu-file-previewer__file--uploading]=\"\n attachmentUpload.state === 'uploading'\n \"\n [class.rfu-file-previewer__file--failed]=\"\n attachmentUpload.state === 'error'\n \"\n >\n <stream-icon-placeholder icon=\"file\"></stream-icon-placeholder>\n\n <a\n data-testclass=\"file-download-link\"\n href=\"{{ attachmentUpload.url }}\"\n (click)=\"attachmentUpload.url ? null : $event.preventDefault()\"\n (keyup.enter)=\"\n attachmentUpload.url ? null : $event.preventDefault()\n \"\n download\n >\n {{ attachmentUpload.file.name }}\n <ng-container *ngIf=\"attachmentUpload.state === 'error'\">\n <div\n data-testclass=\"file-upload-retry\"\n class=\"rfu-file-previewer__failed\"\n (click)=\"attachmentUploadRetried(attachmentUpload.file)\"\n (keyup.enter)=\"attachmentUploadRetried(attachmentUpload.file)\"\n translate\n >\n streamChat.failed\n </div>\n <div\n class=\"rfu-file-previewer__retry\"\n (click)=\"attachmentUploadRetried(attachmentUpload.file)\"\n (keyup.enter)=\"attachmentUploadRetried(attachmentUpload.file)\"\n translate\n >\n streamChat.retry\n </div>\n </ng-container>\n </a>\n\n <span\n data-testclass=\"file-delete\"\n class=\"rfu-file-previewer__close-button\"\n (click)=\"attachmentDeleted(attachmentUpload)\"\n (keyup.enter)=\"attachmentDeleted(attachmentUpload)\"\n >\n \u2718\n </span>\n <div\n *ngIf=\"attachmentUpload.state === 'uploading'\"\n class=\"rfu-file-previewer__loading-indicator\"\n >\n <stream-loading-indicator-placeholder></stream-loading-indicator-placeholder>\n </div>\n </li>\n </ol>\n </div>\n </ng-container>\n</div>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: LoadingIndicatorPlaceholderComponent, selector: "stream-loading-indicator-placeholder", inputs: ["size", "color"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }], pipes: { "async": i3.AsyncPipe } });
|
|
2223
2270
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentPreviewListComponent, decorators: [{
|
|
2224
2271
|
type: Component,
|
|
2225
2272
|
args: [{
|
|
@@ -2358,8 +2405,19 @@ class MessageInputComponent {
|
|
|
2358
2405
|
if (changes.mode) {
|
|
2359
2406
|
this.setCanSendMessages();
|
|
2360
2407
|
}
|
|
2408
|
+
if (changes.sendMessage$) {
|
|
2409
|
+
if (this.sendMessageSubcription) {
|
|
2410
|
+
this.sendMessageSubcription.unsubscribe();
|
|
2411
|
+
}
|
|
2412
|
+
if (this.sendMessage$) {
|
|
2413
|
+
this.sendMessageSubcription = this.sendMessage$.subscribe(() => void this.messageSent());
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2361
2416
|
}
|
|
2362
2417
|
ngOnDestroy() {
|
|
2418
|
+
if (this.sendMessageSubcription) {
|
|
2419
|
+
this.sendMessageSubcription.unsubscribe();
|
|
2420
|
+
}
|
|
2363
2421
|
this.subscriptions.forEach((s) => s.unsubscribe());
|
|
2364
2422
|
}
|
|
2365
2423
|
messageSent() {
|
|
@@ -2566,7 +2624,7 @@ class MessageInputComponent {
|
|
|
2566
2624
|
}
|
|
2567
2625
|
}
|
|
2568
2626
|
MessageInputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageInputComponent, deps: [{ token: ChannelService }, { token: NotificationService }, { token: AttachmentService }, { token: MessageInputConfigService }, { token: textareaInjectionToken }, { token: i0.ComponentFactoryResolver }, { token: i0.ChangeDetectorRef }, { token: ChatClientService }, { token: EmojiInputService }, { token: CustomTemplatesService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2569
|
-
MessageInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageInputComponent, selector: "stream-message-input", inputs: { isFileUploadEnabled: "isFileUploadEnabled", areMentionsEnabled: "areMentionsEnabled", mentionScope: "mentionScope", mode: "mode", isMultipleFileUploadEnabled: "isMultipleFileUploadEnabled", message: "message" }, outputs: { messageUpdate: "messageUpdate" }, providers: [AttachmentService, EmojiInputService], 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=\"{{\n mode === 'main' ? 'str-chat__input-flat' : 'str-chat__small-message-input'\n }}\"\n [class.str-chat__input-flat-has-attachments]=\"\n (attachmentUploads$ | async)!.length > 0\n \"\n [class.str-chat__input-flat-quoted]=\"!!quotedMessage\"\n>\n <div\n data-testid=\"quoted-message-container\"\n class=\"quoted-message-preview\"\n *ngIf=\"quotedMessage\"\n >\n <div class=\"quoted-message-preview-header\">\n <div>{{ \"streamChat.Reply to Message\" | translate }}</div>\n <button\n class=\"str-chat__square-button\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToQuote()\"\n (keyup.enter)=\"deselectMessageToQuote()\"\n >\n <stream-icon-placeholder\n icon=\"close-no-outline\"\n style=\"font-size: 10px; line-height: 10px\"\n ></stream-icon-placeholder>\n </button>\n </div>\n <div class=\"quoted-message-preview-content\">\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host\"\n [imageUrl]=\"quotedMessage?.user?.image\"\n [name]=\"quotedMessage?.user?.name || quotedMessage?.user?.id\"\n [size]=\"20\"\n ></stream-avatar-placeholder>\n <div class=\"quoted-message-preview-content-inner\">\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 <div\n data-testid=\"quoted-message-text\"\n [innerHTML]=\"quotedMessage?.html || quotedMessage?.text\"\n ></div>\n </div>\n </div>\n </div>\n <div class=\"str-chat__input-flat-wrapper\" style=\"width: 100%\">\n <div\n class=\"{{\n mode === 'main'\n ? 'str-chat__input-flat--textarea-wrapper'\n : 'str-chat__small-message-input--textarea-wrapper'\n }}\"\n >\n <ng-template\n #defaultAttachmentsPreview\n let-attachmentUploads$=\"attachmentUploads$\"\n let-retryUploadHandler=\"retryUploadHandler\"\n let-deleteUploadHandler=\"deleteUploadHandler\"\n >\n <stream-attachment-preview-list\n [attachmentUploads$]=\"attachmentUploads$\"\n (retryAttachmentUpload)=\"retryUploadHandler($event)\"\n (deleteAttachment)=\"deleteUploadHandler($event)\"\n class=\"rfu-image-previewer-angular-host\"\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=\"rta str-chat__textarea str-chat-angular__textarea\">\n <ng-container\n *ngIf=\"emojiPickerTemplate && !isCooldownInProgress\"\n data-testid=\"emoji-picker\"\n >\n <div\n class=\"\n str-chat__input-flat-emojiselect\n str-chat-angular__emojiselect\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n emojiPickerTemplate;\n context: getEmojiPickerContext()\n \"\n ></ng-container>\n </div>\n </ng-container>\n <div\n class=\"str-chat__input-flat-cooldown str-chat-angular__cooldown\"\n *ngIf=\"isCooldownInProgress\"\n data-testid=\"cooldown-timer\"\n >\n {{ cooldown$ | async }}\n </div>\n <ng-template\n *ngIf=\"canSendMessages && !isCooldownInProgress; else notAllowed\"\n streamTextarea\n [(value)]=\"textareaValue\"\n (valueChange)=\"typingStart$.next()\"\n (send)=\"messageSent()\"\n [componentRef]=\"textareaRef\"\n (userMentions)=\"mentionedUsers = $event\"\n [areMentionsEnabled]=\"areMentionsEnabled\"\n [mentionScope]=\"mentionScope\"\n ></ng-template>\n <ng-template #notAllowed>\n <textarea\n disabled\n rows=\"1\"\n [value]=\"disabledTextareaText | translate\"\n class=\"rta__textarea str-chat__textarea__textarea\"\n data-testid=\"disabled-textarea\"\n ></textarea>\n </ng-template>\n </div>\n <div\n *ngIf=\"\n isFileUploadEnabled &&\n isFileUploadAuthorized &&\n canSendMessages &&\n !isCooldownInProgress\n \"\n class=\"str-chat__fileupload-wrapper\"\n data-testid=\"file-upload-button\"\n >\n <div class=\"str-chat__tooltip\">\n {{ \"streamChat.Attach files\" | translate }}\n </div>\n <div class=\"rfu-file-upload-button\">\n <label>\n <input\n #fileInput\n type=\"file\"\n class=\"rfu-file-input\"\n data-testid=\"file-input\"\n [multiple]=\"isMultipleFileUploadEnabled\"\n (change)=\"filesSelected(fileInput.files)\"\n />\n <span class=\"str-chat__input-flat-fileupload\">\n <stream-icon-placeholder\n icon=\"file-upload\"\n ></stream-icon-placeholder>\n </span>\n </label>\n </div>\n </div>\n </div>\n <button\n *ngIf=\"canSendMessages\"\n data-testid=\"send-button\"\n class=\"str-chat__send-button\"\n (click)=\"messageSent()\"\n (keyup.enter)=\"messageSent()\"\n >\n <stream-icon-placeholder icon=\"send\"></stream-icon-placeholder>\n </button>\n </div>\n</div>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: AvatarPlaceholderComponent, selector: "stream-avatar-placeholder", inputs: ["name", "imageUrl", "size"] }, { type: AttachmentListComponent, selector: "stream-attachment-list", inputs: ["messageId", "attachments"] }, { type: AttachmentPreviewListComponent, selector: "stream-attachment-preview-list", inputs: ["attachmentUploads$"], outputs: ["retryAttachmentUpload", "deleteAttachment"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: TextareaDirective, selector: "[streamTextarea]", inputs: ["componentRef", "areMentionsEnabled", "mentionScope", "value"], outputs: ["valueChange", "send", "userMentions"] }], pipes: { "async": i3.AsyncPipe, "translate": i2.TranslatePipe } });
|
|
2627
|
+
MessageInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageInputComponent, selector: "stream-message-input", inputs: { isFileUploadEnabled: "isFileUploadEnabled", areMentionsEnabled: "areMentionsEnabled", mentionScope: "mentionScope", mode: "mode", isMultipleFileUploadEnabled: "isMultipleFileUploadEnabled", message: "message", sendMessage$: "sendMessage$" }, outputs: { messageUpdate: "messageUpdate" }, providers: [AttachmentService, EmojiInputService], 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=\"{{\n mode === 'main' ? 'str-chat__input-flat' : 'str-chat__small-message-input'\n }}\"\n [class.str-chat__input-flat-has-attachments]=\"\n (attachmentUploads$ | async)!.length > 0\n \"\n [class.str-chat__input-flat-quoted]=\"!!quotedMessage\"\n>\n <div\n data-testid=\"quoted-message-container\"\n class=\"quoted-message-preview\"\n *ngIf=\"quotedMessage\"\n >\n <div class=\"quoted-message-preview-header\">\n <div>{{ \"streamChat.Reply to Message\" | translate }}</div>\n <button\n class=\"str-chat__square-button\"\n data-testid=\"remove-quote\"\n (click)=\"deselectMessageToQuote()\"\n (keyup.enter)=\"deselectMessageToQuote()\"\n >\n <stream-icon-placeholder\n icon=\"close-no-outline\"\n style=\"font-size: 10px; line-height: 10px\"\n ></stream-icon-placeholder>\n </button>\n </div>\n <div class=\"quoted-message-preview-content\">\n <stream-avatar-placeholder\n data-testid=\"qouted-message-avatar\"\n class=\"str-chat-angular__avatar-host\"\n [imageUrl]=\"quotedMessage?.user?.image\"\n [name]=\"quotedMessage?.user?.name || quotedMessage?.user?.id\"\n [size]=\"20\"\n ></stream-avatar-placeholder>\n <div class=\"quoted-message-preview-content-inner\">\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 <div\n data-testid=\"quoted-message-text\"\n [innerHTML]=\"quotedMessage?.html || quotedMessage?.text\"\n ></div>\n </div>\n </div>\n </div>\n <div class=\"str-chat__input-flat-wrapper\" style=\"width: 100%\">\n <div\n class=\"{{\n mode === 'main'\n ? 'str-chat__input-flat--textarea-wrapper'\n : 'str-chat__small-message-input--textarea-wrapper'\n }}\"\n >\n <ng-template\n #defaultAttachmentsPreview\n let-attachmentUploads$=\"attachmentUploads$\"\n let-retryUploadHandler=\"retryUploadHandler\"\n let-deleteUploadHandler=\"deleteUploadHandler\"\n >\n <stream-attachment-preview-list\n [attachmentUploads$]=\"attachmentUploads$\"\n (retryAttachmentUpload)=\"retryUploadHandler($event)\"\n (deleteAttachment)=\"deleteUploadHandler($event)\"\n class=\"rfu-image-previewer-angular-host\"\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=\"rta str-chat__textarea str-chat-angular__textarea\">\n <ng-container\n *ngIf=\"emojiPickerTemplate && !isCooldownInProgress\"\n data-testid=\"emoji-picker\"\n >\n <div\n class=\"\n str-chat__input-flat-emojiselect\n str-chat-angular__emojiselect\n \"\n >\n <ng-container\n *ngTemplateOutlet=\"\n emojiPickerTemplate;\n context: getEmojiPickerContext()\n \"\n ></ng-container>\n </div>\n </ng-container>\n <div\n class=\"str-chat__input-flat-cooldown str-chat-angular__cooldown\"\n *ngIf=\"isCooldownInProgress\"\n data-testid=\"cooldown-timer\"\n >\n {{ cooldown$ | async }}\n </div>\n <ng-template\n *ngIf=\"canSendMessages && !isCooldownInProgress; else notAllowed\"\n streamTextarea\n [(value)]=\"textareaValue\"\n (valueChange)=\"typingStart$.next()\"\n (send)=\"messageSent()\"\n [componentRef]=\"textareaRef\"\n (userMentions)=\"mentionedUsers = $event\"\n [areMentionsEnabled]=\"areMentionsEnabled\"\n [mentionScope]=\"mentionScope\"\n ></ng-template>\n <ng-template #notAllowed>\n <textarea\n disabled\n rows=\"1\"\n [value]=\"disabledTextareaText | translate\"\n class=\"rta__textarea str-chat__textarea__textarea\"\n data-testid=\"disabled-textarea\"\n ></textarea>\n </ng-template>\n </div>\n <div\n *ngIf=\"\n isFileUploadEnabled &&\n isFileUploadAuthorized &&\n canSendMessages &&\n !isCooldownInProgress\n \"\n class=\"str-chat__fileupload-wrapper\"\n data-testid=\"file-upload-button\"\n >\n <div class=\"str-chat__tooltip\">\n {{ \"streamChat.Attach files\" | translate }}\n </div>\n <div class=\"rfu-file-upload-button\">\n <label>\n <input\n #fileInput\n type=\"file\"\n class=\"rfu-file-input\"\n data-testid=\"file-input\"\n [multiple]=\"isMultipleFileUploadEnabled\"\n (change)=\"filesSelected(fileInput.files)\"\n />\n <span class=\"str-chat__input-flat-fileupload\">\n <stream-icon-placeholder\n icon=\"file-upload\"\n ></stream-icon-placeholder>\n </span>\n </label>\n </div>\n </div>\n </div>\n <button\n *ngIf=\"canSendMessages\"\n data-testid=\"send-button\"\n class=\"str-chat__send-button\"\n (click)=\"messageSent()\"\n (keyup.enter)=\"messageSent()\"\n >\n <stream-icon-placeholder icon=\"send\"></stream-icon-placeholder>\n </button>\n </div>\n</div>\n", components: [{ type: IconPlaceholderComponent, selector: "stream-icon-placeholder", inputs: ["icon", "size"] }, { type: AvatarPlaceholderComponent, selector: "stream-avatar-placeholder", inputs: ["name", "imageUrl", "size"] }, { type: AttachmentListComponent, selector: "stream-attachment-list", inputs: ["messageId", "attachments"] }, { type: AttachmentPreviewListComponent, selector: "stream-attachment-preview-list", inputs: ["attachmentUploads$"], outputs: ["retryAttachmentUpload", "deleteAttachment"] }], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: TextareaDirective, selector: "[streamTextarea]", inputs: ["componentRef", "areMentionsEnabled", "mentionScope", "value"], outputs: ["valueChange", "send", "userMentions"] }], pipes: { "async": i3.AsyncPipe, "translate": i2.TranslatePipe } });
|
|
2570
2628
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageInputComponent, decorators: [{
|
|
2571
2629
|
type: Component,
|
|
2572
2630
|
args: [{
|
|
@@ -2590,6 +2648,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
|
|
|
2590
2648
|
type: Input
|
|
2591
2649
|
}], message: [{
|
|
2592
2650
|
type: Input
|
|
2651
|
+
}], sendMessage$: [{
|
|
2652
|
+
type: Input
|
|
2593
2653
|
}], messageUpdate: [{
|
|
2594
2654
|
type: Output
|
|
2595
2655
|
}], fileInput: [{
|
|
@@ -2680,6 +2740,7 @@ class MessageActionsBoxComponent {
|
|
|
2680
2740
|
this.isEditModalOpen = false;
|
|
2681
2741
|
this.subscriptions = [];
|
|
2682
2742
|
this.visibleMessageActionItems = [];
|
|
2743
|
+
this.sendMessageSubject = new Subject();
|
|
2683
2744
|
this.modalClosed = () => {
|
|
2684
2745
|
this.isEditModalOpen = false;
|
|
2685
2746
|
this.isEditing.emit(false);
|
|
@@ -2743,6 +2804,7 @@ class MessageActionsBoxComponent {
|
|
|
2743
2804
|
enabledActions.indexOf('delete-any-message') !== -1,
|
|
2744
2805
|
},
|
|
2745
2806
|
];
|
|
2807
|
+
this.sendMessage$ = this.sendMessageSubject.asObservable();
|
|
2746
2808
|
}
|
|
2747
2809
|
ngOnChanges(changes) {
|
|
2748
2810
|
if (changes.isMine || changes.enabledActions || changes.message) {
|
|
@@ -2760,8 +2822,7 @@ class MessageActionsBoxComponent {
|
|
|
2760
2822
|
: actionLabelOrTranslationKey();
|
|
2761
2823
|
}
|
|
2762
2824
|
sendClicked() {
|
|
2763
|
-
|
|
2764
|
-
(_a = this.messageInput) === null || _a === void 0 ? void 0 : _a.messageSent();
|
|
2825
|
+
this.sendMessageSubject.next();
|
|
2765
2826
|
}
|
|
2766
2827
|
getMessageInputContext() {
|
|
2767
2828
|
return {
|
|
@@ -2772,6 +2833,7 @@ class MessageActionsBoxComponent {
|
|
|
2772
2833
|
isMultipleFileUploadEnabled: undefined,
|
|
2773
2834
|
mentionScope: undefined,
|
|
2774
2835
|
mode: undefined,
|
|
2836
|
+
sendMessage$: this.sendMessage$,
|
|
2775
2837
|
};
|
|
2776
2838
|
}
|
|
2777
2839
|
getEditModalContext() {
|
|
@@ -2791,7 +2853,7 @@ class MessageActionsBoxComponent {
|
|
|
2791
2853
|
}
|
|
2792
2854
|
}
|
|
2793
2855
|
MessageActionsBoxComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageActionsBoxComponent, deps: [{ token: ChatClientService }, { token: NotificationService }, { token: ChannelService }, { token: CustomTemplatesService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2794
|
-
MessageActionsBoxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageActionsBoxComponent, selector: "stream-message-actions-box", inputs: { isOpen: "isOpen", isMine: "isMine", message: "message", enabledActions: "enabledActions" }, outputs: { displayedActionsCount: "displayedActionsCount", isEditing: "isEditing" }, viewQueries: [{ propertyName: "
|
|
2856
|
+
MessageActionsBoxComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: MessageActionsBoxComponent, selector: "stream-message-actions-box", inputs: { isOpen: "isOpen", isMine: "isMine", message: "message", enabledActions: "enabledActions" }, outputs: { displayedActionsCount: "displayedActionsCount", isEditing: "isEditing" }, viewQueries: [{ propertyName: "modalContent", first: true, predicate: ["modalContent"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div\n data-testid=\"action-box\"\n class=\"str-chat__message-actions-box\"\n [class.str-chat__message-actions-box--open]=\"isOpen\"\n [class.str-chat__message-actions-box--mine]=\"isMine\"\n>\n <ul class=\"str-chat__message-actions-list\">\n <ng-container\n *ngFor=\"let item of visibleMessageActionItems; trackBy: trackByActionName\"\n >\n <ng-container\n *ngTemplateOutlet=\"\n messageActionItemTemplate || defaultMessageActionItem;\n context: item\n \"\n ></ng-container>\n </ng-container>\n </ul>\n</div>\n\n<ng-template\n #defaultMessageActionItem\n let-actionName=\"actionName\"\n let-actionHandler=\"actionHandler\"\n let-actionLabelOrTranslationKey=\"actionLabelOrTranslationKey\"\n>\n <button [attr.data-testid]=\"actionName + '-action'\" (click)=\"actionHandler()\">\n <li class=\"str-chat__message-actions-list-item\">\n {{ getActionLabel(actionLabelOrTranslationKey) | translate }}\n </li>\n </button>\n</ng-template>\n\n<ng-container\n *ngTemplateOutlet=\"\n modalTemplate || defaultModal;\n context: getEditModalContext()\n \"\n></ng-container>\n\n<ng-template\n #defaultModal\n let-isOpen=\"isOpen\"\n let-isOpenChangeHandler=\"isOpenChangeHandler\"\n let-content=\"content\"\n>\n <stream-modal\n [isOpen]=\"isOpen\"\n (isOpenChange)=\"isOpenChangeHandler($event)\"\n [content]=\"content\"\n >\n </stream-modal>\n</ng-template>\n\n<ng-template #modalContent>\n <div class=\"str-chat__edit-message-form\" *ngIf=\"isEditModalOpen\">\n <ng-template\n #defaultInput\n let-messageInput=\"message\"\n let-messageUpdateHandler=\"messageUpdateHandler\"\n let-sendMessage$Input=\"sendMessage$\"\n >\n <stream-message-input\n [message]=\"messageInput\"\n (messageUpdate)=\"messageUpdateHandler()\"\n [sendMessage$]=\"sendMessage$Input\"\n ></stream-message-input>\n </ng-template>\n <ng-container\n *ngTemplateOutlet=\"\n messageInputTemplate || defaultInput;\n context: getMessageInputContext()\n \"\n >\n </ng-container>\n\n <stream-notification-list></stream-notification-list>\n <div\n class=\"\n str-chat__message-team-form-footer\n str-chat__message-team-form-footer-angular\n \"\n >\n <div class=\"str-chat__edit-message-form-options\">\n <button translate data-testid=\"cancel-button\" (click)=\"modalClosed()\">\n streamChat.Cancel\n </button>\n <button\n type=\"submit\"\n translate\n data-testid=\"send-button\"\n (click)=\"sendClicked()\"\n (keyup.enter)=\"sendClicked()\"\n >\n streamChat.Send\n </button>\n </div>\n </div>\n </div>\n</ng-template>\n", components: [{ type: ModalComponent, selector: "stream-modal", inputs: ["isOpen", "content"], outputs: ["isOpenChange"] }, { type: MessageInputComponent, selector: "stream-message-input", inputs: ["isFileUploadEnabled", "areMentionsEnabled", "mentionScope", "mode", "isMultipleFileUploadEnabled", "message", "sendMessage$"], outputs: ["messageUpdate"] }, { type: NotificationListComponent, selector: "stream-notification-list" }], directives: [{ type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }], pipes: { "translate": i2.TranslatePipe } });
|
|
2795
2857
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: MessageActionsBoxComponent, decorators: [{
|
|
2796
2858
|
type: Component,
|
|
2797
2859
|
args: [{
|
|
@@ -2811,9 +2873,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
|
|
|
2811
2873
|
type: Output
|
|
2812
2874
|
}], isEditing: [{
|
|
2813
2875
|
type: Output
|
|
2814
|
-
}], messageInput: [{
|
|
2815
|
-
type: ViewChild,
|
|
2816
|
-
args: [MessageInputComponent]
|
|
2817
2876
|
}], modalContent: [{
|
|
2818
2877
|
type: ViewChild,
|
|
2819
2878
|
args: ['modalContent', { static: true }]
|
|
@@ -3501,7 +3560,12 @@ class MessageComponent {
|
|
|
3501
3560
|
this.message.mentioned_users.length === 0) {
|
|
3502
3561
|
// Wrap emojis in span to display emojis correctly in Chrome https://bugs.chromium.org/p/chromium/issues/detail?id=596223
|
|
3503
3562
|
const regex = new RegExp(emojiRegex(), 'g');
|
|
3504
|
-
|
|
3563
|
+
// Based on this: https://stackoverflow.com/questions/4565112/javascript-how-to-find-out-if-the-user-browser-is-chrome
|
|
3564
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
3565
|
+
const isChrome = !!window.chrome &&
|
|
3566
|
+
typeof window.opr === 'undefined';
|
|
3567
|
+
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
|
|
3568
|
+
content = content.replace(regex, (match) => `<span ${isChrome ? 'class="str-chat__emoji-display-fix"' : ''}>${match}</span>`);
|
|
3505
3569
|
this.messageTextParts = [{ content, type: 'text' }];
|
|
3506
3570
|
}
|
|
3507
3571
|
else {
|