@banta/sdk 4.6.4 → 4.6.6

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.
@@ -230,7 +230,8 @@
230
230
  ]; };
231
231
 
232
232
  var BantaAttachmentComponent = /** @class */ (function () {
233
- function BantaAttachmentComponent() {
233
+ function BantaAttachmentComponent(elementRef) {
234
+ this.elementRef = elementRef;
234
235
  this.loading = false;
235
236
  this.editing = false;
236
237
  this.loadingMessage = 'Please wait...';
@@ -238,7 +239,18 @@
238
239
  this.errorMessage = 'An error has occurred';
239
240
  this.removed = new rxjs.Subject();
240
241
  this.activated = new rxjs.Subject();
242
+ this.loaded = new rxjs.Subject();
243
+ this._viewLoaded = false;
241
244
  }
245
+ Object.defineProperty(BantaAttachmentComponent.prototype, "attachment", {
246
+ get: function () { return this._attachment; },
247
+ set: function (value) {
248
+ this._attachment = value;
249
+ this.checkLoad();
250
+ },
251
+ enumerable: false,
252
+ configurable: true
253
+ });
242
254
  BantaAttachmentComponent.prototype.ngOnInit = function () {
243
255
  if (typeof window !== 'undefined') {
244
256
  setTimeout(function () {
@@ -248,6 +260,20 @@
248
260
  }, 100);
249
261
  }
250
262
  };
263
+ BantaAttachmentComponent.prototype.ngAfterViewInit = function () {
264
+ this._viewLoaded = true;
265
+ this.checkLoad();
266
+ };
267
+ BantaAttachmentComponent.prototype.checkLoad = function () {
268
+ var _this = this;
269
+ var _a;
270
+ if (!this._attachment || !this._viewLoaded || !((_a = this.elementRef) === null || _a === void 0 ? void 0 : _a.nativeElement))
271
+ return;
272
+ if (typeof window === 'undefined')
273
+ this.loaded.next();
274
+ else
275
+ setTimeout(function () { return _this.loaded.next(); }, 250);
276
+ };
251
277
  BantaAttachmentComponent.prototype.activate = function () {
252
278
  this.activated.next();
253
279
  };
@@ -320,7 +346,9 @@
320
346
  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-attachment{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;margin:1em 0}a.card-attachment img{width:250px;aspect-ratio:16/9;-o-object-fit:cover;object-fit:cover;border-radius:10px}a.card-attachment.has-image h1{font-size:22px}a.card-attachment h1{min-width:0;margin:0 0 .5em;font-size:26px}a.card-attachment cite{min-width:0;opacity:.75;margin-top:1em;display:block;font-size:80%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}a.card-attachment .description{min-width:0;width:100%}a.card-attachment .summary{overflow-x:hidden;text-overflow:ellipsis}.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}@media (max-width:700px){a.card-attachment{flex-direction:column}a.card-attachment img{width:100%}}"]
321
347
  },] }
322
348
  ];
323
- BantaAttachmentComponent.ctorParameters = function () { return []; };
349
+ BantaAttachmentComponent.ctorParameters = function () { return [
350
+ { type: core.ElementRef }
351
+ ]; };
324
352
  BantaAttachmentComponent.propDecorators = {
325
353
  attachment: [{ type: core.Input }],
326
354
  loading: [{ type: core.Input }],
@@ -330,6 +358,7 @@
330
358
  errorMessage: [{ type: core.Input }],
331
359
  removed: [{ type: core.Output }],
332
360
  activated: [{ type: core.Output }],
361
+ loaded: [{ type: core.Output }],
333
362
  isLoading: [{ type: core.HostBinding, args: ['class.loading',] }]
334
363
  };
335
364
 
@@ -337,7 +366,27 @@
337
366
  function BantaAttachmentsComponent() {
338
367
  this.editing = false;
339
368
  this.remove = new rxjs.Subject();
369
+ this.loaded = new rxjs.Subject();
370
+ this.loadedAttachments = new WeakMap();
340
371
  }
372
+ BantaAttachmentsComponent.prototype.ngAfterViewInit = function () {
373
+ };
374
+ BantaAttachmentsComponent.prototype.markAttachmentLoaded = function (attachment) {
375
+ this.loadedAttachments.set(attachment, true);
376
+ if (this.allAttachmentsLoaded)
377
+ this.loaded.next();
378
+ };
379
+ BantaAttachmentsComponent.prototype.isAttachmentLoaded = function (attachment) {
380
+ return this.loadedAttachments.has(attachment);
381
+ };
382
+ Object.defineProperty(BantaAttachmentsComponent.prototype, "allAttachmentsLoaded", {
383
+ get: function () {
384
+ var _this = this;
385
+ return this.attachments.every(function (x) { return _this.isAttachmentLoaded(x); });
386
+ },
387
+ enumerable: false,
388
+ configurable: true
389
+ });
341
390
  BantaAttachmentsComponent.prototype.removeAttachment = function (attachment) {
342
391
  this.remove.next(attachment);
343
392
  };
@@ -370,12 +419,15 @@
370
419
  enumerable: false,
371
420
  configurable: true
372
421
  });
422
+ BantaAttachmentsComponent.prototype.attachmentId = function (index, attachment) {
423
+ return attachment.url;
424
+ };
373
425
  return BantaAttachmentsComponent;
374
426
  }());
375
427
  BantaAttachmentsComponent.decorators = [
376
428
  { type: core.Component, args: [{
377
429
  selector: 'banta-attachments',
378
- 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>",
430
+ 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; trackBy: attachmentId\">\r\n <banta-attachment \r\n [attachment]=\"attachment\"\r\n [editing]=\"editing\"\r\n (loaded)=\"markAttachmentLoaded(attachment)\"\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; trackBy: attachmentId\">\r\n <banta-attachment \r\n [attachment]=\"attachment\"\r\n [editing]=\"editing\"\r\n (loaded)=\"markAttachmentLoaded(attachment)\"\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>",
379
431
  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}"]
380
432
  },] }
381
433
  ];
@@ -383,7 +435,8 @@
383
435
  attachments: [{ type: core.Input }],
384
436
  editing: [{ type: core.Input }],
385
437
  lightbox: [{ type: core.ViewChild, args: ['lightbox',] }],
386
- remove: [{ type: core.Output }]
438
+ remove: [{ type: core.Output }],
439
+ loaded: [{ type: core.Output }]
387
440
  };
388
441
 
389
442
  var COMPONENTS = [
@@ -8413,6 +8466,8 @@
8413
8466
  this.isNew = false;
8414
8467
  this.visible = false;
8415
8468
  this.showReplyAction = true;
8469
+ this.loaded = new rxjs.Subject();
8470
+ this.isLoaded = false;
8416
8471
  this.mine = false;
8417
8472
  this.editing = false;
8418
8473
  this._editStarted = new rxjs.Subject();
@@ -8438,6 +8493,10 @@
8438
8493
  setTimeout(function () { return _this.isNew = false; }, 1000);
8439
8494
  }, randomTime);
8440
8495
  };
8496
+ CommentComponent.prototype.markAttachmentsLoaded = function () {
8497
+ this.isLoaded = true;
8498
+ this.loaded.next();
8499
+ };
8441
8500
  Object.defineProperty(CommentComponent.prototype, "isHighlighted", {
8442
8501
  get: function () {
8443
8502
  var _a, _b, _c;
@@ -8446,6 +8505,39 @@
8446
8505
  enumerable: false,
8447
8506
  configurable: true
8448
8507
  });
8508
+ Object.defineProperty(CommentComponent.prototype, "message", {
8509
+ get: function () {
8510
+ return this._message;
8511
+ },
8512
+ set: function (value) {
8513
+ var _a;
8514
+ this._message = value;
8515
+ if (((_a = this._message.attachments) === null || _a === void 0 ? void 0 : _a.length) > 0) {
8516
+ this.isLoaded = false;
8517
+ }
8518
+ else {
8519
+ this.isLoaded = true;
8520
+ this.loaded.next();
8521
+ }
8522
+ },
8523
+ enumerable: false,
8524
+ configurable: true
8525
+ });
8526
+ CommentComponent.prototype.waitForLoad = function () {
8527
+ return __awaiter(this, void 0, void 0, function () {
8528
+ return __generator(this, function (_d) {
8529
+ switch (_d.label) {
8530
+ case 0:
8531
+ if (this.isLoaded)
8532
+ return [2 /*return*/, true];
8533
+ return [4 /*yield*/, this.loaded.pipe(operators.take(1)).toPromise()];
8534
+ case 1:
8535
+ _d.sent();
8536
+ return [2 /*return*/];
8537
+ }
8538
+ });
8539
+ });
8540
+ };
8449
8541
  Object.defineProperty(CommentComponent.prototype, "userSelected", {
8450
8542
  get: function () {
8451
8543
  return this._userSelected.asObservable();
@@ -8584,7 +8676,7 @@
8584
8676
  CommentComponent.decorators = [
8585
8677
  { type: core.Component, args: [{
8586
8678
  selector: 'banta-comment',
8587
- 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\r\n <button *ngFor=\"let menuItem of customMenuItems\" mat-menu-item (click)=\"menuItem.action(message)\">\r\n <mat-icon>{{menuItem.icon}}</mat-icon>\r\n {{menuItem.label}}\r\n </button>\r\n\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\" [maxlength]=\"maxLength\"></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 [matTooltip]=\"permissions?.canLike ? upvoting ? 'Please wait...' : 'Like' : permissions?.canLikeErrorMessage\" \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",
8679
+ 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\r\n <button *ngFor=\"let menuItem of customMenuItems\" mat-menu-item (click)=\"menuItem.action(message)\">\r\n <mat-icon>{{menuItem.icon}}</mat-icon>\r\n {{menuItem.label}}\r\n </button>\r\n\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 \r\n [attachments]=\"message.attachments\"\r\n (loaded)=\"markAttachmentsLoaded()\"\r\n ></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\" [maxlength]=\"maxLength\"></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 [matTooltip]=\"permissions?.canLike ? upvoting ? 'Please wait...' : 'Like' : permissions?.canLikeErrorMessage\" \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",
8588
8680
  styles: ["@-webkit-keyframes comment-appear{0%{transform:translate(6em)}to{transform:translate(0)}}@keyframes comment-appear{0%{transform:translate(6em)}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:.25s;animation-duration:.25s;-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}"]
8589
8681
  },] }
8590
8682
  ];
@@ -8599,6 +8691,7 @@
8599
8691
  message: [{ type: core.Input }],
8600
8692
  customMenuItems: [{ type: core.Input }],
8601
8693
  showReplyAction: [{ type: core.Input }],
8694
+ loaded: [{ type: core.Output }],
8602
8695
  userSelected: [{ type: core.Output }],
8603
8696
  usernameSelected: [{ type: core.Output }],
8604
8697
  avatarSelected: [{ type: core.Output }],
@@ -8684,7 +8777,6 @@
8684
8777
  _c.sent();
8685
8778
  return [3 /*break*/, 2];
8686
8779
  case 4:
8687
- console.log("Finished loading comments, hasMore=" + this.hasMore);
8688
8780
  if (!this.isMessageLoadedInContext(message)) {
8689
8781
  console.error("Error while loading message in context: Failed to find message " + message.id + ", maybe it was deleted!");
8690
8782
  return [2 /*return*/, false];
@@ -8696,17 +8788,11 @@
8696
8788
  console.error("Error while loading message in context: Message was not present in message list!");
8697
8789
  return [2 /*return*/, false];
8698
8790
  }
8699
- console.log("Total messages: " + items.length);
8700
- console.log("Page size: " + pageSize);
8701
- console.log("Message index: " + index);
8702
8791
  startIndex = Math.max(0, index - pageSize / 2);
8703
- console.log("Start index: " + index);
8704
8792
  this.newMessages = items.splice(0, startIndex);
8705
8793
  this.messages = items.splice(0, pageSize);
8706
8794
  this.olderMessages = items;
8707
- console.log(this.olderMessages.length + " older messages");
8708
- console.log(this.messages.length + " current messages");
8709
- console.log(this.newMessages.length + " newer messages");
8795
+ this.isViewingMore = true;
8710
8796
  return [2 /*return*/];
8711
8797
  }
8712
8798
  });
@@ -8905,7 +8991,6 @@
8905
8991
  else {
8906
8992
  lastMessage = (_b = this.olderMessages[this.olderMessages.length - 1]) !== null && _b !== void 0 ? _b : this.messages[this.messages.length - 1];
8907
8993
  }
8908
- console.dir(lastMessage);
8909
8994
  if (!lastMessage) {
8910
8995
  this.isLoadingMore = false;
8911
8996
  this.hasMore = false;
@@ -8914,6 +8999,8 @@
8914
8999
  return [4 /*yield*/, this.source.loadAfter(lastMessage, nextPageSize)];
8915
9000
  case 1:
8916
9001
  messages = _c.sent();
9002
+ if (this.newestLast)
9003
+ messages = messages.slice().reverse();
8917
9004
  messages.forEach(function (m) { var _a; return (_a = m.transientState) !== null && _a !== void 0 ? _a : (m.transientState = {}); });
8918
9005
  if (this.newestLast)
8919
9006
  this.messages = messages.concat(this.messages);
@@ -8992,6 +9079,28 @@
8992
9079
  }
8993
9080
  return maxPagingCursor;
8994
9081
  };
9082
+ /**
9083
+ * Wait for all currently visible comments to be fully loaded, including all attachments.
9084
+ * Doing this will prevent layout shift when scrolling to a specific comment.
9085
+ */
9086
+ CommentViewComponent.prototype.waitForAllCommentsToLoad = function () {
9087
+ return __awaiter(this, void 0, void 0, function () {
9088
+ return __generator(this, function (_c) {
9089
+ switch (_c.label) {
9090
+ case 0: return [4 /*yield*/, new Promise(function (r) { return setTimeout(r, 100); })];
9091
+ case 1:
9092
+ _c.sent();
9093
+ return [4 /*yield*/, this.sourceLoaded];
9094
+ case 2:
9095
+ _c.sent();
9096
+ return [4 /*yield*/, Promise.all(this.comments.map(function (x) { return x.waitForLoad(); }))];
9097
+ case 3:
9098
+ _c.sent();
9099
+ return [2 /*return*/];
9100
+ }
9101
+ });
9102
+ });
9103
+ };
8995
9104
  CommentViewComponent.prototype.sortMessages = function () {
8996
9105
  var _this = this;
8997
9106
  if (!this.source)
@@ -9156,6 +9265,7 @@
9156
9265
  { hashtag: 'slow', description: 'Be slow when this message is posted' },
9157
9266
  ];
9158
9267
  this.loadingSharedComment = false;
9268
+ this.sharedCommentMissing = false;
9159
9269
  this.sendMessage = function (message) { return __awaiter(_this, void 0, void 0, function () {
9160
9270
  var _a, intercept, e_1;
9161
9271
  return __generator(this, function (_e) {
@@ -9263,10 +9373,6 @@
9263
9373
  });
9264
9374
  BantaCommentsComponent.prototype.ngAfterViewInit = function () {
9265
9375
  var _this = this;
9266
- this.threadViewQuery.changes.subscribe(function (changes) {
9267
- console.log("i see changes...");
9268
- console.dir(changes);
9269
- });
9270
9376
  if (typeof window !== 'undefined') {
9271
9377
  var callback = function () {
9272
9378
  var size = _this.elementRef.nativeElement.getBoundingClientRect();
@@ -9400,6 +9506,21 @@
9400
9506
  enumerable: false,
9401
9507
  configurable: true
9402
9508
  });
9509
+ BantaCommentsComponent.prototype.waitForThreadView = function () {
9510
+ return __awaiter(this, void 0, void 0, function () {
9511
+ return __generator(this, function (_e) {
9512
+ switch (_e.label) {
9513
+ case 0:
9514
+ if (this.threadView)
9515
+ return [2 /*return*/, this.threadView];
9516
+ return [4 /*yield*/, this.threadViewQuery.changes.pipe(operators.take(1)).toPromise()];
9517
+ case 1:
9518
+ _e.sent();
9519
+ return [2 /*return*/, this.threadView];
9520
+ }
9521
+ });
9522
+ });
9523
+ };
9403
9524
  BantaCommentsComponent.prototype.updateLoading = function () {
9404
9525
  var _this = this;
9405
9526
  var _a, _b;
@@ -9438,6 +9559,7 @@
9438
9559
  if (value) {
9439
9560
  if (this.sharedCommentID) {
9440
9561
  this.navigateToSharedComment(this.sharedCommentID);
9562
+ this.lastSharedCommentID = this.sharedCommentID;
9441
9563
  this.sharedCommentID = null;
9442
9564
  }
9443
9565
  this._source.messages.forEach(function (m) { return _this.addParticipant(m); });
@@ -9538,94 +9660,128 @@
9538
9660
  });
9539
9661
  // UI Interactions
9540
9662
  BantaCommentsComponent.prototype.scrollToComment = function (commentId) {
9541
- if (typeof window === 'undefined')
9542
- return;
9543
- setTimeout(function () {
9544
- var comment = document.querySelectorAll("[data-comment-id=\"" + commentId + "\"]");
9545
- if (comment.length > 0) {
9546
- // comment.item(0).scroll({behavior: 'smooth'});
9547
- comment.item(0).scrollIntoView({
9548
- inline: 'center',
9549
- block: 'center'
9550
- });
9551
- }
9552
- }, 1000);
9663
+ return __awaiter(this, void 0, void 0, function () {
9664
+ var comment;
9665
+ return __generator(this, function (_e) {
9666
+ switch (_e.label) {
9667
+ case 0:
9668
+ if (typeof window === 'undefined')
9669
+ return [2 /*return*/];
9670
+ return [4 /*yield*/, this.commentView.waitForAllCommentsToLoad()];
9671
+ case 1:
9672
+ _e.sent();
9673
+ comment = document.querySelector("[data-comment-id=\"" + commentId + "\"]");
9674
+ if (comment) {
9675
+ comment.scrollIntoView({
9676
+ inline: 'center',
9677
+ block: 'center'
9678
+ });
9679
+ }
9680
+ return [2 /*return*/];
9681
+ }
9682
+ });
9683
+ });
9553
9684
  };
9554
9685
  BantaCommentsComponent.prototype.navigateToSharedComment = function (id) {
9555
9686
  var _a, _b, _c, _d;
9556
9687
  return __awaiter(this, void 0, void 0, function () {
9557
9688
  var source, message, e_3, parentMessage, thread;
9689
+ var _this = this;
9558
9690
  return __generator(this, function (_e) {
9559
9691
  switch (_e.label) {
9560
9692
  case 0:
9561
9693
  this.loadingSharedComment = true;
9694
+ return [4 /*yield*/, new Promise(function (r) { return setTimeout(r, 10); })];
9695
+ case 1:
9696
+ _e.sent();
9697
+ this.sharedCommentMissing = false;
9562
9698
  source = this.source;
9563
9699
  return [4 /*yield*/, source.ready];
9564
- case 1:
9700
+ case 2:
9565
9701
  _e.sent();
9566
9702
  return [4 /*yield*/, this.viewReady];
9567
- case 2:
9703
+ case 3:
9568
9704
  _e.sent();
9569
9705
  return [4 /*yield*/, this.loaded];
9570
- case 3:
9706
+ case 4:
9571
9707
  _e.sent();
9572
9708
  console.log("Navigating to shared comment with ID '" + id + "'...");
9573
- _e.label = 4;
9574
- case 4:
9575
- _e.trys.push([4, 6, , 7]);
9576
- return [4 /*yield*/, this.source.get(id)];
9709
+ _e.label = 5;
9577
9710
  case 5:
9578
- message = _e.sent();
9579
- return [3 /*break*/, 7];
9711
+ _e.trys.push([5, 7, , 8]);
9712
+ return [4 /*yield*/, this.source.get(id)];
9580
9713
  case 6:
9714
+ message = _e.sent();
9715
+ return [3 /*break*/, 8];
9716
+ case 7:
9581
9717
  e_3 = _e.sent();
9582
9718
  console.error("Failed to find comment from URL: " + e_3.message);
9583
- alert("Could not load desired comment. It may have been removed.");
9719
+ this.sharedCommentMissing = true;
9720
+ this.loadingSharedComment = false;
9721
+ if (typeof window !== 'undefined') {
9722
+ setTimeout(function () {
9723
+ var notice = _this.element.querySelector('.loading-comment');
9724
+ notice.scrollIntoView({
9725
+ block: 'center',
9726
+ inline: 'center'
9727
+ });
9728
+ }, 200);
9729
+ }
9584
9730
  return [2 /*return*/];
9585
- case 7:
9731
+ case 8:
9586
9732
  (_a = message.transientState) !== null && _a !== void 0 ? _a : (message.transientState = {});
9587
- if (!message.parentMessageId) return [3 /*break*/, 14];
9733
+ if (!message.parentMessageId) return [3 /*break*/, 17];
9588
9734
  return [4 /*yield*/, this.source.get(message.parentMessageId)];
9589
- case 8:
9735
+ case 9:
9590
9736
  parentMessage = _e.sent();
9591
9737
  (_b = parentMessage.transientState) !== null && _b !== void 0 ? _b : (parentMessage.transientState = {});
9592
9738
  // Make sure that this message is loaded and visible to the user
9593
9739
  return [4 /*yield*/, this.commentView.loadMessageInContext(parentMessage)];
9594
- case 9:
9740
+ case 10:
9595
9741
  // Make sure that this message is loaded and visible to the user
9596
9742
  _e.sent();
9597
9743
  return [4 /*yield*/, this.selectMessage(parentMessage)];
9598
- case 10:
9744
+ case 11:
9599
9745
  thread = _e.sent();
9600
9746
  // Need to re-retrieve the message within the new chat source to affect its
9601
9747
  // transient state.
9602
9748
  return [4 /*yield*/, thread.ready];
9603
- case 11:
9749
+ case 12:
9604
9750
  // Need to re-retrieve the message within the new chat source to affect its
9605
9751
  // transient state.
9752
+ _e.sent();
9753
+ // Make sure that this message is loaded and visible to the user
9754
+ return [4 /*yield*/, this.waitForThreadView()];
9755
+ case 13:
9756
+ // Make sure that this message is loaded and visible to the user
9757
+ _e.sent();
9758
+ return [4 /*yield*/, this.threadView.loadMessageInContext(message)];
9759
+ case 14:
9606
9760
  _e.sent();
9607
9761
  return [4 /*yield*/, thread.get(message.id)];
9608
- case 12:
9762
+ case 15:
9609
9763
  message = _e.sent();
9610
9764
  (_c = message.transientState) !== null && _c !== void 0 ? _c : (message.transientState = {});
9611
9765
  message.transientState.highlighted = true;
9612
9766
  console.dir(message);
9613
9767
  return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 500); })];
9614
- case 13:
9768
+ case 16:
9615
9769
  _e.sent();
9616
- return [3 /*break*/, 16];
9617
- case 14:
9770
+ return [3 /*break*/, 19];
9771
+ case 17:
9618
9772
  // Make sure that this message is loaded and visible to the user
9619
9773
  return [4 /*yield*/, this.commentView.loadMessageInContext(message)];
9620
- case 15:
9774
+ case 18:
9621
9775
  // Make sure that this message is loaded and visible to the user
9622
9776
  _e.sent();
9623
9777
  (_d = message.transientState) !== null && _d !== void 0 ? _d : (message.transientState = {});
9624
9778
  message.transientState.highlighted = true;
9625
- _e.label = 16;
9626
- case 16:
9627
- this.scrollToComment(id);
9779
+ _e.label = 19;
9780
+ case 19:
9628
9781
  this.loadingSharedComment = false;
9782
+ return [4 /*yield*/, this.scrollToComment(id)];
9783
+ case 20:
9784
+ _e.sent();
9629
9785
  return [2 /*return*/];
9630
9786
  }
9631
9787
  });
@@ -9758,6 +9914,25 @@
9758
9914
  });
9759
9915
  });
9760
9916
  };
9917
+ BantaCommentsComponent.prototype.toggleSelectedMessage = function (message) {
9918
+ return __awaiter(this, void 0, void 0, function () {
9919
+ return __generator(this, function (_e) {
9920
+ switch (_e.label) {
9921
+ case 0:
9922
+ if (!(this.selectedMessage === message)) return [3 /*break*/, 2];
9923
+ return [4 /*yield*/, this.unselectMessage()];
9924
+ case 1:
9925
+ _e.sent();
9926
+ return [3 /*break*/, 4];
9927
+ case 2: return [4 /*yield*/, this.selectMessage(message)];
9928
+ case 3:
9929
+ _e.sent();
9930
+ _e.label = 4;
9931
+ case 4: return [2 /*return*/];
9932
+ }
9933
+ });
9934
+ });
9935
+ };
9761
9936
  BantaCommentsComponent.prototype.selectMessage = function (message) {
9762
9937
  return __awaiter(this, void 0, void 0, function () {
9763
9938
  var selectedMessageThread;
@@ -9766,17 +9941,23 @@
9766
9941
  switch (_e.label) {
9767
9942
  case 0:
9768
9943
  if (this.selectedMessage === message) {
9769
- this.unselectMessage();
9770
- return [2 /*return*/];
9944
+ console.log("[Banta] Thread " + this.topicID + "/" + message.id + " is already open.");
9945
+ return [2 /*return*/, this.selectedMessageThread];
9771
9946
  }
9772
9947
  this._selected.next(message);
9773
- this.selectedMessage = message;
9948
+ console.log("[Banta] Opening thread for " + this.topicID + "/" + message.id + "...");
9774
9949
  return [4 /*yield*/, this.backend.getSourceForThread(this.topicID, message.id)];
9775
9950
  case 1:
9776
9951
  selectedMessageThread = _e.sent();
9952
+ if (!selectedMessageThread) {
9953
+ console.warn("Failed to locate thread for message " + this.topicID + "/" + message.id + "!");
9954
+ return [2 /*return*/, null];
9955
+ }
9956
+ console.log("[Banta] Thread opened for " + this.topicID + "/" + message.id + ".");
9777
9957
  setTimeout(function () { return _this.selectedMessageVisible = true; });
9778
9958
  setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
9779
9959
  return __generator(this, function (_e) {
9960
+ this.selectedMessage = message;
9780
9961
  this.selectedMessageThread = selectedMessageThread;
9781
9962
  return [2 /*return*/];
9782
9963
  });
@@ -9937,8 +10118,8 @@
9937
10118
  BantaCommentsComponent.decorators = [
9938
10119
  { type: core.Component, args: [{
9939
10120
  selector: 'banta-comments',
9940
- template: "<ng-container *ngIf=\"loading\">\r\n <div class=\"loading-screen\" [class.visible]=\"showLoadingScreen\">\r\n <h1>Loading...</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 [maxLength]=\"maxCommentLength\"\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 class=\"replies\"\r\n #threadView\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 [customMenuItems]=\"customMenuItems\"\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 <div class=\"loading-comment\" *ngIf=\"loadingSharedComment\">\r\n <h1>Loading desired comment...</h1>\r\n <mat-spinner [diameter]=\"300\" [strokeWidth]=\"2\"></mat-spinner>\r\n <p>\r\n If there are a lot of comments, this might take awhile!\r\n </p>\r\n </div>\r\n <banta-comment-view\r\n #commentView\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 [customMenuItems]=\"customMenuItems\"\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",
9941
- 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%}.loading-comment{z-index:100;border:1px solid #333;background:#000;color:#fff;padding:1em;border-radius:4px;text-align:center}.loading-comment h1{font-weight:100;text-align:center}.loading-comment mat-spinner{margin:0 auto}"]
10121
+ template: "<ng-container *ngIf=\"loading\">\r\n <div class=\"loading-screen\" [class.visible]=\"showLoadingScreen\">\r\n <h1>Loading...</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 [maxLength]=\"maxCommentLength\"\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)=\"toggleSelectedMessage(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 class=\"replies\"\r\n #threadView\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 [customMenuItems]=\"customMenuItems\"\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 <div class=\"loading-comment\" *ngIf=\"loadingSharedComment\">\r\n <h1>Loading the comment you linked to...</h1>\r\n <mat-spinner [diameter]=\"300\" [strokeWidth]=\"2\"></mat-spinner>\r\n <p>\r\n If there are a lot of comments, this might take awhile!\r\n </p>\r\n </div>\r\n <div class=\"loading-comment\" *ngIf=\"!loadingSharedComment && lastSharedCommentID\">\r\n <ng-container *ngIf=\"sharedCommentMissing\">\r\n\r\n <a class=\"close\" mat-icon-button matTooltip=\"Close this notice\" href=\"javascript:;\" (click)=\"lastSharedCommentID = null\">\r\n <mat-icon>close</mat-icon>\r\n </a>\r\n\r\n <h1>\r\n <mat-icon>error</mat-icon>\r\n Uh oh!\r\n </h1>\r\n\r\n <p>The comment you linked to can't be found! It may have been removed.</p>\r\n </ng-container>\r\n <ng-container *ngIf=\"!sharedCommentMissing\">\r\n <a class=\"close\" mat-icon-button matTooltip=\"Close this notice\" href=\"javascript:;\" (click)=\"lastSharedCommentID = null\">\r\n <mat-icon>close</mat-icon>\r\n </a>\r\n <button mat-button (click)=\"navigateToSharedComment(lastSharedCommentID)\">\r\n <mat-icon>move_down</mat-icon> Jump to shared comment\r\n </button>\r\n </ng-container>\r\n </div>\r\n\r\n <banta-comment-view\r\n #commentView\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 [customMenuItems]=\"customMenuItems\"\r\n (userSelected)=\"selectMessageUser($event)\"\r\n (sortOrderChanged)=\"sortOrder = $event\"\r\n (selected)=\"toggleSelectedMessage($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",
10122
+ 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%}.loading-comment{z-index:100;border:1px solid #333;background:#000;color:#fff;padding:1em;border-radius:4px;text-align:center;position:relative}.loading-comment a.close{position:absolute;top:1em;right:1em}.loading-comment h1{font-weight:100;text-align:center}.loading-comment mat-spinner{margin:0 auto}"]
9942
10123
  },] }
9943
10124
  ];
9944
10125
  BantaCommentsComponent.ctorParameters = function () { return [
@@ -10525,6 +10706,7 @@
10525
10706
  CommentFieldComponent.prototype.sendMessage = function () {
10526
10707
  return __awaiter(this, void 0, void 0, function () {
10527
10708
  var text, message, e_8;
10709
+ var _this = this;
10528
10710
  return __generator(this, function (_b) {
10529
10711
  switch (_b.label) {
10530
10712
  case 0:
@@ -10565,6 +10747,9 @@
10565
10747
  case 6: return [3 /*break*/, 8];
10566
10748
  case 7:
10567
10749
  this.sending = false;
10750
+ setTimeout(function () {
10751
+ _this.textareaEl.nativeElement.focus();
10752
+ }, 100);
10568
10753
  return [7 /*endfinally*/];
10569
10754
  case 8: return [2 /*return*/];
10570
10755
  }
@@ -11051,7 +11236,7 @@
11051
11236
  };
11052
11237
  ChatSource.prototype.get = function (id) {
11053
11238
  return __awaiter(this, void 0, void 0, function () {
11054
- var message;
11239
+ var message, existingMessage;
11055
11240
  return __generator(this, function (_b) {
11056
11241
  switch (_b.label) {
11057
11242
  case 0:
@@ -11063,7 +11248,14 @@
11063
11248
  return [4 /*yield*/, this.peer.getMessage(id)];
11064
11249
  case 2:
11065
11250
  message = _b.sent();
11066
- this.messageMap.set(id, message);
11251
+ if (this.messageMap.has(id)) {
11252
+ existingMessage = this.messageMap.get(id);
11253
+ Object.assign(existingMessage, message);
11254
+ message = existingMessage;
11255
+ }
11256
+ else {
11257
+ this.messageMap.set(id, message);
11258
+ }
11067
11259
  return [2 /*return*/, message];
11068
11260
  }
11069
11261
  });