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.
Files changed (32) hide show
  1. package/assets/version.d.ts +1 -1
  2. package/bundles/stream-chat-angular.umd.js +148 -47
  3. package/bundles/stream-chat-angular.umd.js.map +1 -1
  4. package/esm2015/assets/version.js +2 -2
  5. package/esm2015/lib/attachment-configuration.service.js +92 -17
  6. package/esm2015/lib/attachment-list/attachment-list.component.js +49 -21
  7. package/esm2015/lib/attachment.service.js +4 -1
  8. package/esm2015/lib/channel.service.js +5 -3
  9. package/esm2015/lib/message/message.component.js +7 -8
  10. package/esm2015/lib/message-list/message-list.component.js +2 -2
  11. package/esm2015/lib/types.js +1 -1
  12. package/fesm2015/stream-chat-angular.js +154 -47
  13. package/fesm2015/stream-chat-angular.js.map +1 -1
  14. package/lib/attachment-configuration.service.d.ts +17 -6
  15. package/lib/attachment-list/attachment-list.component.d.ts +9 -7
  16. package/lib/message/message.component.d.ts +1 -20
  17. package/lib/types.d.ts +4 -0
  18. package/package.json +1 -1
  19. package/src/assets/styles/css/index.css +1 -1
  20. package/src/assets/styles/css/index.css.map +1 -1
  21. package/src/assets/styles/scss/Attachment.scss +45 -2
  22. package/src/assets/styles/scss/Gallery.scss +12 -6
  23. package/src/assets/styles/scss/Message.scss +2 -1
  24. package/src/assets/styles/v2/css/index.css +1 -1
  25. package/src/assets/styles/v2/css/index.css.map +1 -1
  26. package/src/assets/styles/v2/css/index.layout.css +1 -1
  27. package/src/assets/styles/v2/css/index.layout.css.map +1 -1
  28. package/src/assets/styles/v2/scss/AttachmentList/AttachmentList-layout.scss +72 -46
  29. package/src/assets/styles/v2/scss/Message/Message-layout.scss +0 -16
  30. package/src/assets/styles/v2/scss/MessageReactions/MessageReactions-layout.scss +1 -10
  31. package/src/assets/styles/v2/scss/Tooltip/Tooltip-layout.scss +2 -23
  32. package/src/assets/version.ts +1 -1
@@ -1 +1 @@
1
- export declare const version = "4.1.0";
1
+ export declare const version = "4.2.0";
@@ -355,7 +355,7 @@
355
355
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
356
356
  }
357
357
 
358
- var version = '4.1.0';
358
+ var version = '4.2.0';
359
359
 
360
360
  /**
361
361
  * The `NotificationService` can be used to add or remove notifications. By default the [`NotificationList`](../components/NotificationListComponent.mdx) component displays the currently active notifications.
@@ -1156,10 +1156,10 @@
1156
1156
  return [4 /*yield*/, Promise.allSettled(uploads.map(function (upload) { return upload.type === 'image'
1157
1157
  ? _this.customImageUploadRequest
1158
1158
  ? _this.customImageUploadRequest(upload.file, channel)
1159
- : channel.sendImage(upload.file)
1159
+ : channel.sendImage(upload.file, upload.file.name, upload.file.type)
1160
1160
  : _this.customFileUploadRequest
1161
1161
  ? _this.customFileUploadRequest(upload.file, channel)
1162
- : channel.sendFile(upload.file); }))];
1162
+ : channel.sendFile(upload.file, upload.file.name, upload.file.type); }))];
1163
1163
  case 1:
1164
1164
  uploadResults = _h.sent();
1165
1165
  uploadResults.forEach(function (uploadResult, i) {
@@ -1171,6 +1171,8 @@
1171
1171
  type: type,
1172
1172
  state: 'success',
1173
1173
  url: uploadResult.value.file,
1174
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
1175
+ thumb_url: uploadResult.value.thumb_url,
1174
1176
  });
1175
1177
  }
1176
1178
  else {
@@ -2234,6 +2236,7 @@
2234
2236
  attachment.asset_url = r.url;
2235
2237
  attachment.title = (_b = r.file) === null || _b === void 0 ? void 0 : _b.name;
2236
2238
  attachment.file_size = (_c = r.file) === null || _c === void 0 ? void 0 : _c.size;
2239
+ attachment.thumb_url = r.thumb_url;
2237
2240
  }
2238
2241
  return attachment;
2239
2242
  });
@@ -2266,6 +2269,7 @@
2266
2269
  size: attachment.file_size,
2267
2270
  },
2268
2271
  type: attachment.type,
2272
+ thumb_url: attachment.thumb_url,
2269
2273
  });
2270
2274
  }
2271
2275
  });
@@ -2310,6 +2314,7 @@
2310
2314
  }
2311
2315
  upload.state = r.state;
2312
2316
  upload.url = r.url;
2317
+ upload.thumb_url = r.thumb_url;
2313
2318
  if (upload.state === 'error') {
2314
2319
  _this.notificationService.addTemporaryNotification(upload.type === 'image'
2315
2320
  ? 'streamChat.Error uploading image'
@@ -2335,30 +2340,38 @@
2335
2340
  }], ctorParameters: function () { return [{ type: ChannelService }, { type: NotificationService }]; } });
2336
2341
 
2337
2342
  /**
2338
- * The `AttachmentConfigurationService` provides customization for certain attributes of attachments displayed inside the message component.
2343
+ * 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.
2339
2344
  */
2340
2345
  var AttachmentConfigurationService = /** @class */ (function () {
2341
2346
  function AttachmentConfigurationService() {
2347
+ /**
2348
+ * You can turn on/off thumbnail generation for video attachments
2349
+ */
2350
+ this.shouldGenerateVideoThumbnail = true;
2342
2351
  }
2343
2352
  /**
2344
2353
  * Handles the configuration for image attachments, it's possible to provide your own function to override the default logic
2345
2354
  * @param attachment The attachment to configure
2346
2355
  * @param location Specifies where the image is being displayed
2356
+ * @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.
2347
2357
  */
2348
- AttachmentConfigurationService.prototype.getImageAttachmentConfiguration = function (attachment, location) {
2358
+ AttachmentConfigurationService.prototype.getImageAttachmentConfiguration = function (attachment, location, element) {
2349
2359
  if (this.customImageAttachmentConfigurationHandler) {
2350
- return this.customImageAttachmentConfigurationHandler(attachment, location);
2360
+ return this.customImageAttachmentConfigurationHandler(attachment, location, element);
2361
+ }
2362
+ var url = new URL((attachment.img_url ||
2363
+ attachment.thumb_url ||
2364
+ attachment.image_url ||
2365
+ ''));
2366
+ var _b = this.getSizingRestrictions(url, element), sizeRestriction = _b.sizeRestriction, height = _b.height;
2367
+ if (sizeRestriction) {
2368
+ // Apply 2x for retina displays
2369
+ sizeRestriction.height *= 2;
2370
+ sizeRestriction.width *= 2;
2371
+ this.addResizingParamsToUrl(sizeRestriction, url);
2351
2372
  }
2352
- var height = {
2353
- gallery: '',
2354
- single: '300px',
2355
- carousel: '', // Set from CSS
2356
- }[location];
2357
2373
  return {
2358
- url: (attachment.img_url ||
2359
- attachment.thumb_url ||
2360
- attachment.image_url ||
2361
- ''),
2374
+ url: url.href,
2362
2375
  width: '',
2363
2376
  height: height,
2364
2377
  };
@@ -2366,15 +2379,34 @@
2366
2379
  /**
2367
2380
  * Handles the configuration for video attachments, it's possible to provide your own function to override the default logic
2368
2381
  * @param attachment The attachment to configure
2382
+ * @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.
2369
2383
  */
2370
- AttachmentConfigurationService.prototype.getVideoAttachmentConfiguration = function (attachment) {
2384
+ AttachmentConfigurationService.prototype.getVideoAttachmentConfiguration = function (attachment, element) {
2371
2385
  if (this.customVideoAttachmentConfigurationHandler) {
2372
- return this.customVideoAttachmentConfigurationHandler(attachment);
2386
+ return this.customVideoAttachmentConfigurationHandler(attachment, element);
2387
+ }
2388
+ var attachmentHeight = "";
2389
+ var thumbUrl = undefined;
2390
+ if (attachment.thumb_url && this.shouldGenerateVideoThumbnail) {
2391
+ var url = new URL(attachment.thumb_url);
2392
+ var _b = this.getSizingRestrictions(url, element), sizeRestriction = _b.sizeRestriction, height = _b.height;
2393
+ if (sizeRestriction) {
2394
+ sizeRestriction.height *= 2;
2395
+ sizeRestriction.width *= 2;
2396
+ this.addResizingParamsToUrl(sizeRestriction, url);
2397
+ }
2398
+ thumbUrl = url.href;
2399
+ attachmentHeight = height;
2400
+ }
2401
+ else {
2402
+ var cssSizeRestriction = this.getCSSSizeRestriction(element);
2403
+ attachmentHeight = (cssSizeRestriction.maxHeight || cssSizeRestriction.height || '') + "px";
2373
2404
  }
2374
2405
  return {
2375
2406
  url: attachment.asset_url || '',
2376
- width: '100%',
2377
- height: '100%',
2407
+ width: '',
2408
+ height: attachmentHeight,
2409
+ thumbUrl: thumbUrl,
2378
2410
  };
2379
2411
  };
2380
2412
  /**
@@ -2407,6 +2439,52 @@
2407
2439
  height: '', // Set from CSS
2408
2440
  };
2409
2441
  };
2442
+ AttachmentConfigurationService.prototype.addResizingParamsToUrl = function (sizeRestriction, url) {
2443
+ url.searchParams.set('h', sizeRestriction.height.toString());
2444
+ url.searchParams.set('w', sizeRestriction.width.toString());
2445
+ };
2446
+ AttachmentConfigurationService.prototype.getSizingRestrictions = function (url, htmlElement) {
2447
+ var urlParams = url.searchParams;
2448
+ var originalHeight = Number(urlParams.get('oh')) || 1;
2449
+ var originalWidth = Number(urlParams.get('ow')) || 1;
2450
+ var cssSizeRestriction = this.getCSSSizeRestriction(htmlElement);
2451
+ var sizeRestriction;
2452
+ var height = '';
2453
+ if ((cssSizeRestriction.maxHeight || cssSizeRestriction.height) &&
2454
+ cssSizeRestriction.maxWidth) {
2455
+ sizeRestriction = this.getSizeRestrictions(originalHeight, originalWidth, (cssSizeRestriction.maxHeight || cssSizeRestriction.height), cssSizeRestriction.maxWidth);
2456
+ if (cssSizeRestriction.maxHeight) {
2457
+ var heightNum = originalHeight > 1 && originalWidth > 1
2458
+ ? originalHeight <= cssSizeRestriction.maxHeight &&
2459
+ originalWidth <= cssSizeRestriction.maxWidth
2460
+ ? originalHeight
2461
+ : Math.round(Math.min(cssSizeRestriction.maxHeight, (cssSizeRestriction.maxWidth / originalWidth) *
2462
+ originalHeight))
2463
+ : cssSizeRestriction.maxHeight;
2464
+ height = heightNum + "px";
2465
+ }
2466
+ }
2467
+ else {
2468
+ sizeRestriction = undefined;
2469
+ }
2470
+ return { sizeRestriction: sizeRestriction, height: height };
2471
+ };
2472
+ AttachmentConfigurationService.prototype.getSizeRestrictions = function (originalHeight, originalWidth, maxHeight, maxWidth) {
2473
+ return {
2474
+ height: Math.round(Math.max(maxHeight, (maxWidth / originalWidth) * originalHeight)),
2475
+ width: Math.round(Math.max(maxHeight, (maxWidth / originalHeight) * originalWidth)),
2476
+ };
2477
+ };
2478
+ AttachmentConfigurationService.prototype.getCSSSizeRestriction = function (htmlElement) {
2479
+ var computedStylesheet = getComputedStyle(htmlElement);
2480
+ var height = this.getValueRepresentationOfCSSProperty(computedStylesheet.getPropertyValue('height'));
2481
+ var maxHeight = this.getValueRepresentationOfCSSProperty(computedStylesheet.getPropertyValue('max-height'));
2482
+ var maxWidth = this.getValueRepresentationOfCSSProperty(computedStylesheet.getPropertyValue('max-width'));
2483
+ return { height: height, maxHeight: maxHeight, maxWidth: maxWidth };
2484
+ };
2485
+ AttachmentConfigurationService.prototype.getValueRepresentationOfCSSProperty = function (property) {
2486
+ return Number(property.replace('px', '')) || undefined;
2487
+ };
2410
2488
  return AttachmentConfigurationService;
2411
2489
  }());
2412
2490
  AttachmentConfigurationService.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0__namespace, type: AttachmentConfigurationService, deps: [], target: i0__namespace.ɵɵFactoryTarget.Injectable });
@@ -3138,22 +3216,29 @@
3138
3216
  this.orderedAttachments = [];
3139
3217
  this.imagesToView = [];
3140
3218
  this.imagesToViewCurrentIndex = 0;
3219
+ this.attachmentConfigurations = new Map();
3141
3220
  this.themeVersion = themeService.themeVersion;
3142
3221
  }
3143
- AttachmentListComponent.prototype.ngOnChanges = function () {
3222
+ AttachmentListComponent.prototype.ngOnChanges = function (changes) {
3144
3223
  var _a;
3145
3224
  var _this = this;
3146
- var images = this.attachments.filter(this.isImage);
3147
- var containsGallery = images.length >= 2;
3148
- this.orderedAttachments = __spreadArray(__spreadArray(__spreadArray([], __read((containsGallery ? this.createGallery(images) : images))), __read(this.attachments.filter(function (a) { return _this.isVideo(a); }))), __read(this.attachments.filter(function (a) { return _this.isFile(a); })));
3149
- // Display link attachments only if there are no other attachments
3150
- // Giphy-s always sent without other attachments
3151
- if (this.orderedAttachments.length === 0) {
3152
- (_a = this.orderedAttachments).push.apply(_a, __spreadArray([], __read(this.attachments.filter(function (a) { return _this.isCard(a); }))));
3225
+ if (changes.attachments) {
3226
+ var images = this.attachments.filter(this.isImage);
3227
+ var containsGallery = images.length >= 2;
3228
+ this.orderedAttachments = __spreadArray(__spreadArray(__spreadArray([], __read((containsGallery ? this.createGallery(images) : images))), __read(this.attachments.filter(function (a) { return _this.isVideo(a); }))), __read(this.attachments.filter(function (a) { return _this.isFile(a); })));
3229
+ this.attachmentConfigurations = new Map();
3230
+ // Display link attachments only if there are no other attachments
3231
+ // Giphy-s always sent without other attachments
3232
+ if (this.orderedAttachments.length === 0) {
3233
+ (_a = this.orderedAttachments).push.apply(_a, __spreadArray([], __read(this.attachments.filter(function (a) { return _this.isCard(a); }))));
3234
+ }
3153
3235
  }
3154
3236
  };
3155
- AttachmentListComponent.prototype.trackById = function (index) {
3156
- return index;
3237
+ AttachmentListComponent.prototype.trackByUrl = function (_, attachment) {
3238
+ return (attachment.image_url ||
3239
+ attachment.img_url ||
3240
+ attachment.asset_url ||
3241
+ attachment.thumb_url);
3157
3242
  };
3158
3243
  AttachmentListComponent.prototype.isImage = function (attachment) {
3159
3244
  return isImageAttachment(attachment);
@@ -3222,18 +3307,39 @@
3222
3307
  AttachmentListComponent.prototype.trackByImageUrl = function (_, item) {
3223
3308
  return item.image_url || item.img_url || item.thumb_url;
3224
3309
  };
3225
- AttachmentListComponent.prototype.getImageAttachmentConfiguration = function (attachment, type) {
3226
- return this.attachmentConfigurationService.getImageAttachmentConfiguration(attachment, type);
3310
+ AttachmentListComponent.prototype.getImageAttachmentConfiguration = function (attachment, type, element) {
3311
+ var existingConfiguration = this.attachmentConfigurations.get(attachment);
3312
+ if (existingConfiguration) {
3313
+ return existingConfiguration;
3314
+ }
3315
+ var configuration = this.attachmentConfigurationService.getImageAttachmentConfiguration(attachment, type, element);
3316
+ this.attachmentConfigurations.set(attachment, configuration);
3317
+ return configuration;
3227
3318
  };
3228
- AttachmentListComponent.prototype.getVideoAttachmentConfiguration = function (attachment) {
3229
- return this.attachmentConfigurationService.getVideoAttachmentConfiguration(attachment);
3319
+ AttachmentListComponent.prototype.getCarouselImageAttachmentConfiguration = function (attachment, element) {
3320
+ return this.attachmentConfigurationService.getImageAttachmentConfiguration(attachment, 'carousel', element);
3321
+ };
3322
+ AttachmentListComponent.prototype.getVideoAttachmentConfiguration = function (attachment, element) {
3323
+ var existingConfiguration = this.attachmentConfigurations.get(attachment);
3324
+ if (existingConfiguration) {
3325
+ return existingConfiguration;
3326
+ }
3327
+ var configuration = this.attachmentConfigurationService.getVideoAttachmentConfiguration(attachment, element);
3328
+ this.attachmentConfigurations.set(attachment, configuration);
3329
+ return configuration;
3230
3330
  };
3231
3331
  AttachmentListComponent.prototype.getCardAttachmentConfiguration = function (attachment) {
3332
+ var existingConfiguration = this.attachmentConfigurations.get(attachment);
3333
+ if (existingConfiguration) {
3334
+ return existingConfiguration;
3335
+ }
3232
3336
  if (attachment.type === 'giphy') {
3233
3337
  return this.attachmentConfigurationService.getGiphyAttachmentConfiguration(attachment);
3234
3338
  }
3235
3339
  else {
3236
- return this.attachmentConfigurationService.getScrapedImageAttachmentConfiguration(attachment);
3340
+ var configuration = this.attachmentConfigurationService.getScrapedImageAttachmentConfiguration(attachment);
3341
+ this.attachmentConfigurations.set(attachment, configuration);
3342
+ return configuration;
3237
3343
  }
3238
3344
  };
3239
3345
  Object.defineProperty(AttachmentListComponent.prototype, "isImageModalPrevButtonVisible", {
@@ -3264,7 +3370,7 @@
3264
3370
  return AttachmentListComponent;
3265
3371
  }());
3266
3372
  AttachmentListComponent.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0__namespace, type: AttachmentListComponent, deps: [{ token: CustomTemplatesService }, { token: ChannelService }, { token: AttachmentConfigurationService }, { token: ThemeService }], target: i0__namespace.ɵɵFactoryTarget.Component });
3267
- AttachmentListComponent.ɵcmp = i0__namespace.ɵɵ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__namespace, 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__namespace.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4__namespace.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4__namespace.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i4__namespace.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], pipes: { "translate": i6__namespace.TranslatePipe, "async": i4__namespace.AsyncPipe } });
3373
+ AttachmentListComponent.ɵcmp = i0__namespace.ɵɵ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__namespace, 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__namespace.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4__namespace.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4__namespace.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i4__namespace.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], pipes: { "translate": i6__namespace.TranslatePipe, "async": i4__namespace.AsyncPipe } });
3268
3374
  i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.5", ngImport: i0__namespace, type: AttachmentListComponent, decorators: [{
3269
3375
  type: i0.Component,
3270
3376
  args: [{
@@ -4646,8 +4752,14 @@
4646
4752
  this.subscriptions.push(this.customTemplatesService.messageReactionsTemplate$.subscribe(function (template) { return (_this.messageReactionsTemplate = template); }));
4647
4753
  };
4648
4754
  MessageComponent.prototype.ngOnChanges = function (changes) {
4755
+ var _a, _b;
4649
4756
  if (changes.message) {
4650
4757
  this.createMessageParts();
4758
+ var originalAttachments = (_b = (_a = this.message) === null || _a === void 0 ? void 0 : _a.quoted_message) === null || _b === void 0 ? void 0 : _b.attachments;
4759
+ this.quotedMessageAttachments =
4760
+ originalAttachments && originalAttachments.length
4761
+ ? [originalAttachments[0]]
4762
+ : [];
4651
4763
  }
4652
4764
  if (changes.enabledMessageActions) {
4653
4765
  this.canReactToMessage =
@@ -4777,17 +4889,6 @@
4777
4889
  enumerable: false,
4778
4890
  configurable: true
4779
4891
  });
4780
- Object.defineProperty(MessageComponent.prototype, "quotedMessageAttachments", {
4781
- get: function () {
4782
- var _a, _b;
4783
- var originalAttachments = (_b = (_a = this.message) === null || _a === void 0 ? void 0 : _a.quoted_message) === null || _b === void 0 ? void 0 : _b.attachments;
4784
- return originalAttachments && originalAttachments.length
4785
- ? [originalAttachments[0]]
4786
- : [];
4787
- },
4788
- enumerable: false,
4789
- configurable: true
4790
- });
4791
4892
  MessageComponent.prototype.getAttachmentListContext = function () {
4792
4893
  var _a, _b, _c;
4793
4894
  return {
@@ -5564,7 +5665,7 @@
5564
5665
  (((_b = this.parentMessageElement) === null || _b === void 0 ? void 0 : _b.nativeElement.clientHeight) || 0))) {
5565
5666
  position = 'top';
5566
5667
  }
5567
- else if (Math.round(this.scrollContainer.nativeElement.scrollTop) +
5668
+ else if (Math.ceil(this.scrollContainer.nativeElement.scrollTop) +
5568
5669
  this.scrollContainer.nativeElement.clientHeight >=
5569
5670
  this.scrollContainer.nativeElement.scrollHeight) {
5570
5671
  position = 'bottom';