stream-chat-angular 4.1.0 → 4.2.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/bundles/stream-chat-angular.umd.js +148 -47
- package/bundles/stream-chat-angular.umd.js.map +1 -1
- package/esm2015/assets/version.js +2 -2
- package/esm2015/lib/attachment-configuration.service.js +92 -17
- package/esm2015/lib/attachment-list/attachment-list.component.js +49 -21
- package/esm2015/lib/attachment.service.js +4 -1
- package/esm2015/lib/channel.service.js +5 -3
- package/esm2015/lib/message/message.component.js +7 -8
- package/esm2015/lib/message-list/message-list.component.js +2 -2
- package/esm2015/lib/types.js +1 -1
- package/fesm2015/stream-chat-angular.js +154 -47
- package/fesm2015/stream-chat-angular.js.map +1 -1
- package/lib/attachment-configuration.service.d.ts +17 -6
- package/lib/attachment-list/attachment-list.component.d.ts +9 -7
- package/lib/message/message.component.d.ts +1 -20
- package/lib/types.d.ts +4 -0
- package/package.json +1 -1
- package/src/assets/styles/css/index.css +1 -1
- package/src/assets/styles/css/index.css.map +1 -1
- package/src/assets/styles/scss/Attachment.scss +45 -2
- package/src/assets/styles/scss/Gallery.scss +12 -6
- package/src/assets/styles/scss/Message.scss +2 -1
- package/src/assets/styles/v2/css/index.css +1 -1
- package/src/assets/styles/v2/css/index.css.map +1 -1
- package/src/assets/styles/v2/css/index.layout.css +1 -1
- package/src/assets/styles/v2/css/index.layout.css.map +1 -1
- package/src/assets/styles/v2/scss/AttachmentList/AttachmentList-layout.scss +72 -46
- package/src/assets/styles/v2/scss/Message/Message-layout.scss +0 -16
- package/src/assets/styles/v2/scss/MessageReactions/MessageReactions-layout.scss +1 -10
- package/src/assets/styles/v2/scss/Tooltip/Tooltip-layout.scss +2 -23
- package/src/assets/version.ts +1 -1
|
@@ -19,7 +19,7 @@ import transliterate from '@stream-io/transliterate';
|
|
|
19
19
|
import * as i8 from 'angular-mentions';
|
|
20
20
|
import { MentionModule } from 'angular-mentions';
|
|
21
21
|
|
|
22
|
-
const version = '4.
|
|
22
|
+
const version = '4.2.0';
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* The `NotificationService` can be used to add or remove notifications. By default the [`NotificationList`](../components/NotificationListComponent.mdx) component displays the currently active notifications.
|
|
@@ -641,10 +641,10 @@ class ChannelService {
|
|
|
641
641
|
const uploadResults = yield Promise.allSettled(uploads.map((upload) => upload.type === 'image'
|
|
642
642
|
? this.customImageUploadRequest
|
|
643
643
|
? this.customImageUploadRequest(upload.file, channel)
|
|
644
|
-
: channel.sendImage(upload.file)
|
|
644
|
+
: channel.sendImage(upload.file, upload.file.name, upload.file.type)
|
|
645
645
|
: this.customFileUploadRequest
|
|
646
646
|
? this.customFileUploadRequest(upload.file, channel)
|
|
647
|
-
: channel.sendFile(upload.file)));
|
|
647
|
+
: channel.sendFile(upload.file, upload.file.name, upload.file.type)));
|
|
648
648
|
uploadResults.forEach((uploadResult, i) => {
|
|
649
649
|
const file = uploads[i].file;
|
|
650
650
|
const type = uploads[i].type;
|
|
@@ -654,6 +654,8 @@ class ChannelService {
|
|
|
654
654
|
type,
|
|
655
655
|
state: 'success',
|
|
656
656
|
url: uploadResult.value.file,
|
|
657
|
+
/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
|
|
658
|
+
thumb_url: uploadResult.value.thumb_url,
|
|
657
659
|
});
|
|
658
660
|
}
|
|
659
661
|
else {
|
|
@@ -1564,6 +1566,7 @@ class AttachmentService {
|
|
|
1564
1566
|
attachment.asset_url = r.url;
|
|
1565
1567
|
attachment.title = (_b = r.file) === null || _b === void 0 ? void 0 : _b.name;
|
|
1566
1568
|
attachment.file_size = (_c = r.file) === null || _c === void 0 ? void 0 : _c.size;
|
|
1569
|
+
attachment.thumb_url = r.thumb_url;
|
|
1567
1570
|
}
|
|
1568
1571
|
return attachment;
|
|
1569
1572
|
});
|
|
@@ -1596,6 +1599,7 @@ class AttachmentService {
|
|
|
1596
1599
|
size: attachment.file_size,
|
|
1597
1600
|
},
|
|
1598
1601
|
type: attachment.type,
|
|
1602
|
+
thumb_url: attachment.thumb_url,
|
|
1599
1603
|
});
|
|
1600
1604
|
}
|
|
1601
1605
|
});
|
|
@@ -1635,6 +1639,7 @@ class AttachmentService {
|
|
|
1635
1639
|
}
|
|
1636
1640
|
upload.state = r.state;
|
|
1637
1641
|
upload.url = r.url;
|
|
1642
|
+
upload.thumb_url = r.thumb_url;
|
|
1638
1643
|
if (upload.state === 'error') {
|
|
1639
1644
|
this.notificationService.addTemporaryNotification(upload.type === 'image'
|
|
1640
1645
|
? 'streamChat.Error uploading image'
|
|
@@ -1656,28 +1661,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImpor
|
|
|
1656
1661
|
}], ctorParameters: function () { return [{ type: ChannelService }, { type: NotificationService }]; } });
|
|
1657
1662
|
|
|
1658
1663
|
/**
|
|
1659
|
-
* The `AttachmentConfigurationService` provides customization for certain attributes of attachments displayed inside the message component.
|
|
1664
|
+
* The `AttachmentConfigurationService` provides customization for certain attributes of attachments displayed inside the message component. If you're using your own CDN, you can integrate resizing features of it by providing your own handlers.
|
|
1660
1665
|
*/
|
|
1661
1666
|
class AttachmentConfigurationService {
|
|
1667
|
+
constructor() {
|
|
1668
|
+
/**
|
|
1669
|
+
* You can turn on/off thumbnail generation for video attachments
|
|
1670
|
+
*/
|
|
1671
|
+
this.shouldGenerateVideoThumbnail = true;
|
|
1672
|
+
}
|
|
1662
1673
|
/**
|
|
1663
1674
|
* Handles the configuration for image attachments, it's possible to provide your own function to override the default logic
|
|
1664
1675
|
* @param attachment The attachment to configure
|
|
1665
1676
|
* @param location Specifies where the image is being displayed
|
|
1677
|
+
* @param element The default resizing logics reads the height/max-height and max-width propperties of this element and reduces file size based on the given values. File size reduction is done by Stream's CDN.
|
|
1666
1678
|
*/
|
|
1667
|
-
getImageAttachmentConfiguration(attachment, location) {
|
|
1679
|
+
getImageAttachmentConfiguration(attachment, location, element) {
|
|
1668
1680
|
if (this.customImageAttachmentConfigurationHandler) {
|
|
1669
|
-
return this.customImageAttachmentConfigurationHandler(attachment, location);
|
|
1681
|
+
return this.customImageAttachmentConfigurationHandler(attachment, location, element);
|
|
1682
|
+
}
|
|
1683
|
+
const url = new URL((attachment.img_url ||
|
|
1684
|
+
attachment.thumb_url ||
|
|
1685
|
+
attachment.image_url ||
|
|
1686
|
+
''));
|
|
1687
|
+
const { sizeRestriction, height } = this.getSizingRestrictions(url, element);
|
|
1688
|
+
if (sizeRestriction) {
|
|
1689
|
+
// Apply 2x for retina displays
|
|
1690
|
+
sizeRestriction.height *= 2;
|
|
1691
|
+
sizeRestriction.width *= 2;
|
|
1692
|
+
this.addResizingParamsToUrl(sizeRestriction, url);
|
|
1670
1693
|
}
|
|
1671
|
-
const height = {
|
|
1672
|
-
gallery: '',
|
|
1673
|
-
single: '300px',
|
|
1674
|
-
carousel: '', // Set from CSS
|
|
1675
|
-
}[location];
|
|
1676
1694
|
return {
|
|
1677
|
-
url:
|
|
1678
|
-
attachment.thumb_url ||
|
|
1679
|
-
attachment.image_url ||
|
|
1680
|
-
''),
|
|
1695
|
+
url: url.href,
|
|
1681
1696
|
width: '',
|
|
1682
1697
|
height,
|
|
1683
1698
|
};
|
|
@@ -1685,15 +1700,34 @@ class AttachmentConfigurationService {
|
|
|
1685
1700
|
/**
|
|
1686
1701
|
* Handles the configuration for video attachments, it's possible to provide your own function to override the default logic
|
|
1687
1702
|
* @param attachment The attachment to configure
|
|
1703
|
+
* @param element The default resizing logics reads the height/max-height and max-width propperties of this element and reduces file size based on the given values. File size reduction is done by Stream's CDN.
|
|
1688
1704
|
*/
|
|
1689
|
-
getVideoAttachmentConfiguration(attachment) {
|
|
1705
|
+
getVideoAttachmentConfiguration(attachment, element) {
|
|
1690
1706
|
if (this.customVideoAttachmentConfigurationHandler) {
|
|
1691
|
-
return this.customVideoAttachmentConfigurationHandler(attachment);
|
|
1707
|
+
return this.customVideoAttachmentConfigurationHandler(attachment, element);
|
|
1708
|
+
}
|
|
1709
|
+
let attachmentHeight = ``;
|
|
1710
|
+
let thumbUrl = undefined;
|
|
1711
|
+
if (attachment.thumb_url && this.shouldGenerateVideoThumbnail) {
|
|
1712
|
+
const url = new URL(attachment.thumb_url);
|
|
1713
|
+
const { sizeRestriction, height } = this.getSizingRestrictions(url, element);
|
|
1714
|
+
if (sizeRestriction) {
|
|
1715
|
+
sizeRestriction.height *= 2;
|
|
1716
|
+
sizeRestriction.width *= 2;
|
|
1717
|
+
this.addResizingParamsToUrl(sizeRestriction, url);
|
|
1718
|
+
}
|
|
1719
|
+
thumbUrl = url.href;
|
|
1720
|
+
attachmentHeight = height;
|
|
1721
|
+
}
|
|
1722
|
+
else {
|
|
1723
|
+
const cssSizeRestriction = this.getCSSSizeRestriction(element);
|
|
1724
|
+
attachmentHeight = `${cssSizeRestriction.maxHeight || cssSizeRestriction.height || ''}px`;
|
|
1692
1725
|
}
|
|
1693
1726
|
return {
|
|
1694
1727
|
url: attachment.asset_url || '',
|
|
1695
|
-
width: '
|
|
1696
|
-
height:
|
|
1728
|
+
width: '',
|
|
1729
|
+
height: attachmentHeight,
|
|
1730
|
+
thumbUrl: thumbUrl,
|
|
1697
1731
|
};
|
|
1698
1732
|
}
|
|
1699
1733
|
/**
|
|
@@ -1726,6 +1760,52 @@ class AttachmentConfigurationService {
|
|
|
1726
1760
|
height: '', // Set from CSS
|
|
1727
1761
|
};
|
|
1728
1762
|
}
|
|
1763
|
+
addResizingParamsToUrl(sizeRestriction, url) {
|
|
1764
|
+
url.searchParams.set('h', sizeRestriction.height.toString());
|
|
1765
|
+
url.searchParams.set('w', sizeRestriction.width.toString());
|
|
1766
|
+
}
|
|
1767
|
+
getSizingRestrictions(url, htmlElement) {
|
|
1768
|
+
const urlParams = url.searchParams;
|
|
1769
|
+
const originalHeight = Number(urlParams.get('oh')) || 1;
|
|
1770
|
+
const originalWidth = Number(urlParams.get('ow')) || 1;
|
|
1771
|
+
const cssSizeRestriction = this.getCSSSizeRestriction(htmlElement);
|
|
1772
|
+
let sizeRestriction;
|
|
1773
|
+
let height = '';
|
|
1774
|
+
if ((cssSizeRestriction.maxHeight || cssSizeRestriction.height) &&
|
|
1775
|
+
cssSizeRestriction.maxWidth) {
|
|
1776
|
+
sizeRestriction = this.getSizeRestrictions(originalHeight, originalWidth, (cssSizeRestriction.maxHeight || cssSizeRestriction.height), cssSizeRestriction.maxWidth);
|
|
1777
|
+
if (cssSizeRestriction.maxHeight) {
|
|
1778
|
+
const heightNum = originalHeight > 1 && originalWidth > 1
|
|
1779
|
+
? originalHeight <= cssSizeRestriction.maxHeight &&
|
|
1780
|
+
originalWidth <= cssSizeRestriction.maxWidth
|
|
1781
|
+
? originalHeight
|
|
1782
|
+
: Math.round(Math.min(cssSizeRestriction.maxHeight, (cssSizeRestriction.maxWidth / originalWidth) *
|
|
1783
|
+
originalHeight))
|
|
1784
|
+
: cssSizeRestriction.maxHeight;
|
|
1785
|
+
height = `${heightNum}px`;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
else {
|
|
1789
|
+
sizeRestriction = undefined;
|
|
1790
|
+
}
|
|
1791
|
+
return { sizeRestriction, height };
|
|
1792
|
+
}
|
|
1793
|
+
getSizeRestrictions(originalHeight, originalWidth, maxHeight, maxWidth) {
|
|
1794
|
+
return {
|
|
1795
|
+
height: Math.round(Math.max(maxHeight, (maxWidth / originalWidth) * originalHeight)),
|
|
1796
|
+
width: Math.round(Math.max(maxHeight, (maxWidth / originalHeight) * originalWidth)),
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
getCSSSizeRestriction(htmlElement) {
|
|
1800
|
+
const computedStylesheet = getComputedStyle(htmlElement);
|
|
1801
|
+
const height = this.getValueRepresentationOfCSSProperty(computedStylesheet.getPropertyValue('height'));
|
|
1802
|
+
const maxHeight = this.getValueRepresentationOfCSSProperty(computedStylesheet.getPropertyValue('max-height'));
|
|
1803
|
+
const maxWidth = this.getValueRepresentationOfCSSProperty(computedStylesheet.getPropertyValue('max-width'));
|
|
1804
|
+
return { height, maxHeight, maxWidth };
|
|
1805
|
+
}
|
|
1806
|
+
getValueRepresentationOfCSSProperty(property) {
|
|
1807
|
+
return Number(property.replace('px', '')) || undefined;
|
|
1808
|
+
}
|
|
1729
1809
|
}
|
|
1730
1810
|
AttachmentConfigurationService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentConfigurationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1731
1811
|
AttachmentConfigurationService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentConfigurationService, providedIn: 'root' });
|
|
@@ -2434,24 +2514,31 @@ class AttachmentListComponent {
|
|
|
2434
2514
|
this.orderedAttachments = [];
|
|
2435
2515
|
this.imagesToView = [];
|
|
2436
2516
|
this.imagesToViewCurrentIndex = 0;
|
|
2517
|
+
this.attachmentConfigurations = new Map();
|
|
2437
2518
|
this.themeVersion = themeService.themeVersion;
|
|
2438
2519
|
}
|
|
2439
|
-
ngOnChanges() {
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2520
|
+
ngOnChanges(changes) {
|
|
2521
|
+
if (changes.attachments) {
|
|
2522
|
+
const images = this.attachments.filter(this.isImage);
|
|
2523
|
+
const containsGallery = images.length >= 2;
|
|
2524
|
+
this.orderedAttachments = [
|
|
2525
|
+
...(containsGallery ? this.createGallery(images) : images),
|
|
2526
|
+
...this.attachments.filter((a) => this.isVideo(a)),
|
|
2527
|
+
...this.attachments.filter((a) => this.isFile(a)),
|
|
2528
|
+
];
|
|
2529
|
+
this.attachmentConfigurations = new Map();
|
|
2530
|
+
// Display link attachments only if there are no other attachments
|
|
2531
|
+
// Giphy-s always sent without other attachments
|
|
2532
|
+
if (this.orderedAttachments.length === 0) {
|
|
2533
|
+
this.orderedAttachments.push(...this.attachments.filter((a) => this.isCard(a)));
|
|
2534
|
+
}
|
|
2451
2535
|
}
|
|
2452
2536
|
}
|
|
2453
|
-
|
|
2454
|
-
return
|
|
2537
|
+
trackByUrl(_, attachment) {
|
|
2538
|
+
return (attachment.image_url ||
|
|
2539
|
+
attachment.img_url ||
|
|
2540
|
+
attachment.asset_url ||
|
|
2541
|
+
attachment.thumb_url);
|
|
2455
2542
|
}
|
|
2456
2543
|
isImage(attachment) {
|
|
2457
2544
|
return isImageAttachment(attachment);
|
|
@@ -2517,18 +2604,39 @@ class AttachmentListComponent {
|
|
|
2517
2604
|
trackByImageUrl(_, item) {
|
|
2518
2605
|
return item.image_url || item.img_url || item.thumb_url;
|
|
2519
2606
|
}
|
|
2520
|
-
getImageAttachmentConfiguration(attachment, type) {
|
|
2521
|
-
|
|
2607
|
+
getImageAttachmentConfiguration(attachment, type, element) {
|
|
2608
|
+
const existingConfiguration = this.attachmentConfigurations.get(attachment);
|
|
2609
|
+
if (existingConfiguration) {
|
|
2610
|
+
return existingConfiguration;
|
|
2611
|
+
}
|
|
2612
|
+
const configuration = this.attachmentConfigurationService.getImageAttachmentConfiguration(attachment, type, element);
|
|
2613
|
+
this.attachmentConfigurations.set(attachment, configuration);
|
|
2614
|
+
return configuration;
|
|
2615
|
+
}
|
|
2616
|
+
getCarouselImageAttachmentConfiguration(attachment, element) {
|
|
2617
|
+
return this.attachmentConfigurationService.getImageAttachmentConfiguration(attachment, 'carousel', element);
|
|
2522
2618
|
}
|
|
2523
|
-
getVideoAttachmentConfiguration(attachment) {
|
|
2524
|
-
|
|
2619
|
+
getVideoAttachmentConfiguration(attachment, element) {
|
|
2620
|
+
const existingConfiguration = this.attachmentConfigurations.get(attachment);
|
|
2621
|
+
if (existingConfiguration) {
|
|
2622
|
+
return existingConfiguration;
|
|
2623
|
+
}
|
|
2624
|
+
const configuration = this.attachmentConfigurationService.getVideoAttachmentConfiguration(attachment, element);
|
|
2625
|
+
this.attachmentConfigurations.set(attachment, configuration);
|
|
2626
|
+
return configuration;
|
|
2525
2627
|
}
|
|
2526
2628
|
getCardAttachmentConfiguration(attachment) {
|
|
2629
|
+
const existingConfiguration = this.attachmentConfigurations.get(attachment);
|
|
2630
|
+
if (existingConfiguration) {
|
|
2631
|
+
return existingConfiguration;
|
|
2632
|
+
}
|
|
2527
2633
|
if (attachment.type === 'giphy') {
|
|
2528
2634
|
return this.attachmentConfigurationService.getGiphyAttachmentConfiguration(attachment);
|
|
2529
2635
|
}
|
|
2530
2636
|
else {
|
|
2531
|
-
|
|
2637
|
+
const configuration = this.attachmentConfigurationService.getScrapedImageAttachmentConfiguration(attachment);
|
|
2638
|
+
this.attachmentConfigurations.set(attachment, configuration);
|
|
2639
|
+
return configuration;
|
|
2532
2640
|
}
|
|
2533
2641
|
}
|
|
2534
2642
|
get isImageModalPrevButtonVisible() {
|
|
@@ -2550,7 +2658,7 @@ class AttachmentListComponent {
|
|
|
2550
2658
|
}
|
|
2551
2659
|
}
|
|
2552
2660
|
AttachmentListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentListComponent, deps: [{ token: CustomTemplatesService }, { token: ChannelService }, { token: AttachmentConfigurationService }, { token: ThemeService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2553
|
-
AttachmentListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: AttachmentListComponent, selector: "stream-attachment-list", inputs: { messageId: "messageId", parentMessageId: "parentMessageId", attachments: "attachments" }, host: { properties: { "class": "this.class" } }, viewQueries: [{ propertyName: "modalContent", first: true, predicate: ["modalContent"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"orderedAttachments.length > 0\" class=\"str-chat__attachment-list\">\n <ng-container\n *ngFor=\"let attachment of orderedAttachments; trackBy: trackById\"\n >\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 [class.str-chat__message-attachment-with-actions]=\"\n attachment.actions && attachment.actions.length > 0\n \"\n [class.str-chat__message-attachment--svg-image]=\"isSvg(attachment)\"\n >\n <img\n *ngIf=\"\n isImage(attachment) &&\n getImageAttachmentConfiguration(\n attachment,\n 'single'\n ) as attachmentConfiguration\n \"\n class=\"str-chat__message-attachment--img\"\n data-testclass=\"image\"\n [src]=\"attachmentConfiguration.url\"\n [alt]=\"attachment?.fallback\"\n (click)=\"openImageModal([attachment])\"\n (keyup.enter)=\"openImageModal([attachment])\"\n [ngStyle]=\"{\n height: attachmentConfiguration.height,\n width: attachmentConfiguration.width\n }\"\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 [class.str-chat__gallery-two-rows]=\"(attachment?.images)!.length > 2\"\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 [class.str-chat__message-attachment--svg-image]=\"\n isSvg(galleryImage)\n \"\n >\n <img\n *ngIf=\"\n getImageAttachmentConfiguration(\n galleryImage,\n 'gallery'\n ) as attachmentConfiguration\n \"\n [src]=\"attachmentConfiguration.url\"\n [alt]=\"galleryImage.fallback\"\n [ngStyle]=\"{\n height: attachmentConfiguration.height,\n width: attachmentConfiguration.width\n }\"\n />\n </button>\n <button\n *ngIf=\"\n index === 3 &&\n !isLast &&\n getImageAttachmentConfiguration(\n galleryImage,\n 'gallery'\n ) as attachmentConfiguration\n \"\n class=\"str-chat__gallery-placeholder\"\n data-testclass=\"gallery-image\"\n data-testid=\"more-image-button\"\n (click)=\"openImageModal(attachment.images!, index)\"\n (keyup.enter)=\"openImageModal(attachment.images!, index)\"\n [class.str-chat__message-attachment--svg-image]=\"\n isSvg(galleryImage)\n \"\n [ngStyle]=\"{\n 'background-image': 'url(' + attachmentConfiguration.url + ')',\n height: attachmentConfiguration.height,\n width: attachmentConfiguration.width\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 class=\"str-chat__player-wrapper\"\n *ngIf=\"\n isVideo(attachment) &&\n getVideoAttachmentConfiguration(attachment) as attachmentConfiguration\n \"\n >\n <video\n class=\"str-chat__video-angular\"\n controls\n data-testclass=\"video-attachment\"\n [src]=\"attachmentConfiguration.url\"\n [ngStyle]=\"{\n height: attachmentConfiguration.height,\n width: attachmentConfiguration.width\n }\"\n ></video>\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 *ngIf=\"themeVersion === '1'\"\n icon=\"file\"\n [size]=\"30\"\n ></stream-icon-placeholder>\n <stream-icon-placeholder\n *ngIf=\"themeVersion === '2'\"\n icon=\"unspecified-filetype\"\n [size]=\"30\"\n ></stream-icon-placeholder>\n <div class=\"str-chat__message-attachment-file--item-text\">\n <div class=\"str-chat__message-attachment-file--item-first-row\">\n <div\n data-testclass=\"file-title\"\n class=\"str-chat__message-attachment-file--item-name\"\n >\n {{ attachment.title }}\n </div>\n <a\n class=\"str-chat__message-attachment-file--item-download\"\n data-testclass=\"file-link\"\n download\n href=\"{{ attachment.asset_url }}\"\n target=\"_blank\"\n >\n <stream-icon-placeholder\n class=\"str-chat__message-attachment-download-icon\"\n icon=\"download\"\n ></stream-icon-placeholder>\n </a>\n </div>\n <span\n class=\"str-chat__message-attachment-file--item-size\"\n data-testclass=\"size\"\n *ngIf=\"hasFileSize(attachment)\"\n >{{ getFileSize(attachment) }}</span\n >\n </div>\n </div>\n <div\n *ngIf=\"\n isCard(attachment) &&\n getCardAttachmentConfiguration(attachment) as attachmentConfiguration\n \"\n class=\"str-chat__message-attachment-card str-chat__message-attachment-card--{{\n attachment.type\n }}\"\n >\n <div\n *ngIf=\"attachmentConfiguration.url\"\n class=\"str-chat__message-attachment-card--header\"\n >\n <img\n data-testclass=\"card-img\"\n alt=\"{{ attachmentConfiguration.url }}\"\n src=\"{{ attachmentConfiguration.url }}\"\n [ngStyle]=\"{\n height: attachmentConfiguration.height,\n width: attachmentConfiguration.width\n }\"\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=\"\n let action of attachment.actions;\n trackBy: trackByActionValue\n \"\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</div>\n\n<ng-template\n #defaultModal\n let-isOpen=\"isOpen\"\n let-isOpenChangeHandler=\"isOpenChangeHandler\"\n let-content=\"content\"\n>\n <stream-modal\n class=\"stream-chat-angular__image-modal-host\"\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 str-chat__image-carousel\">\n <button\n class=\"\n stream-chat-angular__image-modal-stepper\n str-chat__image-carousel-stepper\n \"\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 *ngIf=\"\n getImageAttachmentConfiguration(\n imagesToView[imagesToViewCurrentIndex],\n 'carousel'\n ) as attachmentConfiguration\n \"\n class=\"\n stream-chat-angular__image-modal-image\n str-chat__image-carousel-image\n \"\n data-testid=\"modal-image\"\n [src]=\"attachmentConfiguration.url\"\n [alt]=\"imagesToView[imagesToViewCurrentIndex].fallback\"\n [ngStyle]=\"{\n width: attachmentConfiguration.width,\n height: attachmentConfiguration.height\n }\"\n />\n <button\n class=\"\n stream-chat-angular__image-modal-stepper\n str-chat__image-carousel-stepper\n \"\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: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], pipes: { "translate": i6.TranslatePipe, "async": i4.AsyncPipe } });
|
|
2661
|
+
AttachmentListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.5", type: AttachmentListComponent, selector: "stream-attachment-list", inputs: { messageId: "messageId", parentMessageId: "parentMessageId", attachments: "attachments" }, host: { properties: { "class": "this.class" } }, viewQueries: [{ propertyName: "modalContent", first: true, predicate: ["modalContent"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"orderedAttachments.length > 0\" class=\"str-chat__attachment-list\">\n <ng-container\n *ngFor=\"let attachment of orderedAttachments; trackBy: trackByUrl\"\n >\n <div\n data-testclass=\"attachment-container\"\n class=\"str-chat__message-attachment str-chat__message-attachment--{{\n attachment.type\n }} str-chat__message-attachment-dynamic-size\"\n [class.str-chat__message-attachment--card]=\"isCard(attachment)\"\n [class.str-chat-angular__message-attachment-file-single]=\"\n isFile(attachment)\n \"\n [class.str-chat__message-attachment-with-actions]=\"\n attachment.actions && attachment.actions.length > 0\n \"\n [class.str-chat__message-attachment--svg-image]=\"isSvg(attachment)\"\n >\n <img\n #imgElement\n *ngIf=\"isImage(attachment)\"\n class=\"str-chat__message-attachment--img\"\n data-testclass=\"image\"\n [src]=\"\n getImageAttachmentConfiguration(attachment, 'single', imgElement).url\n \"\n [alt]=\"attachment?.fallback\"\n (click)=\"openImageModal([attachment])\"\n (keyup.enter)=\"openImageModal([attachment])\"\n [ngStyle]=\"{\n height: getImageAttachmentConfiguration(\n attachment,\n 'single',\n imgElement\n ).height,\n width: getImageAttachmentConfiguration(\n attachment,\n 'single',\n imgElement\n ).width\n }\"\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 [class.str-chat__gallery-two-rows]=\"(attachment?.images)!.length > 2\"\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 [class.str-chat__message-attachment--svg-image]=\"\n isSvg(galleryImage)\n \"\n >\n <img\n #imgElement\n [src]=\"\n getImageAttachmentConfiguration(\n galleryImage,\n 'gallery',\n imgElement\n ).url\n \"\n [alt]=\"galleryImage.fallback\"\n [ngStyle]=\"{\n height: getImageAttachmentConfiguration(\n galleryImage,\n 'gallery',\n imgElement\n ).height,\n width: getImageAttachmentConfiguration(\n galleryImage,\n 'gallery',\n imgElement\n ).width\n }\"\n />\n </button>\n <button\n #element\n *ngIf=\"index === 3 && !isLast\"\n class=\"str-chat__gallery-placeholder\"\n data-testclass=\"gallery-image\"\n data-testid=\"more-image-button\"\n (click)=\"openImageModal(attachment.images!, index)\"\n (keyup.enter)=\"openImageModal(attachment.images!, index)\"\n [class.str-chat__message-attachment--svg-image]=\"\n isSvg(galleryImage)\n \"\n [ngStyle]=\"{\n 'background-image':\n 'url(' +\n getImageAttachmentConfiguration(\n galleryImage,\n 'gallery',\n element\n ).url +\n ')',\n height: getImageAttachmentConfiguration(\n galleryImage,\n 'gallery',\n element\n ).height,\n width: getImageAttachmentConfiguration(\n galleryImage,\n 'gallery',\n element\n ).width\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 class=\"str-chat__player-wrapper\" *ngIf=\"isVideo(attachment)\">\n <video\n #videoElement\n class=\"str-chat__video-angular\"\n controls\n data-testclass=\"video-attachment\"\n [src]=\"getVideoAttachmentConfiguration(attachment, videoElement).url\"\n [ngStyle]=\"{\n height: getVideoAttachmentConfiguration(attachment, videoElement)\n .height,\n width: getVideoAttachmentConfiguration(attachment, videoElement)\n .width\n }\"\n [poster]=\"\n getVideoAttachmentConfiguration(attachment, videoElement).thumbUrl\n \"\n ></video>\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 *ngIf=\"themeVersion === '1'\"\n icon=\"file\"\n [size]=\"30\"\n ></stream-icon-placeholder>\n <stream-icon-placeholder\n *ngIf=\"themeVersion === '2'\"\n icon=\"unspecified-filetype\"\n [size]=\"30\"\n ></stream-icon-placeholder>\n <div class=\"str-chat__message-attachment-file--item-text\">\n <div class=\"str-chat__message-attachment-file--item-first-row\">\n <div\n data-testclass=\"file-title\"\n class=\"str-chat__message-attachment-file--item-name\"\n >\n {{ attachment.title }}\n </div>\n <a\n class=\"str-chat__message-attachment-file--item-download\"\n data-testclass=\"file-link\"\n download\n href=\"{{ attachment.asset_url }}\"\n target=\"_blank\"\n >\n <stream-icon-placeholder\n class=\"str-chat__message-attachment-download-icon\"\n icon=\"download\"\n ></stream-icon-placeholder>\n </a>\n </div>\n <span\n class=\"str-chat__message-attachment-file--item-size\"\n data-testclass=\"size\"\n *ngIf=\"hasFileSize(attachment)\"\n >{{ getFileSize(attachment) }}</span\n >\n </div>\n </div>\n <div\n *ngIf=\"\n isCard(attachment) &&\n getCardAttachmentConfiguration(attachment) as attachmentConfiguration\n \"\n class=\"str-chat__message-attachment-card str-chat__message-attachment-card--{{\n attachment.type\n }}\"\n >\n <div\n *ngIf=\"attachmentConfiguration.url\"\n class=\"str-chat__message-attachment-card--header\"\n >\n <img\n data-testclass=\"card-img\"\n alt=\"{{ attachmentConfiguration.url }}\"\n src=\"{{ attachmentConfiguration.url }}\"\n [ngStyle]=\"{\n height: attachmentConfiguration.height,\n width: attachmentConfiguration.width\n }\"\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=\"\n let action of attachment.actions;\n trackBy: trackByActionValue\n \"\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</div>\n\n<ng-template\n #defaultModal\n let-isOpen=\"isOpen\"\n let-isOpenChangeHandler=\"isOpenChangeHandler\"\n let-content=\"content\"\n>\n <stream-modal\n class=\"stream-chat-angular__image-modal-host\"\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 str-chat__image-carousel\">\n <button\n class=\"\n stream-chat-angular__image-modal-stepper\n str-chat__image-carousel-stepper\n \"\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 #imgElement\n class=\"\n stream-chat-angular__image-modal-image\n str-chat__image-carousel-image\n \"\n data-testid=\"modal-image\"\n [src]=\"\n getCarouselImageAttachmentConfiguration(\n imagesToView[imagesToViewCurrentIndex],\n imgElement\n ).url\n \"\n [alt]=\"imagesToView[imagesToViewCurrentIndex].fallback\"\n [ngStyle]=\"{\n width: getCarouselImageAttachmentConfiguration(\n imagesToView[imagesToViewCurrentIndex],\n imgElement\n ).width,\n height: getCarouselImageAttachmentConfiguration(\n imagesToView[imagesToViewCurrentIndex],\n imgElement\n ).height\n }\"\n />\n <button\n class=\"\n stream-chat-angular__image-modal-stepper\n str-chat__image-carousel-stepper\n \"\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: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i4.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], pipes: { "translate": i6.TranslatePipe, "async": i4.AsyncPipe } });
|
|
2554
2662
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0, type: AttachmentListComponent, decorators: [{
|
|
2555
2663
|
type: Component,
|
|
2556
2664
|
args: [{
|
|
@@ -3777,8 +3885,14 @@ class MessageComponent {
|
|
|
3777
3885
|
this.subscriptions.push(this.customTemplatesService.messageReactionsTemplate$.subscribe((template) => (this.messageReactionsTemplate = template)));
|
|
3778
3886
|
}
|
|
3779
3887
|
ngOnChanges(changes) {
|
|
3888
|
+
var _a, _b;
|
|
3780
3889
|
if (changes.message) {
|
|
3781
3890
|
this.createMessageParts();
|
|
3891
|
+
const originalAttachments = (_b = (_a = this.message) === null || _a === void 0 ? void 0 : _a.quoted_message) === null || _b === void 0 ? void 0 : _b.attachments;
|
|
3892
|
+
this.quotedMessageAttachments =
|
|
3893
|
+
originalAttachments && originalAttachments.length
|
|
3894
|
+
? [originalAttachments[0]]
|
|
3895
|
+
: [];
|
|
3782
3896
|
}
|
|
3783
3897
|
if (changes.enabledMessageActions) {
|
|
3784
3898
|
this.canReactToMessage =
|
|
@@ -3855,13 +3969,6 @@ class MessageComponent {
|
|
|
3855
3969
|
return (this.canReceiveReadEvents !== false &&
|
|
3856
3970
|
this.enabledMessageActions.indexOf('read-events') !== -1);
|
|
3857
3971
|
}
|
|
3858
|
-
get quotedMessageAttachments() {
|
|
3859
|
-
var _a, _b;
|
|
3860
|
-
const originalAttachments = (_b = (_a = this.message) === null || _a === void 0 ? void 0 : _a.quoted_message) === null || _b === void 0 ? void 0 : _b.attachments;
|
|
3861
|
-
return originalAttachments && originalAttachments.length
|
|
3862
|
-
? [originalAttachments[0]]
|
|
3863
|
-
: [];
|
|
3864
|
-
}
|
|
3865
3972
|
getAttachmentListContext() {
|
|
3866
3973
|
var _a, _b, _c;
|
|
3867
3974
|
return {
|
|
@@ -4605,7 +4712,7 @@ class MessageListComponent {
|
|
|
4605
4712
|
(((_b = this.parentMessageElement) === null || _b === void 0 ? void 0 : _b.nativeElement.clientHeight) || 0))) {
|
|
4606
4713
|
position = 'top';
|
|
4607
4714
|
}
|
|
4608
|
-
else if (Math.
|
|
4715
|
+
else if (Math.ceil(this.scrollContainer.nativeElement.scrollTop) +
|
|
4609
4716
|
this.scrollContainer.nativeElement.clientHeight >=
|
|
4610
4717
|
this.scrollContainer.nativeElement.scrollHeight) {
|
|
4611
4718
|
position = 'bottom';
|