@banta/sdk 4.3.2 → 4.4.1

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/bundles/banta-sdk.umd.js +574 -117
  2. package/bundles/banta-sdk.umd.js.map +1 -1
  3. package/bundles/banta-sdk.umd.min.js +1 -1
  4. package/bundles/banta-sdk.umd.min.js.map +1 -1
  5. package/esm2015/lib/attachment-scraper.js +87 -0
  6. package/esm2015/lib/banta-sdk.module.js +6 -2
  7. package/esm2015/lib/chat-backend-base.js +20 -1
  8. package/esm2015/lib/chat-backend.js +19 -1
  9. package/esm2015/lib/comments/banta-comments/banta-comments.component.js +1 -1
  10. package/esm2015/lib/comments/comment/comment.component.js +9 -12
  11. package/esm2015/lib/comments/comment-field/comment-field.component.js +89 -7
  12. package/esm2015/lib/comments/comments.module.js +6 -2
  13. package/esm2015/lib/common/attachment/attachment.component.js +55 -0
  14. package/esm2015/lib/common/attachments/attachments.component.js +47 -0
  15. package/esm2015/lib/common/common.module.js +13 -3
  16. package/esm2015/lib/common/index.js +4 -1
  17. package/esm2015/lib/common/trust-resource-url.pipe.js +21 -0
  18. package/esm2015/lib/emoji/emoji-selector-button.component.js +56 -105
  19. package/esm2015/lib/emoji/emoji.module.js +6 -2
  20. package/fesm2015/banta-sdk.js +404 -127
  21. package/fesm2015/banta-sdk.js.map +1 -1
  22. package/lib/attachment-scraper.d.ts +30 -0
  23. package/lib/chat-backend-base.d.ts +9 -0
  24. package/lib/chat-backend.d.ts +2 -1
  25. package/lib/comments/comment/comment.component.d.ts +2 -4
  26. package/lib/comments/comment-field/comment-field.component.d.ts +11 -2
  27. package/lib/common/attachment/attachment.component.d.ts +18 -0
  28. package/lib/common/attachments/attachments.component.d.ts +15 -0
  29. package/lib/common/index.d.ts +3 -0
  30. package/lib/common/trust-resource-url.pipe.d.ts +7 -0
  31. package/lib/emoji/emoji-selector-button.component.d.ts +13 -14
  32. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('rxjs/operators'), require('@angular/core'), require('marked'), require('dompurify'), require('@angular/platform-browser'), require('@angular/common'), require('@angular/material/icon'), require('@angular/material/button'), require('@angular/material/form-field'), require('@angular/material/input'), require('@angular/forms'), require('@angular/material/dialog'), require('@banta/common'), require('@angular/router'), require('@angular/material/snack-bar'), require('@angular/material/menu'), require('@angular/material/progress-spinner'), require('@angular/cdk/text-field'), require('@angular/material/tooltip'), require('@angular/material/select')) :
3
- typeof define === 'function' && define.amd ? define('@banta/sdk', ['exports', 'rxjs', 'rxjs/operators', '@angular/core', 'marked', 'dompurify', '@angular/platform-browser', '@angular/common', '@angular/material/icon', '@angular/material/button', '@angular/material/form-field', '@angular/material/input', '@angular/forms', '@angular/material/dialog', '@banta/common', '@angular/router', '@angular/material/snack-bar', '@angular/material/menu', '@angular/material/progress-spinner', '@angular/cdk/text-field', '@angular/material/tooltip', '@angular/material/select'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.banta = global.banta || {}, global.banta.sdk = {}), global.rxjs, global.rxjs.operators, global.ng.core, global.marked, global.DOMPurify, global.ng.platformBrowser, global.ng.common, global.ng.material.icon, global.ng.material.button, global.ng.material.formField, global.ng.material.input, global.ng.forms, global.ng.material.dialog, global.common$1, global.ng.router, global.ng.material.snackBar, global.ng.material.menu, global.ng.material.progressSpinner, global.ng.cdk.textField, global.ng.material.tooltip, global.ng.material.select));
5
- }(this, (function (exports, rxjs, operators, core, marked, DOMPurify, platformBrowser, common, icon, button, formField, input, forms, dialog, common$1, router, snackBar, menu, progressSpinner, textField, tooltip, select) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rxjs'), require('rxjs/operators'), require('@angular/core'), require('marked'), require('dompurify'), require('@angular/platform-browser'), require('@angular/common'), require('@angular/material/icon'), require('@angular/material/progress-spinner'), require('@angular/material/button'), require('@angular/cdk/overlay'), require('@angular/material/form-field'), require('@angular/material/input'), require('@angular/forms'), require('@angular/cdk/portal'), require('@angular/material/dialog'), require('@banta/common'), require('@angular/router'), require('@angular/material/snack-bar'), require('@angular/material/menu'), require('@angular/cdk/text-field'), require('@angular/material/tooltip'), require('@angular/material/select')) :
3
+ typeof define === 'function' && define.amd ? define('@banta/sdk', ['exports', 'rxjs', 'rxjs/operators', '@angular/core', 'marked', 'dompurify', '@angular/platform-browser', '@angular/common', '@angular/material/icon', '@angular/material/progress-spinner', '@angular/material/button', '@angular/cdk/overlay', '@angular/material/form-field', '@angular/material/input', '@angular/forms', '@angular/cdk/portal', '@angular/material/dialog', '@banta/common', '@angular/router', '@angular/material/snack-bar', '@angular/material/menu', '@angular/cdk/text-field', '@angular/material/tooltip', '@angular/material/select'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.banta = global.banta || {}, global.banta.sdk = {}), global.rxjs, global.rxjs.operators, global.ng.core, global.marked, global.DOMPurify, global.ng.platformBrowser, global.ng.common, global.ng.material.icon, global.ng.material.progressSpinner, global.ng.material.button, global.ng.cdk.overlay, global.ng.material.formField, global.ng.material.input, global.ng.forms, global.ng.cdk.portal, global.ng.material.dialog, global.common$1, global.ng.router, global.ng.material.snackBar, global.ng.material.menu, global.ng.cdk.textField, global.ng.material.tooltip, global.ng.material.select));
5
+ }(this, (function (exports, rxjs, operators, core, marked, DOMPurify, platformBrowser, common, icon, progressSpinner, button, overlay, formField, input, forms, portal, dialog, common$1, router, snackBar, menu, textField, tooltip, select) { 'use strict';
6
6
 
7
7
  function lazyConnection(options) {
8
8
  var obs = new rxjs.Observable(function (observer) {
@@ -207,10 +207,156 @@
207
207
  { type: platformBrowser.DomSanitizer }
208
208
  ]; };
209
209
 
210
+ var BantaTrustResourceUrlPipe = /** @class */ (function () {
211
+ function BantaTrustResourceUrlPipe(sanitizer) {
212
+ this.sanitizer = sanitizer;
213
+ }
214
+ BantaTrustResourceUrlPipe.prototype.transform = function (value) {
215
+ if (!value)
216
+ return undefined;
217
+ return this.sanitizer.bypassSecurityTrustResourceUrl(value);
218
+ };
219
+ return BantaTrustResourceUrlPipe;
220
+ }());
221
+ BantaTrustResourceUrlPipe.decorators = [
222
+ { type: core.Pipe, args: [{
223
+ name: 'trustResourceUrl'
224
+ },] }
225
+ ];
226
+ BantaTrustResourceUrlPipe.ctorParameters = function () { return [
227
+ { type: platformBrowser.DomSanitizer }
228
+ ]; };
229
+
230
+ var BantaAttachmentComponent = /** @class */ (function () {
231
+ function BantaAttachmentComponent() {
232
+ this.loading = false;
233
+ this.editing = false;
234
+ this.loadingMessage = 'Please wait...';
235
+ this.error = false;
236
+ this.errorMessage = 'An error has occurred';
237
+ this.removed = new rxjs.Subject();
238
+ this.activated = new rxjs.Subject();
239
+ }
240
+ BantaAttachmentComponent.prototype.activate = function () {
241
+ this.activated.next();
242
+ };
243
+ BantaAttachmentComponent.prototype.remove = function () {
244
+ this.removed.next();
245
+ };
246
+ Object.defineProperty(BantaAttachmentComponent.prototype, "isError", {
247
+ get: function () {
248
+ var _a, _b;
249
+ return this.error || ((_b = (_a = this.attachment) === null || _a === void 0 ? void 0 : _a.transientState) === null || _b === void 0 ? void 0 : _b.error);
250
+ },
251
+ enumerable: false,
252
+ configurable: true
253
+ });
254
+ Object.defineProperty(BantaAttachmentComponent.prototype, "theErrorMessage", {
255
+ get: function () {
256
+ var _a, _b;
257
+ return this.errorMessage || ((_b = (_a = this.attachment) === null || _a === void 0 ? void 0 : _a.transientState) === null || _b === void 0 ? void 0 : _b.errorMessage);
258
+ },
259
+ enumerable: false,
260
+ configurable: true
261
+ });
262
+ Object.defineProperty(BantaAttachmentComponent.prototype, "isLoading", {
263
+ get: function () {
264
+ var _a;
265
+ return this.loading || !this.attachment || ((_a = this.attachment.transientState) === null || _a === void 0 ? void 0 : _a.loading) || !this.attachment.url;
266
+ },
267
+ enumerable: false,
268
+ configurable: true
269
+ });
270
+ Object.defineProperty(BantaAttachmentComponent.prototype, "isImageAttachment", {
271
+ get: function () {
272
+ if (this.attachment.type.startsWith('image/'))
273
+ return true;
274
+ return false;
275
+ },
276
+ enumerable: false,
277
+ configurable: true
278
+ });
279
+ return BantaAttachmentComponent;
280
+ }());
281
+ BantaAttachmentComponent.decorators = [
282
+ { type: core.Component, args: [{
283
+ selector: 'banta-attachment',
284
+ template: "<button type=\"button\" (click)=\"remove()\" mat-mini-fab color=\"primary\" class=\"remove-button\" *ngIf=\"editing\">\r\n <mat-icon>close</mat-icon>\r\n</button>\r\n\r\n<ng-container *ngIf=\"isError\">\r\n <mat-icon class=\"error\">close</mat-icon>\r\n <em class=\"error\">{{theErrorMessage}}</em>\r\n</ng-container>\r\n<ng-container *ngIf=\"!isError\">\r\n <ng-container *ngIf=\"isLoading\">\r\n <mat-spinner></mat-spinner>\r\n <em>{{loadingMessage}}</em>\r\n </ng-container>\r\n <ng-container *ngIf=\"!isLoading\">\r\n <iframe \r\n *ngIf=\"attachment.type === 'iframe'\" \r\n sandbox=\"allow-scripts allow-popups allow-same-origin allow-presentation\" \r\n [src]=\"attachment.url | trustResourceUrl\"></iframe>\r\n <a *ngIf=\"attachment.type === 'card'\" class=\"card\" [href]=\"attachment.url\" target=\"_blank\">\r\n <img \r\n *ngIf=\"attachment.card.image\"\r\n class=\"thumbnail\" \r\n [src]=\"attachment.card.image\"\r\n />\r\n <div class=\"description\">\r\n <h1>{{attachment.card.title}}</h1>\r\n {{attachment.card.description}}\r\n </div>\r\n </a>\r\n <a class=\"image-attachment\" *ngIf=\"isImageAttachment\" href=\"javascript:;\" (click)=\"activate()\">\r\n <img [src]=\"attachment.url\" alt=\"Image Attachment\">\r\n </a>\r\n </ng-container>\r\n</ng-container>",
285
+ styles: [":host{position:relative;display:block}:host.loading{outline:1px solid #333;padding:1em 0;width:300px;text-align:center}:host.loading mat-spinner{display:block;margin:0 auto .5em;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}a.card{display:flex;align-items:flex-start;gap:1em;width:100%;border:1px solid #666;border-radius:4px;padding:1em;box-sizing:border-box;background-color:#191919}a.card img{width:300px;aspect-ratio:16/9;-o-object-fit:cover;object-fit:cover;border-radius:10px}a.card h1{margin:0;font-size:30px}.remove-button{position:absolute;right:10px;top:10px;margin:0;z-index:1}a.image-attachment{width:300px;position:relative;text-align:center}a.image-attachment.with-border{outline:1px solid #333;padding:1em 0}a.image-attachment mat-spinner{display:block;margin:0 auto .5em;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}a.image-attachment mat-icon.error{display:block;font-size:48px;width:48px;height:48px;margin:0 auto .5em}a.image-attachment .error{color:#b76363}a.image-attachment img{width:300px;border-radius:10px;max-width:100%;max-height:20em;-o-object-fit:cover;object-fit:cover}iframe{border:none;width:100%;aspect-ratio:16/9}"]
286
+ },] }
287
+ ];
288
+ BantaAttachmentComponent.propDecorators = {
289
+ attachment: [{ type: core.Input }],
290
+ loading: [{ type: core.Input }],
291
+ editing: [{ type: core.Input }],
292
+ loadingMessage: [{ type: core.Input }],
293
+ error: [{ type: core.Input }],
294
+ errorMessage: [{ type: core.Input }],
295
+ removed: [{ type: core.Output }],
296
+ activated: [{ type: core.Output }],
297
+ isLoading: [{ type: core.HostBinding, args: ['class.loading',] }]
298
+ };
299
+
300
+ var BantaAttachmentsComponent = /** @class */ (function () {
301
+ function BantaAttachmentsComponent() {
302
+ this.editing = false;
303
+ this.remove = new rxjs.Subject();
304
+ }
305
+ BantaAttachmentsComponent.prototype.removeAttachment = function (attachment) {
306
+ this.remove.next(attachment);
307
+ };
308
+ BantaAttachmentsComponent.prototype.isImageAttachment = function (attachment) {
309
+ if (attachment.type.startsWith('image/'))
310
+ return true;
311
+ return false;
312
+ };
313
+ BantaAttachmentsComponent.prototype.isCardAttachment = function (attachment) {
314
+ if (['card'].includes(attachment.type))
315
+ return true;
316
+ return false;
317
+ };
318
+ BantaAttachmentsComponent.prototype.showLightbox = function (image) {
319
+ this.lightbox.open(image.url, this.attachments
320
+ .filter(function (x) { return x.type === 'image/png'; })
321
+ .map(function (x) { return x.url; }));
322
+ };
323
+ Object.defineProperty(BantaAttachmentsComponent.prototype, "inlineAttachments", {
324
+ get: function () {
325
+ return this.attachments.filter(function (x) { return x.type !== 'card' && (x.style === 'inline' || !x.style); });
326
+ },
327
+ enumerable: false,
328
+ configurable: true
329
+ });
330
+ Object.defineProperty(BantaAttachmentsComponent.prototype, "blockAttachments", {
331
+ get: function () {
332
+ return this.attachments.filter(function (x) { return x.style === 'block' || x.type === 'card'; });
333
+ },
334
+ enumerable: false,
335
+ configurable: true
336
+ });
337
+ return BantaAttachmentsComponent;
338
+ }());
339
+ BantaAttachmentsComponent.decorators = [
340
+ { type: core.Component, args: [{
341
+ selector: 'banta-attachments',
342
+ template: "<ng-container *ngIf=\"attachments?.length > 0\">\r\n <banta-lightbox #lightbox></banta-lightbox>\r\n <div class=\"block-attachments\">\r\n <ng-container *ngFor=\"let attachment of blockAttachments\">\r\n <banta-attachment \r\n [attachment]=\"attachment\"\r\n [editing]=\"editing\"\r\n (removed)=\"removeAttachment(attachment)\"\r\n (activated)=\"showLightbox(attachment)\"\r\n ></banta-attachment>\r\n </ng-container>\r\n </div>\r\n\r\n <div \r\n class=\"inline-attachments\" \r\n [class.single]=\"attachments?.length === 1\" \r\n *ngIf=\"attachments && attachments?.length > 0\"\r\n >\r\n <ng-container *ngFor=\"let attachment of inlineAttachments\">\r\n <banta-attachment \r\n [attachment]=\"attachment\"\r\n [editing]=\"editing\"\r\n (removed)=\"removeAttachment(attachment)\"\r\n (activated)=\"showLightbox(attachment)\"\r\n ></banta-attachment>\r\n </ng-container>\r\n </div>\r\n</ng-container>",
343
+ styles: [".block-attachments{display:flex;flex-direction:column}.block-attachments banta-attachment{width:100%}.inline-attachments{flex-direction:row;margin-top:15px;display:flex;gap:20px;flex-wrap:wrap}"]
344
+ },] }
345
+ ];
346
+ BantaAttachmentsComponent.propDecorators = {
347
+ attachments: [{ type: core.Input }],
348
+ editing: [{ type: core.Input }],
349
+ lightbox: [{ type: core.ViewChild, args: ['lightbox',] }],
350
+ remove: [{ type: core.Output }]
351
+ };
352
+
210
353
  var COMPONENTS = [
211
354
  TimestampComponent,
212
355
  LightboxComponent,
213
- BantaMarkdownToHtmlPipe
356
+ BantaMarkdownToHtmlPipe,
357
+ BantaTrustResourceUrlPipe,
358
+ BantaAttachmentComponent,
359
+ BantaAttachmentsComponent
214
360
  ];
215
361
  var BantaCommonModule = /** @class */ (function () {
216
362
  function BantaCommonModule() {
@@ -222,7 +368,9 @@
222
368
  declarations: COMPONENTS,
223
369
  imports: [
224
370
  common.CommonModule,
225
- icon.MatIconModule
371
+ icon.MatIconModule,
372
+ progressSpinner.MatProgressSpinnerModule,
373
+ button.MatButtonModule
226
374
  ],
227
375
  exports: COMPONENTS
228
376
  },] }
@@ -7166,8 +7314,9 @@
7166
7314
 
7167
7315
  /// <reference types="@types/resize-observer-browser" />
7168
7316
  var EmojiSelectorButtonComponent = /** @class */ (function () {
7169
- function EmojiSelectorButtonComponent(elementRef) {
7317
+ function EmojiSelectorButtonComponent(elementRef, overlay) {
7170
7318
  this.elementRef = elementRef;
7319
+ this.overlay = overlay;
7171
7320
  this._selected = new rxjs.Subject();
7172
7321
  this.showEmojiPanel = false;
7173
7322
  }
@@ -7178,115 +7327,76 @@
7178
7327
  enumerable: false,
7179
7328
  configurable: true
7180
7329
  });
7181
- EmojiSelectorButtonComponent.prototype.ngOnDestroy = function () {
7182
- this.removeListener();
7183
- this.panelElement.nativeElement.remove();
7184
- };
7185
- Object.defineProperty(EmojiSelectorButtonComponent.prototype, "widthConstrained", {
7186
- get: function () { return this.width < 700; },
7330
+ Object.defineProperty(EmojiSelectorButtonComponent.prototype, "isOpen", {
7331
+ get: function () {
7332
+ return this.overlayRef;
7333
+ },
7187
7334
  enumerable: false,
7188
7335
  configurable: true
7189
7336
  });
7190
- EmojiSelectorButtonComponent.prototype.ngAfterViewInit = function () {
7191
- };
7192
- EmojiSelectorButtonComponent.prototype.putPanelAtRoot = function () {
7193
- // If we are in full-screen, placing the panel outside of the full-screen element will result in it
7194
- // always being behind said full-screen element, so we need to ensure we never place it further up the
7195
- // stack.
7196
- var root = document.fullscreenElement || document.body.querySelector('[ng-version]') || document.body;
7197
- root.appendChild(this.panelElement.nativeElement);
7198
- };
7199
- EmojiSelectorButtonComponent.prototype.removeListener = function () {
7200
- document.removeEventListener('click', this.clickListener);
7201
- window.removeEventListener('resize', this.resizeListener);
7202
- };
7203
- EmojiSelectorButtonComponent.prototype.place = function () {
7204
- // Not currently used as it can't be easily done handling all
7205
- // scrolling corner cases.
7206
- this.putPanelAtRoot();
7207
- var pos = this.buttonElement.nativeElement.getBoundingClientRect();
7208
- var size = this.panelElement.nativeElement.getBoundingClientRect();
7209
- var left = window.scrollX + pos.left + pos.width - size.width;
7210
- if (left < 0)
7211
- left = (window.scrollX + window.innerWidth) / 2 - size.width / 2;
7212
- var scrollY = window.scrollY;
7213
- if (document.fullscreenElement) {
7337
+ /**
7338
+ * Insert the given emoji.
7339
+ * @param str
7340
+ */
7341
+ EmojiSelectorButtonComponent.prototype.insert = function (str) {
7342
+ this._selected.next(str);
7343
+ };
7344
+ EmojiSelectorButtonComponent.prototype.close = function () {
7345
+ if (this.overlayRef) {
7346
+ this.overlayRef.dispose();
7347
+ this.overlayRef = null;
7348
+ return;
7214
7349
  }
7215
- Object.assign(this.panelElement.nativeElement.style, {
7216
- top: window.scrollY + pos.top + pos.height + "px",
7217
- left: Math.max(0, left) + "px"
7218
- });
7219
7350
  };
7220
7351
  EmojiSelectorButtonComponent.prototype.show = function () {
7221
7352
  var _this = this;
7222
- if (this.showEmojiPanel) {
7223
- this.showEmojiPanel = false;
7224
- return;
7353
+ if (this.isOpen) {
7354
+ this.close();
7225
7355
  }
7226
- this.showEmojiPanel = true;
7227
- //this.place();
7228
- setTimeout(function () {
7229
- var onResize = function () {
7230
- if (!_this.showEmojiPanel)
7231
- return;
7232
- _this.width = window.innerWidth;
7233
- _this.height = window.innerHeight;
7234
- var edgeOffset = 0;
7235
- var commentField = _this.elementRef.nativeElement.closest("banta-comment-field");
7236
- if (commentField) {
7237
- var size = commentField.getBoundingClientRect();
7238
- _this.width = size.width;
7239
- edgeOffset = window.innerWidth - size.right;
7240
- }
7241
- var buttonRect = _this.buttonElement.nativeElement.getBoundingClientRect();
7242
- var buttonRight = window.innerWidth - buttonRect.right - edgeOffset - 10;
7243
- if (_this.width < 700) {
7244
- _this.panelElement.nativeElement.style.right = -buttonRight + "px";
7245
- }
7246
- else {
7247
- _this.panelElement.nativeElement.style.right = '';
7248
- }
7249
- _this.panelElement.nativeElement.style.maxWidth = _this.width - 15 + "px";
7250
- };
7251
- _this.resizeListener = onResize;
7252
- onResize();
7253
- _this.clickListener = function (ev) {
7254
- var parent = ev.target;
7255
- var isInDialog = false;
7256
- while (parent) {
7257
- if (parent.matches('emoji-selector-panel'))
7258
- isInDialog = true;
7259
- parent = parent.parentElement;
7356
+ this.overlayRef = this.overlay.create({
7357
+ positionStrategy: this.overlay.position()
7358
+ .flexibleConnectedTo(this.elementRef)
7359
+ .withPositions([
7360
+ {
7361
+ originX: 'end',
7362
+ originY: 'bottom',
7363
+ overlayX: 'end',
7364
+ overlayY: 'top'
7260
7365
  }
7261
- if (isInDialog)
7262
- return;
7263
- _this.showEmojiPanel = false;
7264
- _this.removeListener();
7265
- };
7266
- document.addEventListener('click', _this.clickListener);
7267
- window.addEventListener('resize', _this.resizeListener);
7366
+ ])
7367
+ .withFlexibleDimensions(true),
7368
+ hasBackdrop: true,
7369
+ disposeOnNavigation: true,
7370
+ scrollStrategy: this.overlay.scrollStrategies.reposition({
7371
+ autoClose: true
7372
+ })
7268
7373
  });
7269
- };
7270
- EmojiSelectorButtonComponent.prototype.insert = function (str) {
7271
- this._selected.next(str);
7374
+ this.overlayRef.backdropClick().subscribe(function () {
7375
+ _this.close();
7376
+ });
7377
+ this.overlayRef.keydownEvents().subscribe(function (event) {
7378
+ if (event.key === 'Escape') {
7379
+ _this.close();
7380
+ }
7381
+ });
7382
+ this.overlayRef.attach(this.selectorPanelTemplate);
7272
7383
  };
7273
7384
  return EmojiSelectorButtonComponent;
7274
7385
  }());
7275
7386
  EmojiSelectorButtonComponent.decorators = [
7276
7387
  { type: core.Component, args: [{
7277
7388
  selector: 'emoji-selector-button',
7278
- template: "\n <button #button type=\"button\" mat-icon-button (click)=\"show()\">\n <mat-icon>emoji_emotions</mat-icon>\n </button>\n <emoji-selector-panel \n #panel\n (selected)=\"insert($event)\"\n [class.visible]=\"showEmojiPanel\"\n ></emoji-selector-panel>\n ",
7279
- styles: ["\n :host {\n display: block;\n position: relative;\n }\n\n emoji-selector-panel {\n position: absolute;\n top: 2.5em;\n right: 0;\n opacity: 0;\n pointer-events: none;\n z-index: 10;\n }\n\n emoji-selector-panel.visible {\n pointer-events: initial;\n opacity: 1;\n }\n\n button {\n color: #666\n }\n "]
7389
+ template: "\n <button #button type=\"button\" mat-icon-button (click)=\"show()\">\n <mat-icon>emoji_emotions</mat-icon>\n </button>\n <ng-template cdkPortal #selectorPanelTemplate=\"cdkPortal\">\n <emoji-selector-panel \n #panel\n (selected)=\"insert($event)\"\n ></emoji-selector-panel>\n </ng-template>\n ",
7390
+ styles: ["\n :host {\n display: block;\n position: relative;\n }\n\n button {\n color: #666\n }\n "]
7280
7391
  },] }
7281
7392
  ];
7282
7393
  EmojiSelectorButtonComponent.ctorParameters = function () { return [
7283
- { type: core.ElementRef }
7394
+ { type: core.ElementRef },
7395
+ { type: overlay.Overlay }
7284
7396
  ]; };
7285
7397
  EmojiSelectorButtonComponent.propDecorators = {
7286
- selected: [{ type: core.Output }],
7287
- panelElement: [{ type: core.ViewChild, args: ['panel', { read: core.ElementRef },] }],
7288
- buttonElement: [{ type: core.ViewChild, args: ['button', { read: core.ElementRef },] }],
7289
- widthConstrained: [{ type: core.HostBinding, args: ['class.width-constrained',] }]
7398
+ selectorPanelTemplate: [{ type: core.ViewChild, args: ['selectorPanelTemplate',] }],
7399
+ selected: [{ type: core.Output }]
7290
7400
  };
7291
7401
 
7292
7402
  var COMPONENTS$1 = [
@@ -7307,15 +7417,131 @@
7307
7417
  icon.MatIconModule,
7308
7418
  button.MatButtonModule,
7309
7419
  formField.MatFormFieldModule,
7310
- input.MatInputModule
7420
+ input.MatInputModule,
7421
+ overlay.OverlayModule,
7422
+ portal.PortalModule
7311
7423
  ],
7312
7424
  exports: COMPONENTS$1
7313
7425
  },] }
7314
7426
  ];
7315
7427
 
7428
+ var URL_REGEX = new RegExp('(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?', 'ig');
7429
+ var UrlAttachmentScraper = /** @class */ (function () {
7430
+ function UrlAttachmentScraper() {
7431
+ }
7432
+ UrlAttachmentScraper.prototype.findFragments = function (message) {
7433
+ var _a;
7434
+ // If a message already has a URL attachment, don't add another one.
7435
+ if (message.attachments && message.attachments.filter(function (x) { return x.type === 'url'; }).length > 0)
7436
+ return null;
7437
+ return (Array.from((_a = message.message.match(URL_REGEX)) !== null && _a !== void 0 ? _a : []))
7438
+ .reduce(function (a, item) { return (a.includes(item) ? undefined : a.push(item), a); }, [])
7439
+ .map(function (url) { return ({
7440
+ text: url,
7441
+ offset: message.message.indexOf(url),
7442
+ type: 'url'
7443
+ }); });
7444
+ };
7445
+ return UrlAttachmentScraper;
7446
+ }());
7447
+ var GiphyAttachmentResolver = /** @class */ (function () {
7448
+ function GiphyAttachmentResolver() {
7449
+ }
7450
+ GiphyAttachmentResolver.prototype.resolveFragment = function (message, fragment) {
7451
+ var _a;
7452
+ return __awaiter(this, void 0, void 0, function () {
7453
+ var gifId;
7454
+ return __generator(this, function (_b) {
7455
+ if (fragment.type === 'url' && fragment.text.startsWith('https://giphy.com/gifs')) {
7456
+ gifId = (_a = /[^-\/]+$/.exec(fragment.text)) === null || _a === void 0 ? void 0 : _a.toString();
7457
+ if (!gifId)
7458
+ return [2 /*return*/, null];
7459
+ return [2 /*return*/, {
7460
+ type: 'iframe',
7461
+ url: "https://giphy.com/embed/" + gifId,
7462
+ style: 'inline'
7463
+ }];
7464
+ }
7465
+ return [2 /*return*/, null];
7466
+ });
7467
+ });
7468
+ };
7469
+ return GiphyAttachmentResolver;
7470
+ }());
7471
+ var YouTubeAttachmentResolver = /** @class */ (function () {
7472
+ function YouTubeAttachmentResolver() {
7473
+ }
7474
+ YouTubeAttachmentResolver.prototype.resolveFragment = function (message, fragment) {
7475
+ return __awaiter(this, void 0, void 0, function () {
7476
+ var videoId, match;
7477
+ return __generator(this, function (_b) {
7478
+ if (fragment.type !== 'url')
7479
+ return [2 /*return*/, null];
7480
+ if (fragment.text.match(/https?:\/\/(www\.)?youtube.com\/watch\?v=/)) {
7481
+ match = /watch\?v=([^&]+)/.exec(fragment.text);
7482
+ if (match) {
7483
+ videoId = match[1];
7484
+ }
7485
+ }
7486
+ if (videoId) {
7487
+ return [2 /*return*/, {
7488
+ type: 'iframe',
7489
+ url: "https://www.youtube.com/embed/" + videoId,
7490
+ style: 'block'
7491
+ }];
7492
+ }
7493
+ return [2 /*return*/, null];
7494
+ });
7495
+ });
7496
+ };
7497
+ return YouTubeAttachmentResolver;
7498
+ }());
7499
+ var UrlAttachmentResolver = /** @class */ (function () {
7500
+ function UrlAttachmentResolver(backend) {
7501
+ this.backend = backend;
7502
+ }
7503
+ UrlAttachmentResolver.prototype.resolveFragment = function (message, fragment) {
7504
+ return __awaiter(this, void 0, void 0, function () {
7505
+ var urlCard;
7506
+ return __generator(this, function (_b) {
7507
+ switch (_b.label) {
7508
+ case 0:
7509
+ if (fragment.type !== 'url')
7510
+ return [2 /*return*/, null];
7511
+ return [4 /*yield*/, this.backend.getCardForUrl(fragment.text)];
7512
+ case 1:
7513
+ urlCard = _b.sent();
7514
+ if (urlCard) {
7515
+ return [2 /*return*/, {
7516
+ type: 'card',
7517
+ url: fragment.text,
7518
+ card: urlCard,
7519
+ style: 'block'
7520
+ }];
7521
+ }
7522
+ return [2 /*return*/];
7523
+ }
7524
+ });
7525
+ });
7526
+ };
7527
+ return UrlAttachmentResolver;
7528
+ }());
7529
+ UrlAttachmentResolver.decorators = [
7530
+ { type: core.Injectable }
7531
+ ];
7532
+ UrlAttachmentResolver.ctorParameters = function () { return [
7533
+ { type: ChatBackendBase }
7534
+ ]; };
7535
+
7316
7536
  var ChatBackendBase = /** @class */ (function () {
7317
7537
  function ChatBackendBase() {
7318
7538
  this._userChanged = new rxjs.BehaviorSubject(null);
7539
+ this._attachmentScrapers = [];
7540
+ this._attachmentResolvers = [];
7541
+ this.registerAttachmentScraper(new UrlAttachmentScraper());
7542
+ this.registerAttachmentResolver(new GiphyAttachmentResolver());
7543
+ this.registerAttachmentResolver(new YouTubeAttachmentResolver());
7544
+ this.registerAttachmentResolver(new UrlAttachmentResolver(this));
7319
7545
  }
7320
7546
  Object.defineProperty(ChatBackendBase.prototype, "userChanged", {
7321
7547
  get: function () {
@@ -7335,6 +7561,26 @@
7335
7561
  enumerable: false,
7336
7562
  configurable: true
7337
7563
  });
7564
+ ChatBackendBase.prototype.registerAttachmentScraper = function (scraper) {
7565
+ this._attachmentScrapers.push(scraper);
7566
+ };
7567
+ ChatBackendBase.prototype.registerAttachmentResolver = function (resolver) {
7568
+ this._attachmentResolvers.push(resolver);
7569
+ };
7570
+ Object.defineProperty(ChatBackendBase.prototype, "attachmentScrapers", {
7571
+ get: function () {
7572
+ return this._attachmentScrapers.slice();
7573
+ },
7574
+ enumerable: false,
7575
+ configurable: true
7576
+ });
7577
+ Object.defineProperty(ChatBackendBase.prototype, "attachmentResolvers", {
7578
+ get: function () {
7579
+ return this._attachmentResolvers.slice();
7580
+ },
7581
+ enumerable: false,
7582
+ configurable: true
7583
+ });
7338
7584
  return ChatBackendBase;
7339
7585
  }());
7340
7586
 
@@ -8385,11 +8631,6 @@
8385
8631
  this._avatarSelected.next(user);
8386
8632
  this.selectUser();
8387
8633
  };
8388
- CommentComponent.prototype.showLightbox = function (image) {
8389
- this.lightbox.open(image.url, this.message.attachments
8390
- .filter(function (x) { return x.type === 'image/png'; })
8391
- .map(function (x) { return x.url; }));
8392
- };
8393
8634
  CommentComponent.prototype.avatarForUser = function (user) {
8394
8635
  var url = this.genericAvatarUrl;
8395
8636
  if (user && user.avatarUrl) {
@@ -8397,13 +8638,21 @@
8397
8638
  }
8398
8639
  return "url(" + url + ")";
8399
8640
  };
8641
+ Object.defineProperty(CommentComponent.prototype, "replyCount", {
8642
+ get: function () {
8643
+ var _a;
8644
+ return ((_a = this.message.submessages) === null || _a === void 0 ? void 0 : _a.length) || this.message.submessageCount || 0;
8645
+ },
8646
+ enumerable: false,
8647
+ configurable: true
8648
+ });
8400
8649
  return CommentComponent;
8401
8650
  }());
8402
8651
  CommentComponent.decorators = [
8403
8652
  { type: core.Component, args: [{
8404
8653
  selector: 'banta-comment',
8405
- template: "\r\n<mat-menu #pointItemMenu=\"matMenu\">\r\n <button *ngIf=\"!mine\" mat-menu-item (click)=\"report()\">Report</button>\r\n <button *ngIf=\"mine\" [disabled]=\"!permissions?.canEdit\" mat-menu-item (click)=\"startEdit()\">Edit</button>\r\n <button *ngIf=\"mine\" [disabled]=\"!permissions?.canDelete\" mat-menu-item (click)=\"delete()\">Delete</button>\r\n</mat-menu>\r\n\r\n<div class=\"message-content\">\r\n <div class=\"user\">\r\n <a\r\n href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"selectAvatar(message.user)\"\r\n [style.background-image]=\"avatarForUser(message.user)\"></a>\r\n <a href=\"javascript:;\" class=\"display-name\" (click)=\"selectUser()\">{{message.user.displayName}}</a>\r\n <a href=\"javascript:;\" class=\"username\" (click)=\"selectUsername(message.user)\">@{{message.user.username}}</a>\r\n <span class=\"user-tag\" *ngIf=\"message.user.tag\">{{message.user.tag}}</span>\r\n <span class=\"spacer\"></span>\r\n </div>\r\n <div class=\"content\" *ngIf=\"!editing\">\r\n <span class=\"banta-message-content\" [innerHTML]=\"message.message | markdownToHtml\"></span>\r\n\r\n <ng-container *ngIf=\"message.attachments?.length > 0\">\r\n <banta-lightbox #lightbox></banta-lightbox>\r\n <div class=\"attachments-row\" [class.single]=\"message.attachments?.length === 1\" *ngIf=\"message.attachments && message.attachments?.length > 0\">\r\n <a \r\n href=\"javascript:;\" \r\n (click)=\"showLightbox(attachment)\"\r\n *ngFor=\"let attachment of message.attachments\" \r\n >\r\n <img [src]=\"attachment.url\" alt=\"\">\r\n </a>\r\n </div>\r\n </ng-container>\r\n\r\n <ul class=\"message-facts small\">\r\n <li *ngIf=\"message.edits?.length > 0\">(Edited)</li>\r\n </ul>\r\n </div>\r\n <div class=\"content\" *ngIf=\"editing\" style=\"padding-bottom: 2em;\">\r\n <div>\r\n <mat-form-field floatLabel=\"always\" appearance=\"outline\" style=\"width: 100%;\">\r\n <mat-label>Edit Message</mat-label>\r\n <textarea matInput [(ngModel)]=\"editedMessage\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n <button mat-raised-button (click)=\"saveEdit()\">Save</button> &nbsp;\r\n <button mat-button (click)=\"endEditing()\">Cancel</button>\r\n </div>\r\n\r\n\r\n <div class=\"actions\">\r\n <banta-timestamp [value]=\"message.sentAt\"></banta-timestamp>\r\n <ul class=\"message-facts\">\r\n <li *ngIf=\"message.edits?.length > 0\">Edited</li>\r\n </ul>\r\n <div class=\"spacer\"></div>\r\n <div class=\"counted-action\" *ngIf=\"showReplyAction\">\r\n <div class=\"count-indicator\">\r\n {{message.submessages?.length || message.submessageCount || 0}}\r\n </div>\r\n <button mat-icon-button matTooltip=\"Replies\" matTooltipPosition=\"below\" (click)=\"select()\">\r\n <mat-icon [inline]=\"true\">comment</mat-icon>\r\n </button>\r\n </div>\r\n <div class=\"counted-action\" [class.active]=\"message.userState?.liked\">\r\n <div class=\"count-indicator\">\r\n {{message.likes}}\r\n </div>\r\n <button \r\n *ngIf=\"message.transientState?.liking\"\r\n mat-icon-button \r\n [disabled]=\"true\" \r\n [matTooltip]=\"upvoting ? 'Please wait...' : message.userState?.liked ? 'Unlike' : 'Like'\" \r\n matTooltipPosition=\"below\" \r\n >\r\n <mat-spinner [diameter]=\"15\" style=\"margin-left: 1em;\"></mat-spinner>\r\n </button>\r\n <button \r\n *ngIf=\"!message.transientState?.liking\"\r\n mat-icon-button \r\n [disabled]=\"!permissions?.canLike\" \r\n [matTooltip]=\"upvoting ? 'Please wait...' : 'Like'\" \r\n matTooltipPosition=\"below\" \r\n (click)=\"message.userState?.liked ? unlike() : like()\" \r\n >\r\n <mat-icon [inline]=\"true\">thumb_up</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"counted-action\">\r\n <button mat-icon-button matTooltip=\"Share this comment\" matTooltipPosition=\"below\" (click)=\"share()\">\r\n <mat-icon [inline]=\"true\" >share</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button mat-icon-button [matMenuTriggerFor]=\"pointItemMenu\">\r\n <mat-icon [inline]=\"true\">more_vert</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n",
8406
- styles: ["@-webkit-keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}@keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}:host{display:flex;flex-direction:column;position:relative;padding:.5em;visibility:hidden}:host.new{visibility:visible;-webkit-animation-name:comment-appear;animation-name:comment-appear;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}:host.highlighted{background:#00223a;outline:2px solid #003277}:host.visible{visibility:visible}:host:hover{background:#eee}:host .message-content .content{margin-left:60px;margin-right:.5em}:host .message-content .attachments-row{margin-top:15px;display:flex;gap:10px}:host .message-content .attachments-row img{border-radius:10px;width:300px;max-width:100%;max-height:20em;-o-object-fit:cover;object-fit:cover}:host.abbreviated .message-content .content{text-overflow:ellipsis;overflow-y:hidden}:host .actions{display:flex;padding-right:10px;margin-left:60px;align-items:center}:host .actions button{color:#666;flex-shrink:0}:host .actions banta-timestamp{color:#666;font-size:10pt;flex-shrink:0}.user{position:relative;margin:1em 0 0;display:flex;align-items:center}.user .display-name,.user .username{z-index:1;position:relative;padding:0 0 0 1em;font-size:10pt;color:#000;margin:0 auto 0 0;display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex-shrink:1;flex-grow:0}.user .display-name.username.username.username,.user .username.username.username.username{color:#666}.avatar{height:48px;width:48px;background-position:50%;background-size:cover;background-color:#333;border-radius:100%;flex-shrink:0;flex-grow:0}.counted-action{display:flex;align-items:center}.counted-action.active .count-indicator,.counted-action.active button{color:#00a5ff}.count-indicator{font-size:9pt;padding:0 0 0 3px;color:#666}:host-context(.mat-dark-theme) .count-indicator{border-color:#333}:host-context(.mat-dark-theme):hover{background:#060606}.user-tag,:host-context(.mat-dark-theme) .user .display-name,:host-context(.mat-dark-theme) .user .username{color:#fff}.user-tag{text-transform:uppercase;font-size:12px;border:1px solid #b27373;background:#7a412b;padding:3px 5px;margin:0 .5em 0 1em;border-radius:3px}.spacer{flex-shrink:1;flex-grow:1}ul.message-facts{margin:0;padding:0;color:#666}ul.message-facts li{list-style-type:none;border-left:1px solid #666;font-size:10pt;padding-left:.5em;margin-left:.5em}ul.message-facts.small{display:none}ul.message-facts.small li{margin-top:.5em}ul.message-facts.small li:first-child{border-left:1px solid transparent;margin-left:0;padding-left:0}@media (max-width:400px){.avatar{height:32px;width:32px}.actions ul.message-facts{display:none}ul.message-facts.small{display:initial}:host .actions{margin-left:0;margin-top:.5em}:host .message-content .content{margin-left:44px;margin-right:.5em}}:host-context(.banta-mobile) .avatar{height:32px;width:32px}:host-context(.banta-mobile) .actions ul.message-facts{display:none}:host-context(.banta-mobile) ul.message-facts.small{display:initial}:host-context(.banta-mobile) :host .actions{margin-left:0;margin-top:.5em}:host-context(.banta-mobile) :host .message-content .content{margin-left:44px;margin-right:.5em}"]
8654
+ template: "\r\n<mat-menu #pointItemMenu=\"matMenu\">\r\n <button mat-menu-item (click)=\"share()\">\r\n <mat-icon>share</mat-icon>\r\n Share\r\n </button>\r\n <button *ngIf=\"!mine\" mat-menu-item (click)=\"report()\">\r\n <mat-icon>warning</mat-icon>\r\n Report\r\n </button>\r\n <button *ngIf=\"mine\" [disabled]=\"!permissions?.canEdit\" mat-menu-item (click)=\"startEdit()\">\r\n <mat-icon>edit</mat-icon>\r\n Edit\r\n </button>\r\n <button *ngIf=\"mine\" [disabled]=\"!permissions?.canDelete\" mat-menu-item (click)=\"delete()\">\r\n <mat-icon>delete</mat-icon>\r\n Delete\r\n </button>\r\n</mat-menu>\r\n\r\n<div class=\"message-content\">\r\n <div class=\"user\">\r\n <div class=\"user-1\">\r\n <a\r\n href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"selectAvatar(message.user)\"\r\n [style.background-image]=\"avatarForUser(message.user)\"></a>\r\n <div class=\"user-identity\">\r\n <a href=\"javascript:;\" class=\"display-name\" (click)=\"selectUser()\">{{message.user.displayName}}</a>\r\n <a href=\"javascript:;\" class=\"username\" (click)=\"selectUsername(message.user)\">@{{message.user.username}}</a>\r\n </div>\r\n </div>\r\n <div class=\"user-2\">\r\n <span class=\"user-tag\" *ngIf=\"message.user.tag\">{{message.user.tag}}</span>\r\n <banta-timestamp [value]=\"message.sentAt\"></banta-timestamp>\r\n <span class=\"spacer\"></span>\r\n </div>\r\n </div>\r\n <div class=\"content\" *ngIf=\"!editing\">\r\n <span class=\"banta-message-content\" [innerHTML]=\"message.message | markdownToHtml\"></span>\r\n <banta-attachments [attachments]=\"message.attachments\"></banta-attachments>\r\n <ul class=\"message-facts\">\r\n <li *ngIf=\"message.edits?.length > 0\">(Edited)</li>\r\n </ul>\r\n </div>\r\n <div class=\"content\" *ngIf=\"editing\" style=\"padding-bottom: 2em;\">\r\n <div>\r\n <mat-form-field floatLabel=\"always\" appearance=\"outline\" style=\"width: 100%;\">\r\n <mat-label>Edit Message</mat-label>\r\n <textarea matInput [(ngModel)]=\"editedMessage\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n <button mat-raised-button (click)=\"saveEdit()\">Save</button> &nbsp;\r\n <button mat-button (click)=\"endEditing()\">Cancel</button>\r\n </div>\r\n\r\n\r\n <div class=\"actions\">\r\n <div class=\"spacer\"></div>\r\n <div class=\"counted-action\" *ngIf=\"showReplyAction\">\r\n <button mat-button [matTooltip]=\"replyCount > 0 ? 'Replies' : 'Reply'\" matTooltipPosition=\"below\" (click)=\"select()\">\r\n <mat-icon [inline]=\"true\">comment</mat-icon>\r\n <span class=\"count-indicator\">\r\n {{replyCount > 0 ? 'Replies' : 'Reply'}}\r\n {{replyCount > 0 ? '(' + replyCount + ')' : ''}}\r\n </span>\r\n </button>\r\n </div>\r\n <div class=\"counted-action\" [class.active]=\"message.userState?.liked\">\r\n <button \r\n *ngIf=\"message.transientState?.liking\"\r\n mat-icon-button \r\n [disabled]=\"true\" \r\n [matTooltip]=\"upvoting ? 'Please wait...' : message.userState?.liked ? 'Unlike' : 'Like'\" \r\n matTooltipPosition=\"below\" \r\n >\r\n <mat-spinner [diameter]=\"15\" style=\"margin-left: 1em;\"></mat-spinner>\r\n </button>\r\n <button \r\n *ngIf=\"!message.transientState?.liking\"\r\n mat-button \r\n [disabled]=\"!permissions?.canLike\" \r\n [matTooltip]=\"upvoting ? 'Please wait...' : 'Like'\" \r\n matTooltipPosition=\"below\" \r\n (click)=\"message.userState?.liked ? unlike() : like()\" \r\n >\r\n <mat-icon [inline]=\"true\">thumb_up</mat-icon>\r\n <span class=\"count-indicator\" *ngIf=\"message.likes > 0\">\r\n {{message.likes}}\r\n </span>\r\n </button>\r\n </div>\r\n\r\n <button mat-icon-button [matMenuTriggerFor]=\"pointItemMenu\">\r\n <mat-icon [inline]=\"true\">more_vert</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n",
8655
+ styles: ["@-webkit-keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}@keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}:host{display:flex;flex-direction:column;position:relative;padding:.5em;visibility:hidden}:host.new{visibility:visible;-webkit-animation-name:comment-appear;animation-name:comment-appear;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}:host.highlighted{background:#00223a;outline:2px solid #003277}:host.visible{visibility:visible}:host:hover{background:#eee}:host .message-content .content{margin-left:60px;margin-right:.5em}:host .message-content .attachments-row{margin-top:15px;display:flex;gap:10px}:host .message-content .attachments-row img{border-radius:10px;width:300px;max-width:100%;max-height:20em;-o-object-fit:cover;object-fit:cover}:host.abbreviated .message-content .content{text-overflow:ellipsis;overflow-y:hidden}:host .actions{display:flex;padding-right:10px;margin-left:60px;align-items:center}:host .actions button,banta-timestamp{color:#666;flex-shrink:0}banta-timestamp{font-size:10pt;margin-left:1em;text-align:right}.user{position:relative;margin:1em 0 0;display:flex;align-items:center;flex-wrap:wrap}.user .user-1,.user .user-2{display:flex;flex-wrap:nowrap;align-items:center;min-width:0}.user .user-2{margin:1em 0}.user .user-identity{display:flex;flex-direction:column;min-width:0}.user .display-name,.user .username{z-index:1;position:relative;padding:0 0 0 1em;font-size:10pt;color:#000;margin:0 auto 0 0;display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex-shrink:1;flex-grow:0;min-width:0}.user .display-name.username.username.username,.user .username.username.username.username{color:#666}.avatar{height:48px;width:48px;background-position:50%;background-size:cover;background-color:#333;border-radius:100%;flex-shrink:0;flex-grow:0}.counted-action{display:flex;align-items:center}.counted-action.active .count-indicator,.counted-action.active button{color:#00a5ff}.counted-action button .count-indicator{margin-left:.5em}.count-indicator{font-size:9pt;padding:0 0 0 3px;color:#666}:host-context(.mat-dark-theme) .count-indicator{border-color:#333}:host-context(.mat-dark-theme):hover{background:#060606}.user-tag,:host-context(.mat-dark-theme) .user .display-name,:host-context(.mat-dark-theme) .user .username{color:#fff}.user-tag{text-transform:uppercase;font-size:12px;border:1px solid #b27373;background:#7a412b;padding:3px 5px;margin:0 .5em 0 1em;border-radius:3px}.spacer{flex-shrink:1;flex-grow:1}ul.message-facts{margin:0;padding:0;color:#666}ul.message-facts li{list-style-type:none;border-left:1px solid #666;font-size:10pt;padding-left:.5em;margin-left:.5em;margin-top:.5em}ul.message-facts li:first-child{border-left:1px solid transparent;margin-left:0;padding-left:0}@media (max-width:400px){.avatar{height:32px;width:32px}:host .actions{margin-left:0;margin-top:.5em}:host .message-content .content{margin-left:44px;margin-right:.5em}}:host-context(.banta-mobile) .avatar{height:32px;width:32px}:host-context(.banta-mobile) :host .actions{margin-left:0;margin-top:.5em}:host-context(.banta-mobile) :host .message-content .content{margin-left:44px;margin-right:.5em}.card-attachment a{display:flex;align-items:flex-start;gap:1em;width:100%;border:1px solid #666;border-radius:4px;padding:1em;box-sizing:border-box;background-color:#191919}.card-attachment a img{width:300px;aspect-ratio:16/9;-o-object-fit:cover;object-fit:cover;border-radius:10px}.card-attachment a h1{margin:0;font-size:30px}"]
8407
8656
  },] }
8408
8657
  ];
8409
8658
  CommentComponent.propDecorators = {
@@ -8428,8 +8677,7 @@
8428
8677
  editEnded: [{ type: core.Output }],
8429
8678
  shared: [{ type: core.Output }],
8430
8679
  genericAvatarUrl: [{ type: core.Input }],
8431
- commentId: [{ type: core.HostBinding, args: ['attr.data-comment-id',] }],
8432
- lightbox: [{ type: core.ViewChild, args: ['lightbox',] }]
8680
+ commentId: [{ type: core.HostBinding, args: ['attr.data-comment-id',] }]
8433
8681
  };
8434
8682
 
8435
8683
  var CommentViewComponent = /** @class */ (function () {
@@ -9487,7 +9735,7 @@
9487
9735
  { type: core.Component, args: [{
9488
9736
  selector: 'banta-comments',
9489
9737
  template: "<ng-container *ngIf=\"loading\">\r\n <div class=\"loading-screen\" [class.visible]=\"showLoadingScreen\">\r\n <h1>Loading Comments</h1>\r\n <div>\r\n <mat-spinner [diameter]=\"300\" [strokeWidth]=\"2\"></mat-spinner>\r\n </div>\r\n\r\n <p class=\"loading-message\" [class.visible]=\"loadingMessageVisible\">{{loadingMessage}}</p>\r\n </div>\r\n</ng-container>\r\n<ng-container *ngIf=\"!loading\">\r\n <div class=\"focused\" [class.visible]=\"selectedMessageVisible\" *ngIf=\"selectedMessage && !useInlineReplies\">\r\n\r\n <div>\r\n <a mat-button href=\"javascript:;\" (click)=\"unselectMessage()\">\r\n <mat-icon>arrow_back</mat-icon>\r\n Latest Comments\r\n </a>\r\n </div>\r\n\r\n <banta-comment\r\n [message]=\"selectedMessage\"\r\n [liking]=\"selectedMessage.transientState.liking\"\r\n [mine]=\"user?.id === selectedMessage.user?.id\"\r\n [permissions]=\"source?.permissions\"\r\n [showReplyAction]=\"false\"\r\n [editing]=\"selectedMessage.transientState.editing\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n (editStarted)=\"startEditing(selectedMessage)\"\r\n (editEnded)=\"selectedMessage.transientState.editing = false\"\r\n (edited)=\"saveEdit(selectedMessage, $event)\"\r\n (userSelected)=\"selectMessageUser(selectedMessage)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (liked)=\"likeMessage(source, selectedMessage)\"\r\n (unliked)=\"unlikeMessage(source, selectedMessage)\"\r\n (reported)=\"reportMessage(selectedMessage)\"\r\n (selected)=\"selectMessage(selectedMessage)\"\r\n (shared)=\"shareMessage($event)\"\r\n (deleted)=\"deleteMessage(selectedMessage)\"\r\n ></banta-comment>\r\n\r\n <div class=\"replies\">\r\n\r\n <ng-container *ngIf=\"!selectedMessageThread\">\r\n <div class=\"loading\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectedMessageThread\">\r\n <banta-comment-view\r\n [source]=\"selectedMessageThread\"\r\n [allowReplies]=\"false\"\r\n [fixedHeight]=\"false\"\r\n [showEmptyState]=\"false\"\r\n [newestLast]=\"true\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n (liked)=\"likeMessage(selectedMessageThread, $event)\"\r\n (unliked)=\"unlikeMessage(selectedMessageThread, $event)\"\r\n (messageEdited)=\"editMessage(selectedMessageThread, $event.message, $event.newMessage)\"\r\n (reported)=\"reportMessage($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (shared)=\"shareMessage($event)\"\r\n (deleted)=\"deleteMessage($event)\"\r\n ></banta-comment-view>\r\n\r\n <banta-comment-field\r\n [sendLabel]=\"replyLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n (signInSelected)=\"showSignIn()\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n [source]=\"selectedMessageThread\"\r\n [maxLength]=\"maxCommentLength\"\r\n [canComment]=\"source?.permissions?.canPost\"\r\n [signInLabel]=\"signInLabel\"\r\n [permissionDeniedLabel]=\"source?.permissions?.canPostErrorMessage || permissionDeniedLabel\"\r\n (permissionDeniedError)=\"handlePermissionDenied($event)\"\r\n [shouldInterceptMessageSend]=\"shouldInterceptMessageSend\"\r\n [user]=\"user\"\r\n [label]=\"postReplyLabel\"\r\n [submit]=\"sendReply\"\r\n [allowAttachments]=\"allowAttachments\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"sendReplyOptionsTemplate\"></ng-container>\r\n </banta-comment-field>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"main\" [class.hidden]=\"selectedMessage && !useInlineReplies\">\r\n <banta-comment-field\r\n [source]=\"source\"\r\n [user]=\"user\"\r\n [sendLabel]=\"sendLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [signInLabel]=\"signInLabel\"\r\n [canComment]=\"source?.permissions?.canPost\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n [label]=\"postCommentLabel\"\r\n [maxLength]=\"maxCommentLength\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n (signInSelected)=\"showSignIn()\"\r\n [permissionDeniedLabel]=\"source?.permissions?.canPostErrorMessage || permissionDeniedLabel\"\r\n (permissionDeniedError)=\"handlePermissionDenied($event)\"\r\n [shouldInterceptMessageSend]=\"shouldInterceptMessageSend\"\r\n [submit]=\"sendMessage\"\r\n [allowAttachments]=\"allowAttachments\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n >\r\n \r\n </banta-comment-field>\r\n\r\n <banta-comment-sort\r\n [(sort)]=\"sortOrder\"></banta-comment-sort>\r\n\r\n <banta-comment-view\r\n [class.faded]=\"selectedMessage && !useInlineReplies\"\r\n [source]=\"source\"\r\n [fixedHeight]=\"fixedHeight\"\r\n [maxMessages]=\"maxMessages\"\r\n [maxVisibleMessages]=\"maxVisibleMessages\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n (userSelected)=\"selectMessageUser($event)\"\r\n (sortOrderChanged)=\"sortOrder = $event\"\r\n (selected)=\"selectMessage($event)\"\r\n (liked)=\"likeMessage(source, $event)\"\r\n (unliked)=\"unlikeMessage(source, $event)\"\r\n (messageEdited)=\"editMessage(source, $event.message, $event.newMessage)\"\r\n (reported)=\"reportMessage($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (shared)=\"shareMessage($event)\"\r\n [selectedMessage]=\"selectedMessage\"\r\n (deleted)=\"deleteMessage($event)\"\r\n >\r\n <div class=\"inline-replies\">\r\n <div class=\"focused\" [class.visible]=\"selectedMessageVisible\" *ngIf=\"selectedMessage\">\r\n <div class=\"replies\">\r\n \r\n <ng-container *ngIf=\"!selectedMessageThread\">\r\n <div class=\"loading\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n </ng-container>\r\n \r\n <ng-container *ngIf=\"selectedMessageThread\">\r\n <banta-comment-view\r\n [source]=\"selectedMessageThread\"\r\n [allowReplies]=\"false\"\r\n [fixedHeight]=\"false\"\r\n [showEmptyState]=\"false\"\r\n [newestLast]=\"true\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n (liked)=\"likeMessage(selectedMessageThread, $event)\"\r\n (unliked)=\"unlikeMessage(selectedMessageThread, $event)\"\r\n (messageEdited)=\"editMessage(selectedMessageThread, $event.message, $event.newMessage)\"\r\n (reported)=\"reportMessage($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (shared)=\"shareMessage($event)\"\r\n (deleted)=\"deleteMessage($event)\"\r\n ></banta-comment-view>\r\n \r\n <banta-comment-field\r\n [sendLabel]=\"replyLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n (signInSelected)=\"showSignIn()\"\r\n [maxLength]=\"maxCommentLength\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n [source]=\"selectedMessageThread\"\r\n [canComment]=\"source?.permissions?.canPost\"\r\n [signInLabel]=\"signInLabel\"\r\n [permissionDeniedLabel]=\"source?.permissions?.canPostErrorMessage || permissionDeniedLabel\"\r\n (permissionDeniedError)=\"handlePermissionDenied($event)\"\r\n [shouldInterceptMessageSend]=\"shouldInterceptMessageSend\"\r\n [user]=\"user\"\r\n [label]=\"postReplyLabel\"\r\n [submit]=\"sendReply\"\r\n [allowAttachments]=\"allowAttachments\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"sendReplyOptionsTemplate\"></ng-container>\r\n </banta-comment-field>\r\n </ng-container>\r\n </div>\r\n </div> \r\n </div>\r\n </banta-comment-view>\r\n </div>\r\n</ng-container>\r\n",
9490
- styles: [":host{display:flex;flex-direction:column}@-webkit-keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}@keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}.focused{-webkit-animation-name:select-comment;animation-name:select-comment;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.focused .replies{margin-top:1em;margin-left:2em;border-left:2px solid #333;padding-left:2em}banta-comment-view{opacity:1;transition:opacity .4s ease-in-out}banta-comment-view.faded{opacity:.25}.loading{display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:0 auto;min-height:16em}.main.hidden{display:none}.loading-screen{text-align:center;opacity:0;transition:opacity .25s ease-in-out}.loading-screen.visible{opacity:1}.loading-screen h1{font-weight:100}.loading-screen mat-spinner{margin:5em auto}.loading-screen .loading-message{opacity:0;transition:opacity .25s ease-in-out;width:500px;max-width:100%;margin:0 auto}.loading-screen .loading-message.visible{opacity:1}banta-comment-sort{margin:0 0 0 auto;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;display:block}.inline-replies{margin-left:4em}@media (max-width:500px){.focused .replies{margin-left:0}banta-comment-sort{margin:0;width:100%}}:host-context(.banta-mobile) .focused .replies{margin-left:0}:host-context(.banta-mobile) banta-comment-sort{margin:0;width:100%}"]
9738
+ styles: [":host{display:flex;flex-direction:column}@-webkit-keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}@keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}.focused{-webkit-animation-name:select-comment;animation-name:select-comment;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.focused .replies{margin-top:1em;margin-left:2em;border-left:2px solid #333;padding-left:2em}banta-comment-view{opacity:1;transition:opacity .4s ease-in-out}banta-comment-view.faded{opacity:.25}.loading{display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:0 auto;min-height:16em}.main.hidden{display:none}.loading-screen{text-align:center;opacity:0;transition:opacity .25s ease-in-out}.loading-screen.visible{opacity:1}.loading-screen h1{font-weight:100}.loading-screen mat-spinner{margin:5em auto}.loading-screen .loading-message{opacity:0;transition:opacity .25s ease-in-out;width:500px;max-width:100%;margin:0 auto}.loading-screen .loading-message.visible{opacity:1}banta-comment-sort{margin:0 0 0 auto;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;display:block}.inline-replies{margin-left:4em}@media (max-width:500px){.focused .replies{margin-left:0}.inline-replies{margin-left:1em}.focused .replies{padding-left:.5em}banta-comment-sort{margin:0;width:100%}}:host-context(.banta-mobile) .focused .replies{margin-left:0}:host-context(.banta-mobile) .inline-replies{margin-left:1em}:host-context(.banta-mobile) .focused .replies{padding-left:.5em}:host-context(.banta-mobile) banta-comment-sort{margin:0;width:100%}"]
9491
9739
  },] }
9492
9740
  ];
9493
9741
  BantaCommentsComponent.ctorParameters = function () { return [
@@ -9611,14 +9859,17 @@
9611
9859
  };
9612
9860
 
9613
9861
  var CommentFieldComponent = /** @class */ (function () {
9614
- function CommentFieldComponent() {
9862
+ function CommentFieldComponent(chatBackend) {
9863
+ this.chatBackend = chatBackend;
9615
9864
  this.canComment = true;
9616
9865
  this.allowAttachments = false;
9617
9866
  this.signInSelected = new rxjs.Subject();
9618
9867
  this.editAvatarSelected = new rxjs.Subject();
9619
9868
  this.sending = false;
9620
9869
  this.expandError = false;
9621
- this.text = '';
9870
+ this._text = '';
9871
+ this.attachmentScrapeDebounce = 1500;
9872
+ this.attachmentFragments = new Map();
9622
9873
  this.sendLabel = 'Send';
9623
9874
  this.sendingLabel = 'Sending';
9624
9875
  this.label = 'Post a comment';
@@ -9634,6 +9885,171 @@
9634
9885
  this.autoCompleteSelected = 0;
9635
9886
  this.chatMessageAttachments = [];
9636
9887
  }
9888
+ Object.defineProperty(CommentFieldComponent.prototype, "text", {
9889
+ get: function () {
9890
+ return this._text;
9891
+ },
9892
+ set: function (value) {
9893
+ var _this = this;
9894
+ this._text = value;
9895
+ clearTimeout(this.attachmentScrapeTimeout);
9896
+ this.attachmentScrapeTimeout = setTimeout(function () { return _this.scrapeAttachments(); }, this.attachmentScrapeDebounce);
9897
+ },
9898
+ enumerable: false,
9899
+ configurable: true
9900
+ });
9901
+ CommentFieldComponent.prototype.scrapeAttachments = function () {
9902
+ var e_1, _b, e_2, _c, e_3, _d, e_4, _e, e_5, _f;
9903
+ var _this = this;
9904
+ var message = {
9905
+ likes: 0,
9906
+ message: this._text,
9907
+ sentAt: undefined,
9908
+ user: this.user,
9909
+ attachments: this.chatMessageAttachments
9910
+ };
9911
+ var foundFragments = [];
9912
+ try {
9913
+ for (var _g = __values(this.chatBackend.attachmentScrapers), _h = _g.next(); !_h.done; _h = _g.next()) {
9914
+ var scraper = _h.value;
9915
+ var fragments = scraper.findFragments(message);
9916
+ if (!fragments) {
9917
+ console.error("Attachment fragment scraper " + scraper.constructor.name + " is implemented incorrectly: Returned null instead of array");
9918
+ continue;
9919
+ }
9920
+ try {
9921
+ for (var fragments_1 = (e_2 = void 0, __values(fragments)), fragments_1_1 = fragments_1.next(); !fragments_1_1.done; fragments_1_1 = fragments_1.next()) {
9922
+ var fragment = fragments_1_1.value;
9923
+ foundFragments.push(fragment.text);
9924
+ if (!this.attachmentFragments.has(fragment.text)) {
9925
+ console.log("Scraped new fragment:");
9926
+ console.dir(fragment);
9927
+ this.attachmentFragments.set(fragment.text, {
9928
+ fragment: fragment,
9929
+ resolution: undefined
9930
+ });
9931
+ }
9932
+ }
9933
+ }
9934
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
9935
+ finally {
9936
+ try {
9937
+ if (fragments_1_1 && !fragments_1_1.done && (_c = fragments_1.return)) _c.call(fragments_1);
9938
+ }
9939
+ finally { if (e_2) throw e_2.error; }
9940
+ }
9941
+ }
9942
+ }
9943
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
9944
+ finally {
9945
+ try {
9946
+ if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
9947
+ }
9948
+ finally { if (e_1) throw e_1.error; }
9949
+ }
9950
+ // Remove fragments that are no longer in the message.
9951
+ var removedFragments = [];
9952
+ try {
9953
+ for (var _j = __values(this.attachmentFragments), _k = _j.next(); !_k.done; _k = _j.next()) {
9954
+ var _l = __read(_k.value, 1), key = _l[0];
9955
+ if (!foundFragments.includes(key))
9956
+ removedFragments.push(key);
9957
+ }
9958
+ }
9959
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
9960
+ finally {
9961
+ try {
9962
+ if (_k && !_k.done && (_d = _j.return)) _d.call(_j);
9963
+ }
9964
+ finally { if (e_3) throw e_3.error; }
9965
+ }
9966
+ try {
9967
+ for (var removedFragments_1 = __values(removedFragments), removedFragments_1_1 = removedFragments_1.next(); !removedFragments_1_1.done; removedFragments_1_1 = removedFragments_1.next()) {
9968
+ var removedFragment = removedFragments_1_1.value;
9969
+ console.log("Removed fragment: " + removedFragment);
9970
+ this.attachmentFragments.delete(removedFragment);
9971
+ }
9972
+ }
9973
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
9974
+ finally {
9975
+ try {
9976
+ if (removedFragments_1_1 && !removedFragments_1_1.done && (_e = removedFragments_1.return)) _e.call(removedFragments_1);
9977
+ }
9978
+ finally { if (e_4) throw e_4.error; }
9979
+ }
9980
+ var _loop_1 = function (key, state) {
9981
+ if (state.resolution)
9982
+ return "continue";
9983
+ state.resolution = new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
9984
+ var _b, _c, resolver, attachment, e_6, e_7_1;
9985
+ var e_7, _d;
9986
+ return __generator(this, function (_e) {
9987
+ switch (_e.label) {
9988
+ case 0:
9989
+ console.log("Resolving fragment " + key);
9990
+ _e.label = 1;
9991
+ case 1:
9992
+ _e.trys.push([1, 8, 9, 10]);
9993
+ _b = __values(this.chatBackend.attachmentResolvers), _c = _b.next();
9994
+ _e.label = 2;
9995
+ case 2:
9996
+ if (!!_c.done) return [3 /*break*/, 7];
9997
+ resolver = _c.value;
9998
+ console.log("- Trying resolver " + resolver.constructor.name + "...");
9999
+ _e.label = 3;
10000
+ case 3:
10001
+ _e.trys.push([3, 5, , 6]);
10002
+ return [4 /*yield*/, resolver.resolveFragment(message, state.fragment)];
10003
+ case 4:
10004
+ attachment = _e.sent();
10005
+ if (attachment) {
10006
+ console.log("Resolved fragment " + key + " into attachment:");
10007
+ console.dir(attachment);
10008
+ this.chatMessageAttachments.push(attachment);
10009
+ resolve(attachment);
10010
+ return [3 /*break*/, 7];
10011
+ }
10012
+ return [3 /*break*/, 6];
10013
+ case 5:
10014
+ e_6 = _e.sent();
10015
+ console.error("Caught error during attachment resolver " + resolver.constructor.name + ":");
10016
+ console.error(e_6);
10017
+ return [3 /*break*/, 6];
10018
+ case 6:
10019
+ _c = _b.next();
10020
+ return [3 /*break*/, 2];
10021
+ case 7: return [3 /*break*/, 10];
10022
+ case 8:
10023
+ e_7_1 = _e.sent();
10024
+ e_7 = { error: e_7_1 };
10025
+ return [3 /*break*/, 10];
10026
+ case 9:
10027
+ try {
10028
+ if (_c && !_c.done && (_d = _b.return)) _d.call(_b);
10029
+ }
10030
+ finally { if (e_7) throw e_7.error; }
10031
+ return [7 /*endfinally*/];
10032
+ case 10: return [2 /*return*/];
10033
+ }
10034
+ });
10035
+ }); });
10036
+ };
10037
+ try {
10038
+ // Process any fragments that are not yet resolved (or being
10039
+ // resolved)
10040
+ for (var _m = __values(this.attachmentFragments), _o = _m.next(); !_o.done; _o = _m.next()) {
10041
+ var _p = __read(_o.value, 2), key = _p[0], state = _p[1];
10042
+ _loop_1(key, state);
10043
+ }
10044
+ }
10045
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
10046
+ finally {
10047
+ try {
10048
+ if (_o && !_o.done && (_f = _m.return)) _f.call(_m);
10049
+ }
10050
+ finally { if (e_5) throw e_5.error; }
10051
+ }
10052
+ };
9637
10053
  Object.defineProperty(CommentFieldComponent.prototype, "userAvatarUrl", {
9638
10054
  get: function () {
9639
10055
  var _a;
@@ -9861,7 +10277,7 @@
9861
10277
  };
9862
10278
  CommentFieldComponent.prototype.sendMessage = function () {
9863
10279
  return __awaiter(this, void 0, void 0, function () {
9864
- var text, message, e_1;
10280
+ var text, message, e_8;
9865
10281
  return __generator(this, function (_b) {
9866
10282
  switch (_b.label) {
9867
10283
  case 0:
@@ -9893,11 +10309,11 @@
9893
10309
  this.chatMessageAttachments = [];
9894
10310
  return [3 /*break*/, 6];
9895
10311
  case 4:
9896
- e_1 = _b.sent();
10312
+ e_8 = _b.sent();
9897
10313
  return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 1000); })];
9898
10314
  case 5:
9899
10315
  _b.sent();
9900
- this.indicateError(e_1.message);
10316
+ this.indicateError(e_8.message);
9901
10317
  return [3 /*break*/, 6];
9902
10318
  case 6: return [3 /*break*/, 8];
9903
10319
  case 7:
@@ -9917,8 +10333,10 @@
9917
10333
  _this.chatMessageAttachments = _this.chatMessageAttachments.filter(function (x) { return x !== attachment; });
9918
10334
  }, 3000);
9919
10335
  };
9920
- CommentFieldComponent.prototype.removeAttachment = function (index) {
9921
- this.chatMessageAttachments.splice(index, 1);
10336
+ CommentFieldComponent.prototype.removeAttachment = function (attachment) {
10337
+ var index = this.chatMessageAttachments.indexOf(attachment);
10338
+ if (index >= 0)
10339
+ this.chatMessageAttachments.splice(index, 1);
9922
10340
  };
9923
10341
  CommentFieldComponent.prototype.alertError = function () {
9924
10342
  if (!this.sendError)
@@ -9930,10 +10348,13 @@
9930
10348
  CommentFieldComponent.decorators = [
9931
10349
  { type: core.Component, args: [{
9932
10350
  selector: 'banta-comment-field',
9933
- template: "<form class=\"new-message\" (submit)=\"sendMessage()\">\r\n <div class=\"avatar-container\">\r\n <a href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"showEditAvatar()\"\r\n [style.background-image]=\"'url(' + userAvatarUrl + ')'\"\r\n ></a>\r\n </div>\r\n <div class=\"text-container\">\r\n <div class=\"field-container\">\r\n <div class=\"field-row\">\r\n <mat-form-field appearance=\"outline\" floatLabel=\"always\">\r\n <mat-label>{{label}}</mat-label>\r\n <textarea\r\n #textarea\r\n name=\"message\"\r\n [placeholder]=\"placeholder\"\r\n matInput\r\n cdkTextareaAutosize\r\n [maxlength]=\"maxLength\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (blur)=\"onBlur()\"\r\n [disabled]=\"sending\"\r\n [(ngModel)]=\"text\"></textarea>\r\n </mat-form-field>\r\n <div class=\"options-line\">\r\n <mat-spinner *ngIf=\"sending\" class=\"icon loading\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n <div *ngIf=\"sendError\" class=\"error-message\" [class.expanded]=\"expandError\" [matTooltip]=\"sendError.message\" (click)=\"alertError()\">\r\n <mat-icon *ngIf=\"sendError\">error</mat-icon>\r\n {{sendError.message}}\r\n </div>\r\n <div class=\"spacer\"></div>\r\n <div class=\"custom\">\r\n <ng-content></ng-content>\r\n </div>\r\n <banta-attachment-button \r\n *ngIf=\"allowAttachments\"\r\n (addedAttachment)=\"addedAttachment($event)\"\r\n (attachmentError)=\"attachmentError($event)\"\r\n ></banta-attachment-button>\r\n <emoji-selector-button (selected)=\"insertEmoji($event)\"></emoji-selector-button>\r\n </div>\r\n \r\n </div>\r\n <div #autocompleteContainer class=\"autocomplete-container\">\r\n <div #autocomplete class=\"autocomplete\" [class.visible]=\"autocompleteVisible\">\r\n\r\n <div>\r\n <strong>{{completionPrefix}}</strong>...\r\n </div>\r\n <a\r\n mat-button\r\n *ngFor=\"let option of autocompleteOptions; index as index\"\r\n (click)=\"activateAutoComplete(option)\"\r\n [class.active]=\"autoCompleteSelected === index\"\r\n >\r\n {{option.label}}\r\n </a>\r\n </div>\r\n </div>\r\n\r\n\r\n <div *ngIf=\"chatMessageAttachments && chatMessageAttachments.length\" class=\"message-attachments-container\">\r\n <div *ngFor=\"let attachment of chatMessageAttachments; index as attachmentIndex\"\r\n class=\"message-attachment\" [class.with-border]=\"!attachment.url\">\r\n <button (click)=\"removeAttachment(attachmentIndex)\" mat-mini-fab color=\"primary\" class=\"remove-img\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n <ng-container *ngIf=\"attachment.transientState?.error\">\r\n <mat-icon class=\"error\">close</mat-icon>\r\n <em class=\"error\">{{attachment.transientState?.errorMessage || 'Error'}}</em>\r\n </ng-container>\r\n <ng-container *ngIf=\"!attachment.transientState?.error\">\r\n <ng-container *ngIf=\"attachment.transientState?.uploading\">\r\n <mat-spinner></mat-spinner>\r\n <em>Uploading...</em>\r\n </ng-container>\r\n <ng-container *ngIf=\"!attachment.transientState?.uploading\">\r\n <img [src]=\"attachment.url\" alt=\"Message Attachment\">\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n <div class=\"actions\">\r\n <ng-container *ngIf=\"!user\">\r\n <button\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n (click)=\"showSignIn()\"\r\n >{{signInLabel}}</button>\r\n </ng-container>\r\n <ng-container *ngIf=\"user\">\r\n <button\r\n mat-raised-button\r\n class=\"send\"\r\n color=\"primary\"\r\n [disabled]=\"!sendButtonEnabled\"\r\n >\r\n <ng-container *ngIf=\"canComment\">\r\n <mat-icon *ngIf=\"!sending\">chevron_right</mat-icon>\r\n <mat-spinner *ngIf=\"sending\" class=\"icon\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n </ng-container>\r\n <span class=\"label\">\r\n <ng-container *ngIf=\"!canComment\">\r\n {{permissionDeniedLabel}}\r\n </ng-container>\r\n <ng-container *ngIf=\"canComment\">\r\n <ng-container *ngIf=\"!sending\">\r\n {{sendLabel}}\r\n </ng-container>\r\n <ng-container *ngIf=\"sending\">\r\n {{sendingLabel}}\r\n </ng-container>\r\n </ng-container>\r\n </span>\r\n </button>\r\n </ng-container>\r\n </div>\r\n</form>",
9934
- styles: ["@-webkit-keyframes comment-field-appear{0%{transform:translateY(128px);opacity:0}to{transform:translate(0);opacity:1}}@keyframes comment-field-appear{0%{transform:translateY(128px);opacity:0}to{transform:translate(0);opacity:1}}:host{margin:0 2em 0 0;display:block;-webkit-animation-name:comment-field-appear;animation-name:comment-field-appear;-webkit-animation-duration:.8s;animation-duration:.8s;-webkit-animation-delay:.4s;animation-delay:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both;position:relative;z-index:20}.avatar-container{width:calc(48px + 1.75em);display:flex;justify-content:flex-end;flex-shrink:0}.avatar-container .avatar{width:48px;height:48px;background:#000;border-radius:100%;background-size:cover;background-repeat:no-repeat;background-position:50%;margin-top:.75em;margin-right:.75em}form{display:flex;padding:.5em;align-items:center}form .text-container{position:relative;display:flex;flex-grow:1;min-width:0}form .text-container textarea{font-size:14pt;width:100%}form .text-container textarea[disabled]{opacity:.5}form .text-container mat-spinner.loading{position:absolute;left:.5em;bottom:.5em}form .text-container .options-line{display:flex;align-items:center}form .text-container .options-line>*{flex-shrink:0}form .text-container .options-line .error-message{left:.5em;bottom:.5em;color:#683333;overflow-x:hidden;max-width:1.5em;white-space:nowrap;transition:max-width 2s ease-in-out;text-overflow:ellipsis;overflow:hidden;flex-shrink:1}form .text-container .options-line .error-message.expanded,form .text-container .options-line .error-message:hover{max-width:100%}form .text-container .options-line .error-message mat-icon{vertical-align:middle}form input[type=text]{background:#000;color:#fff;border:1px solid #333;width:100%;height:1em}form .actions{margin-left:1em;flex-shrink:0}form button{display:block;margin:0 0 0 auto}form.new-message{display:flex;align-items:flex-start;min-width:0}form.new-message .field-container{flex-grow:1;display:flex;flex-direction:column;min-width:0}form.new-message mat-form-field{width:100%}form.new-message mat-form-field ::ng-deep .mat-form-field-wrapper{padding-bottom:0}form.new-message button{margin:1.25em 0 0}button.send{min-width:9em}textarea{max-height:7em}.autocomplete-container{width:calc(100% - 2em);position:relative;pointer-events:none;top:-2em}.autocomplete{visibility:hidden;pointer-events:none;position:absolute;background:#333;padding:.5em;display:flex;flex-direction:column;z-index:100}.autocomplete.visible{visibility:visible;pointer-events:auto}.autocomplete a{width:100%;text-align:left}.autocomplete a.active{background:#555}@media (max-width:500px){:host{margin:0}.avatar-container{width:auto;flex-shrink:0}.avatar-container .avatar{width:32px;height:32px;margin-top:1.5em}button.send{min-width:auto;margin-top:1.5em}button.send .label{display:none}}:host-context(.banta-mobile) :host{margin:0}:host-context(.banta-mobile) .avatar-container{width:auto;flex-shrink:0}:host-context(.banta-mobile) .avatar-container .avatar{width:32px;height:32px;margin-top:1.5em}:host-context(.banta-mobile) button.send{min-width:auto;margin-top:1.5em}:host-context(.banta-mobile) button.send .label{display:none}.message-attachments-container{display:flex;gap:20px}.message-attachments-container .message-attachment{width:300px;position:relative;text-align:center}.message-attachments-container .message-attachment.with-border{outline:1px solid #333;padding:1em 0}.message-attachments-container .message-attachment mat-spinner{display:block;margin:0 auto .5em;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.message-attachments-container .message-attachment mat-icon.error{display:block;font-size:48px;width:48px;height:48px;margin:0 auto .5em}.message-attachments-container .message-attachment .error{color:#b76363}.message-attachments-container .message-attachment img{width:300px;border-radius:10px}.message-attachments-container .message-attachment .remove-img{position:absolute;right:10px;top:10px;margin:0}.field-row{position:relative}"]
10351
+ template: "<form class=\"new-message\" (submit)=\"sendMessage()\">\r\n <div class=\"avatar-container\">\r\n <a href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"showEditAvatar()\"\r\n [style.background-image]=\"'url(' + userAvatarUrl + ')'\"\r\n ></a>\r\n </div>\r\n <div class=\"text-container\">\r\n <div class=\"field-container\">\r\n <div class=\"field-row\">\r\n <mat-form-field appearance=\"outline\" floatLabel=\"always\">\r\n <mat-label>{{label}}</mat-label>\r\n <textarea\r\n #textarea\r\n name=\"message\"\r\n [placeholder]=\"placeholder\"\r\n matInput\r\n cdkTextareaAutosize\r\n [maxlength]=\"maxLength\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (blur)=\"onBlur()\"\r\n [disabled]=\"sending\"\r\n [(ngModel)]=\"text\"></textarea>\r\n </mat-form-field>\r\n <div class=\"options-line\">\r\n <mat-spinner *ngIf=\"sending\" class=\"icon loading\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n <div *ngIf=\"sendError\" class=\"error-message\" [class.expanded]=\"expandError\" [matTooltip]=\"sendError.message\" (click)=\"alertError()\">\r\n <mat-icon *ngIf=\"sendError\">error</mat-icon>\r\n {{sendError.message}}\r\n </div>\r\n <div class=\"spacer\"></div>\r\n <div class=\"custom\">\r\n <ng-content></ng-content>\r\n </div>\r\n <banta-attachment-button \r\n *ngIf=\"allowAttachments\"\r\n (addedAttachment)=\"addedAttachment($event)\"\r\n (attachmentError)=\"attachmentError($event)\"\r\n ></banta-attachment-button>\r\n <emoji-selector-button (selected)=\"insertEmoji($event)\"></emoji-selector-button>\r\n </div>\r\n \r\n </div>\r\n <div #autocompleteContainer class=\"autocomplete-container\">\r\n <div #autocomplete class=\"autocomplete\" [class.visible]=\"autocompleteVisible\">\r\n\r\n <div>\r\n <strong>{{completionPrefix}}</strong>...\r\n </div>\r\n <a\r\n mat-button\r\n *ngFor=\"let option of autocompleteOptions; index as index\"\r\n (click)=\"activateAutoComplete(option)\"\r\n [class.active]=\"autoCompleteSelected === index\"\r\n >\r\n {{option.label}}\r\n </a>\r\n </div>\r\n </div>\r\n\r\n <banta-attachments \r\n [attachments]=\"chatMessageAttachments\"\r\n [editing]=\"true\"\r\n (remove)=\"removeAttachment($event)\"\r\n ></banta-attachments>\r\n </div>\r\n </div>\r\n <div class=\"actions\">\r\n <ng-container *ngIf=\"!user\">\r\n <button\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n (click)=\"showSignIn()\"\r\n >{{signInLabel}}</button>\r\n </ng-container>\r\n <ng-container *ngIf=\"user\">\r\n <button\r\n mat-raised-button\r\n class=\"send\"\r\n color=\"primary\"\r\n [disabled]=\"!sendButtonEnabled\"\r\n >\r\n <ng-container *ngIf=\"canComment\">\r\n <mat-icon *ngIf=\"!sending\">chevron_right</mat-icon>\r\n <mat-spinner *ngIf=\"sending\" class=\"icon\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n </ng-container>\r\n <span class=\"label\">\r\n <ng-container *ngIf=\"!canComment\">\r\n {{permissionDeniedLabel}}\r\n </ng-container>\r\n <ng-container *ngIf=\"canComment\">\r\n <ng-container *ngIf=\"!sending\">\r\n {{sendLabel}}\r\n </ng-container>\r\n <ng-container *ngIf=\"sending\">\r\n {{sendingLabel}}\r\n </ng-container>\r\n </ng-container>\r\n </span>\r\n </button>\r\n </ng-container>\r\n </div>\r\n</form>",
10352
+ styles: ["@-webkit-keyframes comment-field-appear{0%{transform:translateY(128px);opacity:0}to{transform:translate(0);opacity:1}}@keyframes comment-field-appear{0%{transform:translateY(128px);opacity:0}to{transform:translate(0);opacity:1}}:host{margin:0 2em 0 0;display:block;-webkit-animation-name:comment-field-appear;animation-name:comment-field-appear;-webkit-animation-duration:.8s;animation-duration:.8s;-webkit-animation-delay:.4s;animation-delay:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both;position:relative;z-index:20}.avatar-container{width:calc(48px + 1.75em);display:flex;justify-content:flex-end;flex-shrink:0}.avatar-container .avatar{width:48px;height:48px;background:#000;border-radius:100%;background-size:cover;background-repeat:no-repeat;background-position:50%;margin-top:.75em;margin-right:.75em}form{display:flex;padding:.5em;align-items:center}form .text-container{position:relative;display:flex;flex-grow:1;min-width:0}form .text-container textarea{font-size:14pt;width:100%}form .text-container textarea[disabled]{opacity:.5}form .text-container mat-spinner.loading{position:absolute;left:.5em;bottom:.5em}form .text-container .options-line{display:flex;align-items:center}form .text-container .options-line>*{flex-shrink:0}form .text-container .options-line .error-message{left:.5em;bottom:.5em;color:#683333;overflow-x:hidden;max-width:1.5em;white-space:nowrap;transition:max-width 2s ease-in-out;text-overflow:ellipsis;overflow:hidden;flex-shrink:1}form .text-container .options-line .error-message.expanded,form .text-container .options-line .error-message:hover{max-width:100%}form .text-container .options-line .error-message mat-icon{vertical-align:middle}form input[type=text]{background:#000;color:#fff;border:1px solid #333;width:100%;height:1em}form .actions{margin-left:1em;flex-shrink:0}form button{display:block;margin:0 0 0 auto}form.new-message{display:flex;align-items:flex-start;min-width:0}form.new-message .field-container{flex-grow:1;display:flex;flex-direction:column;min-width:0}form.new-message mat-form-field{width:100%}form.new-message mat-form-field ::ng-deep .mat-form-field-wrapper{padding-bottom:0}form.new-message button{margin:1.25em 0 0}button.send{min-width:9em}textarea{max-height:7em}.autocomplete-container{width:calc(100% - 2em);position:relative;pointer-events:none;top:-2em}.autocomplete{visibility:hidden;pointer-events:none;position:absolute;background:#333;padding:.5em;display:flex;flex-direction:column;z-index:100}.autocomplete.visible{visibility:visible;pointer-events:auto}.autocomplete a{width:100%;text-align:left}.autocomplete a.active{background:#555}@media (max-width:500px){:host{margin:0}.avatar-container{width:auto;flex-shrink:0}.avatar-container .avatar{width:32px;height:32px;margin-top:1.5em}button.send{min-width:auto;margin-top:1.5em}button.send .label{display:none}}:host-context(.banta-mobile) :host{margin:0}:host-context(.banta-mobile) .avatar-container{width:auto;flex-shrink:0}:host-context(.banta-mobile) .avatar-container .avatar{width:32px;height:32px;margin-top:1.5em}:host-context(.banta-mobile) button.send{min-width:auto;margin-top:1.5em}:host-context(.banta-mobile) button.send .label{display:none}.image-attachments-container{display:flex;gap:20px}.image-attachments-container .image-attachment{width:300px;position:relative;text-align:center}.image-attachments-container .image-attachment.with-border{outline:1px solid #333;padding:1em 0}.image-attachments-container .image-attachment mat-spinner{display:block;margin:0 auto .5em;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.image-attachments-container .image-attachment mat-icon.error{display:block;font-size:48px;width:48px;height:48px;margin:0 auto .5em}.image-attachments-container .image-attachment .error{color:#b76363}.image-attachments-container .image-attachment img{width:300px;border-radius:10px}.image-attachments-container .image-attachment .remove-img{position:absolute;right:10px;top:10px;margin:0}.card-attachment,.field-row{position:relative}.card-attachment a{display:flex;align-items:flex-start;gap:1em;width:100%;border:1px solid #666;border-radius:4px;padding:2em;box-sizing:border-box;background-color:#191919}.card-attachment a img{width:300px;aspect-ratio:16/9;-o-object-fit:cover;object-fit:cover;border-radius:10px}.card-attachment a h1{margin:0;font-size:30px}.card-attachment .remove-img{position:absolute;right:10px;top:10px;margin:0}"]
9935
10353
  },] }
9936
10354
  ];
10355
+ CommentFieldComponent.ctorParameters = function () { return [
10356
+ { type: ChatBackendBase }
10357
+ ]; };
9937
10358
  CommentFieldComponent.propDecorators = {
9938
10359
  source: [{ type: core.Input }],
9939
10360
  user: [{ type: core.Input }],
@@ -10119,7 +10540,9 @@
10119
10540
  BantaCommonModule,
10120
10541
  EmojiModule,
10121
10542
  tooltip.MatTooltipModule,
10122
- select.MatSelectModule
10543
+ select.MatSelectModule,
10544
+ overlay.OverlayModule,
10545
+ portal.PortalModule
10123
10546
  ],
10124
10547
  exports: COMPONENTS$3
10125
10548
  },] }
@@ -10490,6 +10913,35 @@
10490
10913
  ChatBackend.prototype.watchMessage = function (message, handler) {
10491
10914
  throw new Error("Method not implemented.");
10492
10915
  };
10916
+ ChatBackend.prototype.getCardForUrl = function (url) {
10917
+ return __awaiter(this, void 0, void 0, function () {
10918
+ var response, _c, _d;
10919
+ return __generator(this, function (_e) {
10920
+ switch (_e.label) {
10921
+ case 0: return [4 /*yield*/, fetch(this.serviceUrl + "/urls", {
10922
+ method: 'POST',
10923
+ headers: {
10924
+ 'Content-Type': 'application/json'
10925
+ },
10926
+ body: JSON.stringify({
10927
+ url: url
10928
+ })
10929
+ })];
10930
+ case 1:
10931
+ response = _e.sent();
10932
+ if (response.status == 404)
10933
+ return [2 /*return*/, null];
10934
+ if (!(response.status >= 400)) return [3 /*break*/, 3];
10935
+ _c = Error.bind;
10936
+ _d = "Failed to retrieve URL card: " + response.status + ". Body: '";
10937
+ return [4 /*yield*/, response.text()];
10938
+ case 2: throw new (_c.apply(Error, [void 0, _d + (_e.sent()) + "'"]))();
10939
+ case 3: return [4 /*yield*/, response.json()];
10940
+ case 4: return [2 /*return*/, _e.sent()];
10941
+ }
10942
+ });
10943
+ });
10944
+ };
10493
10945
  return ChatBackend;
10494
10946
  }(ChatBackendBase));
10495
10947
  ChatBackend.decorators = [
@@ -10533,7 +10985,9 @@
10533
10985
  formField.MatFormFieldModule,
10534
10986
  input.MatInputModule,
10535
10987
  progressSpinner.MatProgressSpinnerModule,
10536
- snackBar.MatSnackBarModule
10988
+ snackBar.MatSnackBarModule,
10989
+ overlay.OverlayModule,
10990
+ portal.PortalModule
10537
10991
  ],
10538
10992
  declarations: [
10539
10993
  BantaComponent,
@@ -10560,6 +11014,8 @@
10560
11014
 
10561
11015
  exports.AttachmentButtonComponent = AttachmentButtonComponent;
10562
11016
  exports.BANTA_SDK_OPTIONS = BANTA_SDK_OPTIONS;
11017
+ exports.BantaAttachmentComponent = BantaAttachmentComponent;
11018
+ exports.BantaAttachmentsComponent = BantaAttachmentsComponent;
10563
11019
  exports.BantaChatComponent = BantaChatComponent;
10564
11020
  exports.BantaCommentsComponent = BantaCommentsComponent;
10565
11021
  exports.BantaCommonModule = BantaCommonModule;
@@ -10568,6 +11024,7 @@
10568
11024
  exports.BantaMarkdownToHtmlPipe = BantaMarkdownToHtmlPipe;
10569
11025
  exports.BantaReplySendOptionsDirective = BantaReplySendOptionsDirective;
10570
11026
  exports.BantaSdkModule = BantaSdkModule;
11027
+ exports.BantaTrustResourceUrlPipe = BantaTrustResourceUrlPipe;
10571
11028
  exports.ChatBackend = ChatBackend;
10572
11029
  exports.ChatBackendBase = ChatBackendBase;
10573
11030
  exports.ChatMessageComponent = ChatMessageComponent;