@banta/sdk 4.3.3 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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/cdk/overlay'), require('@angular/material/button'), 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/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/cdk/overlay', '@angular/material/button', '@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/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.cdk.overlay, global.ng.material.button, 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.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, overlay, button, formField, input, forms, portal, 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 BantaAttachmentsComponent = /** @class */ (function () {
211
+ function BantaAttachmentsComponent() {
212
+ this.editing = false;
213
+ this.remove = new rxjs.Subject();
214
+ }
215
+ BantaAttachmentsComponent.prototype.removeAttachment = function (attachment) {
216
+ this.remove.next(attachment);
217
+ };
218
+ BantaAttachmentsComponent.prototype.isImageAttachment = function (attachment) {
219
+ if (attachment.type.startsWith('image/'))
220
+ return true;
221
+ return false;
222
+ };
223
+ BantaAttachmentsComponent.prototype.isCardAttachment = function (attachment) {
224
+ if (['card'].includes(attachment.type))
225
+ return true;
226
+ return false;
227
+ };
228
+ BantaAttachmentsComponent.prototype.showLightbox = function (image) {
229
+ this.lightbox.open(image.url, this.attachments
230
+ .filter(function (x) { return x.type === 'image/png'; })
231
+ .map(function (x) { return x.url; }));
232
+ };
233
+ Object.defineProperty(BantaAttachmentsComponent.prototype, "inlineAttachments", {
234
+ get: function () {
235
+ return this.attachments.filter(function (x) { return x.type !== 'card' && (x.style === 'inline' || !x.style); });
236
+ },
237
+ enumerable: false,
238
+ configurable: true
239
+ });
240
+ Object.defineProperty(BantaAttachmentsComponent.prototype, "blockAttachments", {
241
+ get: function () {
242
+ return this.attachments.filter(function (x) { return x.style === 'block' || x.type === 'card'; });
243
+ },
244
+ enumerable: false,
245
+ configurable: true
246
+ });
247
+ return BantaAttachmentsComponent;
248
+ }());
249
+ BantaAttachmentsComponent.decorators = [
250
+ { type: core.Component, args: [{
251
+ selector: 'banta-attachments',
252
+ 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>",
253
+ 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}"]
254
+ },] }
255
+ ];
256
+ BantaAttachmentsComponent.propDecorators = {
257
+ attachments: [{ type: core.Input }],
258
+ editing: [{ type: core.Input }],
259
+ lightbox: [{ type: core.ViewChild, args: ['lightbox',] }],
260
+ remove: [{ type: core.Output }]
261
+ };
262
+
263
+ var BantaTrustResourceUrlPipe = /** @class */ (function () {
264
+ function BantaTrustResourceUrlPipe(sanitizer) {
265
+ this.sanitizer = sanitizer;
266
+ }
267
+ BantaTrustResourceUrlPipe.prototype.transform = function (value) {
268
+ if (!value)
269
+ return undefined;
270
+ return this.sanitizer.bypassSecurityTrustResourceUrl(value);
271
+ };
272
+ return BantaTrustResourceUrlPipe;
273
+ }());
274
+ BantaTrustResourceUrlPipe.decorators = [
275
+ { type: core.Pipe, args: [{
276
+ name: 'trustResourceUrl'
277
+ },] }
278
+ ];
279
+ BantaTrustResourceUrlPipe.ctorParameters = function () { return [
280
+ { type: platformBrowser.DomSanitizer }
281
+ ]; };
282
+
283
+ var BantaAttachmentComponent = /** @class */ (function () {
284
+ function BantaAttachmentComponent() {
285
+ this.loading = false;
286
+ this.editing = false;
287
+ this.loadingMessage = 'Please wait...';
288
+ this.error = false;
289
+ this.errorMessage = 'An error has occurred';
290
+ this.removed = new rxjs.Subject();
291
+ this.activated = new rxjs.Subject();
292
+ }
293
+ BantaAttachmentComponent.prototype.activate = function () {
294
+ this.activated.next();
295
+ };
296
+ BantaAttachmentComponent.prototype.remove = function () {
297
+ this.removed.next();
298
+ };
299
+ Object.defineProperty(BantaAttachmentComponent.prototype, "isError", {
300
+ get: function () {
301
+ var _a, _b;
302
+ return this.error || ((_b = (_a = this.attachment) === null || _a === void 0 ? void 0 : _a.transientState) === null || _b === void 0 ? void 0 : _b.error);
303
+ },
304
+ enumerable: false,
305
+ configurable: true
306
+ });
307
+ Object.defineProperty(BantaAttachmentComponent.prototype, "theErrorMessage", {
308
+ get: function () {
309
+ var _a, _b;
310
+ return this.errorMessage || ((_b = (_a = this.attachment) === null || _a === void 0 ? void 0 : _a.transientState) === null || _b === void 0 ? void 0 : _b.errorMessage);
311
+ },
312
+ enumerable: false,
313
+ configurable: true
314
+ });
315
+ Object.defineProperty(BantaAttachmentComponent.prototype, "isLoading", {
316
+ get: function () {
317
+ var _a;
318
+ return this.loading || !this.attachment || ((_a = this.attachment.transientState) === null || _a === void 0 ? void 0 : _a.loading) || !this.attachment.url;
319
+ },
320
+ enumerable: false,
321
+ configurable: true
322
+ });
323
+ Object.defineProperty(BantaAttachmentComponent.prototype, "isImageAttachment", {
324
+ get: function () {
325
+ if (this.attachment.type.startsWith('image/'))
326
+ return true;
327
+ return false;
328
+ },
329
+ enumerable: false,
330
+ configurable: true
331
+ });
332
+ return BantaAttachmentComponent;
333
+ }());
334
+ BantaAttachmentComponent.decorators = [
335
+ { type: core.Component, args: [{
336
+ selector: 'banta-attachment',
337
+ 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>",
338
+ 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}"]
339
+ },] }
340
+ ];
341
+ BantaAttachmentComponent.propDecorators = {
342
+ attachment: [{ type: core.Input }],
343
+ loading: [{ type: core.Input }],
344
+ editing: [{ type: core.Input }],
345
+ loadingMessage: [{ type: core.Input }],
346
+ error: [{ type: core.Input }],
347
+ errorMessage: [{ type: core.Input }],
348
+ removed: [{ type: core.Output }],
349
+ activated: [{ type: core.Output }],
350
+ isLoading: [{ type: core.HostBinding, args: ['class.loading',] }]
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
  },] }
@@ -7277,9 +7425,123 @@
7277
7425
  },] }
7278
7426
  ];
7279
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
+
7280
7536
  var ChatBackendBase = /** @class */ (function () {
7281
7537
  function ChatBackendBase() {
7282
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));
7283
7545
  }
7284
7546
  Object.defineProperty(ChatBackendBase.prototype, "userChanged", {
7285
7547
  get: function () {
@@ -7299,6 +7561,26 @@
7299
7561
  enumerable: false,
7300
7562
  configurable: true
7301
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
+ });
7302
7584
  return ChatBackendBase;
7303
7585
  }());
7304
7586
 
@@ -8349,11 +8631,6 @@
8349
8631
  this._avatarSelected.next(user);
8350
8632
  this.selectUser();
8351
8633
  };
8352
- CommentComponent.prototype.showLightbox = function (image) {
8353
- this.lightbox.open(image.url, this.message.attachments
8354
- .filter(function (x) { return x.type === 'image/png'; })
8355
- .map(function (x) { return x.url; }));
8356
- };
8357
8634
  CommentComponent.prototype.avatarForUser = function (user) {
8358
8635
  var url = this.genericAvatarUrl;
8359
8636
  if (user && user.avatarUrl) {
@@ -8374,8 +8651,8 @@
8374
8651
  CommentComponent.decorators = [
8375
8652
  { type: core.Component, args: [{
8376
8653
  selector: 'banta-comment',
8377
- 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 <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 <banta-timestamp [value]=\"message.sentAt\"></banta-timestamp>\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\">\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",
8378
- 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}.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}.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}"]
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}"]
8379
8656
  },] }
8380
8657
  ];
8381
8658
  CommentComponent.propDecorators = {
@@ -8400,8 +8677,7 @@
8400
8677
  editEnded: [{ type: core.Output }],
8401
8678
  shared: [{ type: core.Output }],
8402
8679
  genericAvatarUrl: [{ type: core.Input }],
8403
- commentId: [{ type: core.HostBinding, args: ['attr.data-comment-id',] }],
8404
- lightbox: [{ type: core.ViewChild, args: ['lightbox',] }]
8680
+ commentId: [{ type: core.HostBinding, args: ['attr.data-comment-id',] }]
8405
8681
  };
8406
8682
 
8407
8683
  var CommentViewComponent = /** @class */ (function () {
@@ -9459,7 +9735,7 @@
9459
9735
  { type: core.Component, args: [{
9460
9736
  selector: 'banta-comments',
9461
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",
9462
- 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%}"]
9463
9739
  },] }
9464
9740
  ];
9465
9741
  BantaCommentsComponent.ctorParameters = function () { return [
@@ -9583,14 +9859,17 @@
9583
9859
  };
9584
9860
 
9585
9861
  var CommentFieldComponent = /** @class */ (function () {
9586
- function CommentFieldComponent() {
9862
+ function CommentFieldComponent(chatBackend) {
9863
+ this.chatBackend = chatBackend;
9587
9864
  this.canComment = true;
9588
9865
  this.allowAttachments = false;
9589
9866
  this.signInSelected = new rxjs.Subject();
9590
9867
  this.editAvatarSelected = new rxjs.Subject();
9591
9868
  this.sending = false;
9592
9869
  this.expandError = false;
9593
- this.text = '';
9870
+ this._text = '';
9871
+ this.attachmentScrapeDebounce = 1500;
9872
+ this.attachmentFragments = new Map();
9594
9873
  this.sendLabel = 'Send';
9595
9874
  this.sendingLabel = 'Sending';
9596
9875
  this.label = 'Post a comment';
@@ -9606,6 +9885,171 @@
9606
9885
  this.autoCompleteSelected = 0;
9607
9886
  this.chatMessageAttachments = [];
9608
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
+ };
9609
10053
  Object.defineProperty(CommentFieldComponent.prototype, "userAvatarUrl", {
9610
10054
  get: function () {
9611
10055
  var _a;
@@ -9833,7 +10277,7 @@
9833
10277
  };
9834
10278
  CommentFieldComponent.prototype.sendMessage = function () {
9835
10279
  return __awaiter(this, void 0, void 0, function () {
9836
- var text, message, e_1;
10280
+ var text, message, e_8;
9837
10281
  return __generator(this, function (_b) {
9838
10282
  switch (_b.label) {
9839
10283
  case 0:
@@ -9865,11 +10309,11 @@
9865
10309
  this.chatMessageAttachments = [];
9866
10310
  return [3 /*break*/, 6];
9867
10311
  case 4:
9868
- e_1 = _b.sent();
10312
+ e_8 = _b.sent();
9869
10313
  return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 1000); })];
9870
10314
  case 5:
9871
10315
  _b.sent();
9872
- this.indicateError(e_1.message);
10316
+ this.indicateError(e_8.message);
9873
10317
  return [3 /*break*/, 6];
9874
10318
  case 6: return [3 /*break*/, 8];
9875
10319
  case 7:
@@ -9889,8 +10333,10 @@
9889
10333
  _this.chatMessageAttachments = _this.chatMessageAttachments.filter(function (x) { return x !== attachment; });
9890
10334
  }, 3000);
9891
10335
  };
9892
- CommentFieldComponent.prototype.removeAttachment = function (index) {
9893
- 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);
9894
10340
  };
9895
10341
  CommentFieldComponent.prototype.alertError = function () {
9896
10342
  if (!this.sendError)
@@ -9902,10 +10348,13 @@
9902
10348
  CommentFieldComponent.decorators = [
9903
10349
  { type: core.Component, args: [{
9904
10350
  selector: 'banta-comment-field',
9905
- 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>",
9906
- 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}"]
9907
10353
  },] }
9908
10354
  ];
10355
+ CommentFieldComponent.ctorParameters = function () { return [
10356
+ { type: ChatBackendBase }
10357
+ ]; };
9909
10358
  CommentFieldComponent.propDecorators = {
9910
10359
  source: [{ type: core.Input }],
9911
10360
  user: [{ type: core.Input }],
@@ -10464,6 +10913,35 @@
10464
10913
  ChatBackend.prototype.watchMessage = function (message, handler) {
10465
10914
  throw new Error("Method not implemented.");
10466
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
+ };
10467
10945
  return ChatBackend;
10468
10946
  }(ChatBackendBase));
10469
10947
  ChatBackend.decorators = [