@banta/sdk 4.7.9 → 4.7.11

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.
@@ -2,6 +2,8 @@ import { Observable, Subject, BehaviorSubject, Subscription } from 'rxjs';
2
2
  import { publish, take } from 'rxjs/operators';
3
3
  import * as i0 from '@angular/core';
4
4
  import { Component, Input, ViewChild, Pipe, Inject, Optional, Output, HostBinding, NgModule, ViewChildren, Directive, TemplateRef, ContentChild, Injectable } from '@angular/core';
5
+ import * as i1 from 'projects/sdk/src/lib/common/timer-pool.service';
6
+ import { TimerPool } from 'projects/sdk/src/lib/common/timer-pool.service';
5
7
  import * as i2 from '@angular/common';
6
8
  import { CommonModule } from '@angular/common';
7
9
  import * as i2$1 from '@angular/material/icon';
@@ -9,7 +11,7 @@ import { MatIconModule } from '@angular/material/icon';
9
11
  import * as marked from 'marked';
10
12
  import createDOMPurify from 'dompurify';
11
13
  import twemoji$1 from 'twemoji';
12
- import * as i1 from '@angular/platform-browser';
14
+ import * as i1$1 from '@angular/platform-browser';
13
15
  import * as i3 from '@angular/cdk/bidi';
14
16
  import * as i4 from '@angular/material/progress-spinner';
15
17
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@@ -21,7 +23,7 @@ import * as i6$1 from '@angular/material/form-field';
21
23
  import { MatFormFieldModule } from '@angular/material/form-field';
22
24
  import * as i7 from '@angular/material/input';
23
25
  import { MatInputModule } from '@angular/material/input';
24
- import * as i1$1 from '@angular/cdk/overlay';
26
+ import * as i1$2 from '@angular/cdk/overlay';
25
27
  import { OverlayModule } from '@angular/cdk/overlay';
26
28
  import * as i4$2 from '@angular/cdk/portal';
27
29
  import { PortalModule } from '@angular/cdk/portal';
@@ -31,7 +33,7 @@ import * as i7$1 from '@angular/material/menu';
31
33
  import { MatMenuModule } from '@angular/material/menu';
32
34
  import * as i11 from '@angular/material/tooltip';
33
35
  import { MatTooltipModule } from '@angular/material/tooltip';
34
- import * as i1$2 from '@banta/common';
36
+ import * as i1$3 from '@banta/common';
35
37
  import { CommentsOrder, SocketRPC, RpcEvent, DurableSocket } from '@banta/common';
36
38
  import * as i2$3 from '@angular/router';
37
39
  import * as i3$3 from '@angular/material/snack-bar';
@@ -57,20 +59,24 @@ function lazyConnection(options) {
57
59
  }
58
60
 
59
61
  class TimestampComponent {
60
- constructor() {
62
+ constructor(timerPool) {
63
+ this.timerPool = timerPool;
61
64
  this.relative = '';
62
65
  this.tooltip = '';
63
- this.updateInterval = null;
66
+ this.timerInterval = 0;
67
+ this._destroyed = false;
64
68
  this.showAbsolute = false;
65
69
  }
66
70
  ngOnDestroy() {
67
- if (this.updateInterval)
68
- clearInterval(this.updateInterval);
71
+ this._destroyed = true;
72
+ this.timerUnsubscribe?.();
69
73
  }
70
74
  get value() {
71
75
  return this._value;
72
76
  }
73
77
  update() {
78
+ if (this._destroyed)
79
+ return;
74
80
  let now = Date.now();
75
81
  let diff = now - this.value;
76
82
  let minute = 1000 * 60;
@@ -132,18 +138,23 @@ class TimestampComponent {
132
138
  updateTime = 1000 * 30;
133
139
  }
134
140
  if (typeof window !== 'undefined') {
135
- clearInterval(this.updateInterval);
136
- if (updateTime > 0) {
137
- this.updateInterval = setInterval(() => this.update());
141
+ if (this.timerInterval !== updateTime) {
142
+ this.timerInterval = updateTime;
143
+ this.timerUnsubscribe?.();
144
+ if (updateTime > 0) {
145
+ this.timerUnsubscribe = this.timerPool.addTimer(updateTime, () => this.update());
146
+ }
138
147
  }
139
148
  }
140
149
  }
141
150
  set value(v) {
142
- this._value = v;
143
- this.update();
151
+ if (this._value !== v) {
152
+ this._value = v;
153
+ this.update();
154
+ }
144
155
  }
145
156
  }
146
- TimestampComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TimestampComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
157
+ TimestampComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TimestampComponent, deps: [{ token: i1.TimerPool }], target: i0.ɵɵFactoryTarget.Component });
147
158
  TimestampComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: TimestampComponent, selector: "banta-timestamp", inputs: { value: "value" }, ngImport: i0, template: `
148
159
  <span *ngIf="showAbsolute" [title]="value | date : 'short'">
149
160
  {{value | date : 'shortDate'}}
@@ -162,7 +173,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
162
173
  {{relative}}
163
174
  </span>
164
175
  ` }]
165
- }], propDecorators: { value: [{
176
+ }], ctorParameters: function () { return [{ type: i1.TimerPool }]; }, propDecorators: { value: [{
166
177
  type: Input
167
178
  }] } });
168
179
 
@@ -264,14 +275,14 @@ class BantaMarkdownToHtmlPipe {
264
275
  }));
265
276
  }
266
277
  }
267
- BantaMarkdownToHtmlPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaMarkdownToHtmlPipe, deps: [{ token: i1.DomSanitizer }, { token: BANTA_SDK_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Pipe });
278
+ BantaMarkdownToHtmlPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaMarkdownToHtmlPipe, deps: [{ token: i1$1.DomSanitizer }, { token: BANTA_SDK_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Pipe });
268
279
  BantaMarkdownToHtmlPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaMarkdownToHtmlPipe, name: "markdownToHtml" });
269
280
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaMarkdownToHtmlPipe, decorators: [{
270
281
  type: Pipe,
271
282
  args: [{
272
283
  name: 'markdownToHtml'
273
284
  }]
274
- }], ctorParameters: function () { return [{ type: i1.DomSanitizer }, { type: undefined, decorators: [{
285
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }, { type: undefined, decorators: [{
275
286
  type: Inject,
276
287
  args: [BANTA_SDK_OPTIONS]
277
288
  }, {
@@ -288,14 +299,14 @@ class BantaTrustResourceUrlPipe {
288
299
  return this.sanitizer.bypassSecurityTrustResourceUrl(value);
289
300
  }
290
301
  }
291
- BantaTrustResourceUrlPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaTrustResourceUrlPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
302
+ BantaTrustResourceUrlPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaTrustResourceUrlPipe, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
292
303
  BantaTrustResourceUrlPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaTrustResourceUrlPipe, name: "trustResourceUrl" });
293
304
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaTrustResourceUrlPipe, decorators: [{
294
305
  type: Pipe,
295
306
  args: [{
296
307
  name: 'trustResourceUrl'
297
308
  }]
298
- }], ctorParameters: function () { return [{ type: i1.DomSanitizer }]; } });
309
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }]; } });
299
310
 
300
311
  class BantaMentionLinkerPipe {
301
312
  transform(value, links) {
@@ -386,15 +397,13 @@ class BantaAttachmentComponent {
386
397
  || !this.attachment.url);
387
398
  }
388
399
  get isImageAttachment() {
389
- if (this.attachment.type.startsWith('image/'))
390
- return true;
391
- return false;
400
+ return this.attachment?.type?.startsWith('image/');
392
401
  }
393
402
  get hasFrame() {
394
403
  if (!this.attachment)
395
404
  return false;
396
405
  return this.attachment.type === 'iframe' || (this.attachment.type === 'card'
397
- && this.attachment.card.player);
406
+ && this.attachment.card?.player);
398
407
  }
399
408
  get frameUrl() {
400
409
  if (!this.attachment)
@@ -473,11 +482,14 @@ class BantaAttachmentsComponent {
473
482
  .filter(x => x.type === 'image/png')
474
483
  .map(x => x.url));
475
484
  }
485
+ get validAttachments() {
486
+ return this.attachments.filter(x => x.type);
487
+ }
476
488
  get inlineAttachments() {
477
- return this.attachments.filter(x => x.type !== 'card' && (x.style === 'inline' || !x.style));
489
+ return this.validAttachments.filter(x => x.type !== 'card' && (x.style === 'inline' || !x.style));
478
490
  }
479
491
  get blockAttachments() {
480
- return this.attachments.filter(x => x.style === 'block' || x.type === 'card');
492
+ return this.validAttachments.filter(x => x.style === 'block' || x.type === 'card');
481
493
  }
482
494
  attachmentId(index, attachment) {
483
495
  return attachment.url;
@@ -511,6 +523,14 @@ const COMPONENTS$3 = [
511
523
  BantaAttachmentsComponent
512
524
  ];
513
525
  class BantaCommonModule {
526
+ static forRoot() {
527
+ return {
528
+ ngModule: BantaCommonModule,
529
+ providers: [
530
+ TimerPool
531
+ ]
532
+ };
533
+ }
514
534
  }
515
535
  BantaCommonModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
516
536
  BantaCommonModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, declarations: [TimestampComponent,
@@ -7127,12 +7147,12 @@ class EmojiSelectorPanelComponent {
7127
7147
  this.categories = this.pairs(cats).map(pair => pair[1]);
7128
7148
  }
7129
7149
  }
7130
- EmojiSelectorPanelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorPanelComponent, deps: [{ token: i1.DomSanitizer }, { token: BANTA_SDK_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Component });
7150
+ EmojiSelectorPanelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorPanelComponent, deps: [{ token: i1$1.DomSanitizer }, { token: BANTA_SDK_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Component });
7131
7151
  EmojiSelectorPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: EmojiSelectorPanelComponent, selector: "emoji-selector-panel", outputs: { selected: "selected" }, ngImport: i0, template: "<div class=\"search-box\" *ngIf=\"searchVisible\">\r\n\t<a mat-icon-button href=\"javascript:;\" (click)=\"hideSearch()\">\r\n\t\t<mat-icon>arrow_back</mat-icon>\r\n\t</a>\r\n\t<mat-form-field appearance=\"outline\" floatLabel=\"always\">\r\n\t\t<mat-label>Search for emoji</mat-label>\r\n\t\t<input name=\"search\" type=\"text\" matInput placeholder=\"Start typing\" [(ngModel)]=\"searchQuery\" />\r\n\t</mat-form-field>\r\n</div>\r\n\r\n<div class=\"selector\">\r\n\t<ng-container *ngIf=\"searchVisible\">\r\n\t\t<div class=\"emoji-list\">\r\n\t\t\t<a href=\"javascript:;\" (click)=\"select(emoji.char)\" \r\n\t\t\t\t*ngFor=\"let emoji of searchResults\" [innerHtml]=\"emoji.html || ''\">\r\n\t\t\t</a>\r\n\t\t</div>\r\n\t</ng-container>\r\n\t<ng-container *ngIf=\"!searchVisible\">\r\n\t\t<div class=\"categories\">\r\n\t\t\t<ng-container *ngIf=\"!searchVisible\">\r\n\t\t\t\t<a [title]=\"humanize(category.name)\" [class.active]=\"activeCategory === category.name\" mat-icon-button *ngFor=\"let category of categories\" (click)=\"activeCategory = category.name\">\r\n\t\t\t\t\t<mat-icon>{{category.icon}}</mat-icon>\r\n\t\t\t\t</a>\r\n\r\n\t\t\t\t<a title=\"Search\" [class.active] mat-icon-button (click)=\"showSearch()\">\r\n\t\t\t\t\t<mat-icon>search</mat-icon>\r\n\t\t\t\t</a>\r\n\t\t\t</ng-container>\r\n\t\t</div>\r\n\t\t<ng-container *ngFor=\"let category of categories\">\r\n\t\t\t<div class=\"emoji-list\" *ngIf=\"activeCategory && activeCategory == category.name\">\r\n\t\t\t\t<a href=\"javascript:;\" (click)=\"select(emoji.char)\" \r\n\t\t\t\t\t*ngFor=\"let emoji of category.emojis\" [innerHtml]=\"emoji.html || ''\">\r\n\t\t\t\t</a>\r\n\t\t\t</div>\r\n\t\t</ng-container>\r\n\t</ng-container>\r\n</div>", styles: [":host{background:#111;color:#fff;border:1px solid #333;border-radius:5px;padding:.5em;width:calc(9*(32px + 1em));max-width:calc(100vw - 1.5em - 5px)}.selector{display:flex;flex-direction:column}.categories a{opacity:.25;transition:.4s opacity ease-in-out}.categories a:hover{opacity:.5}.categories a.active{opacity:1}.emoji-list{flex-grow:1;overflow-y:auto;height:20em}.emoji-list a{display:inline-block;padding:2px;margin:4px;background-color:#111}.emoji-list a ::ng-deep .emoji{width:32px;height:32px}.emoji-list a:hover{background-color:#333}.search-box{display:flex;align-items:baseline}.search-box mat-form-field{flex-grow:1}@media (max-width: 500px){.selector{flex-direction:row;height:27em}.emoji-list{height:auto}}:host-context(.banta-mobile) .selector{flex-direction:row;height:27em}:host-context(.banta-mobile) .emoji-list{height:auto}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-icon-button], a[mat-fab], a[mat-mini-fab], a[mat-stroked-button], a[mat-flat-button]", inputs: ["disabled", "disableRipple", "color", "tabIndex"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i6$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }] });
7132
7152
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorPanelComponent, decorators: [{
7133
7153
  type: Component,
7134
7154
  args: [{ selector: 'emoji-selector-panel', template: "<div class=\"search-box\" *ngIf=\"searchVisible\">\r\n\t<a mat-icon-button href=\"javascript:;\" (click)=\"hideSearch()\">\r\n\t\t<mat-icon>arrow_back</mat-icon>\r\n\t</a>\r\n\t<mat-form-field appearance=\"outline\" floatLabel=\"always\">\r\n\t\t<mat-label>Search for emoji</mat-label>\r\n\t\t<input name=\"search\" type=\"text\" matInput placeholder=\"Start typing\" [(ngModel)]=\"searchQuery\" />\r\n\t</mat-form-field>\r\n</div>\r\n\r\n<div class=\"selector\">\r\n\t<ng-container *ngIf=\"searchVisible\">\r\n\t\t<div class=\"emoji-list\">\r\n\t\t\t<a href=\"javascript:;\" (click)=\"select(emoji.char)\" \r\n\t\t\t\t*ngFor=\"let emoji of searchResults\" [innerHtml]=\"emoji.html || ''\">\r\n\t\t\t</a>\r\n\t\t</div>\r\n\t</ng-container>\r\n\t<ng-container *ngIf=\"!searchVisible\">\r\n\t\t<div class=\"categories\">\r\n\t\t\t<ng-container *ngIf=\"!searchVisible\">\r\n\t\t\t\t<a [title]=\"humanize(category.name)\" [class.active]=\"activeCategory === category.name\" mat-icon-button *ngFor=\"let category of categories\" (click)=\"activeCategory = category.name\">\r\n\t\t\t\t\t<mat-icon>{{category.icon}}</mat-icon>\r\n\t\t\t\t</a>\r\n\r\n\t\t\t\t<a title=\"Search\" [class.active] mat-icon-button (click)=\"showSearch()\">\r\n\t\t\t\t\t<mat-icon>search</mat-icon>\r\n\t\t\t\t</a>\r\n\t\t\t</ng-container>\r\n\t\t</div>\r\n\t\t<ng-container *ngFor=\"let category of categories\">\r\n\t\t\t<div class=\"emoji-list\" *ngIf=\"activeCategory && activeCategory == category.name\">\r\n\t\t\t\t<a href=\"javascript:;\" (click)=\"select(emoji.char)\" \r\n\t\t\t\t\t*ngFor=\"let emoji of category.emojis\" [innerHtml]=\"emoji.html || ''\">\r\n\t\t\t\t</a>\r\n\t\t\t</div>\r\n\t\t</ng-container>\r\n\t</ng-container>\r\n</div>", styles: [":host{background:#111;color:#fff;border:1px solid #333;border-radius:5px;padding:.5em;width:calc(9*(32px + 1em));max-width:calc(100vw - 1.5em - 5px)}.selector{display:flex;flex-direction:column}.categories a{opacity:.25;transition:.4s opacity ease-in-out}.categories a:hover{opacity:.5}.categories a.active{opacity:1}.emoji-list{flex-grow:1;overflow-y:auto;height:20em}.emoji-list a{display:inline-block;padding:2px;margin:4px;background-color:#111}.emoji-list a ::ng-deep .emoji{width:32px;height:32px}.emoji-list a:hover{background-color:#333}.search-box{display:flex;align-items:baseline}.search-box mat-form-field{flex-grow:1}@media (max-width: 500px){.selector{flex-direction:row;height:27em}.emoji-list{height:auto}}:host-context(.banta-mobile) .selector{flex-direction:row;height:27em}:host-context(.banta-mobile) .emoji-list{height:auto}\n"] }]
7135
- }], ctorParameters: function () { return [{ type: i1.DomSanitizer }, { type: undefined, decorators: [{
7155
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }, { type: undefined, decorators: [{
7136
7156
  type: Inject,
7137
7157
  args: [BANTA_SDK_OPTIONS]
7138
7158
  }, {
@@ -7206,7 +7226,7 @@ class EmojiSelectorButtonComponent {
7206
7226
  this.overlayRef.attach(this.selectorPanelTemplate);
7207
7227
  }
7208
7228
  }
7209
- EmojiSelectorButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorButtonComponent, deps: [{ token: i0.ElementRef }, { token: i1$1.Overlay }], target: i0.ɵɵFactoryTarget.Component });
7229
+ EmojiSelectorButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorButtonComponent, deps: [{ token: i0.ElementRef }, { token: i1$2.Overlay }], target: i0.ɵɵFactoryTarget.Component });
7210
7230
  EmojiSelectorButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: EmojiSelectorButtonComponent, selector: "emoji-selector-button", inputs: { overlayX: "overlayX", overlayY: "overlayY", originX: "originX", originY: "originY" }, outputs: { selected: "selected" }, viewQueries: [{ propertyName: "selectorPanelTemplate", first: true, predicate: ["selectorPanelTemplate"], descendants: true }], ngImport: i0, template: `
7211
7231
  <button #button type="button" mat-icon-button (click)="show()">
7212
7232
  <mat-icon>emoji_emotions</mat-icon>
@@ -7231,7 +7251,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
7231
7251
  ></emoji-selector-panel>
7232
7252
  </ng-template>
7233
7253
  `, styles: [":host{display:block;position:relative}button{color:#666}\n"] }]
7234
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$1.Overlay }]; }, propDecorators: { selectorPanelTemplate: [{
7254
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$2.Overlay }]; }, propDecorators: { selectorPanelTemplate: [{
7235
7255
  type: ViewChild,
7236
7256
  args: ['selectorPanelTemplate']
7237
7257
  }], selected: [{
@@ -7946,12 +7966,12 @@ class AttachmentButtonComponent {
7946
7966
  }
7947
7967
  }
7948
7968
  }
7949
- AttachmentButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AttachmentButtonComponent, deps: [{ token: i1$2.CDNProvider }], target: i0.ɵɵFactoryTarget.Component });
7969
+ AttachmentButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AttachmentButtonComponent, deps: [{ token: i1$3.CDNProvider }], target: i0.ɵɵFactoryTarget.Component });
7950
7970
  AttachmentButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: AttachmentButtonComponent, selector: "banta-attachment-button", outputs: { addedAttachment: "addedAttachment", attachmentError: "attachmentError" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileUpload"], descendants: true }], ngImport: i0, template: "<button matTooltip=\"Add an image or gif\" #button type=\"button\" mat-icon-button (click)=\"show()\">\r\n\t<mat-icon>image</mat-icon>\r\n</button>\r\n<input style=\"display: none;\" #fileUpload [multiple]=\"false\" (change)=\"fileChange($event)\" type=\"file\" >", styles: ["button{color:#666}\n"], dependencies: [{ kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
7951
7971
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AttachmentButtonComponent, decorators: [{
7952
7972
  type: Component,
7953
7973
  args: [{ selector: 'banta-attachment-button', template: "<button matTooltip=\"Add an image or gif\" #button type=\"button\" mat-icon-button (click)=\"show()\">\r\n\t<mat-icon>image</mat-icon>\r\n</button>\r\n<input style=\"display: none;\" #fileUpload [multiple]=\"false\" (change)=\"fileChange($event)\" type=\"file\" >", styles: ["button{color:#666}\n"] }]
7954
- }], ctorParameters: function () { return [{ type: i1$2.CDNProvider }]; }, propDecorators: { fileInput: [{
7974
+ }], ctorParameters: function () { return [{ type: i1$3.CDNProvider }]; }, propDecorators: { fileInput: [{
7955
7975
  type: ViewChild,
7956
7976
  args: ['fileUpload', { static: false }]
7957
7977
  }], addedAttachment: [{
@@ -8104,7 +8124,10 @@ class CommentFieldComponent {
8104
8124
  if (this._source.connectionStateChanged) {
8105
8125
  this._subs.add(this._source.connectionStateChanged.subscribe(state => {
8106
8126
  if (state === 'lost') {
8107
- this.transientMessage = `Reconnecting...`;
8127
+ if (this._source.errorState === 'server-issue')
8128
+ this.transientMessage = `Error occurred, trying again...`;
8129
+ else
8130
+ this.transientMessage = `Reconnecting...`;
8108
8131
  }
8109
8132
  else if (state === 'restored') {
8110
8133
  this.transientMessage = undefined;
@@ -8695,7 +8718,7 @@ class BantaCommentsComponent {
8695
8718
  return this.threadView;
8696
8719
  }
8697
8720
  updateLoading() {
8698
- if (this.source?.state && this.source?.state !== 'connecting') {
8721
+ if (this.source?.state && !['connecting', 'lost'].includes(this.source?.state)) {
8699
8722
  clearInterval(this._loadingTimer);
8700
8723
  this.loadingMessage = `Here we go!`;
8701
8724
  setTimeout(() => {
@@ -10063,6 +10086,8 @@ class ChatSource extends SocketRPC {
10063
10086
  this.subscription = new Subscription();
10064
10087
  this._state = 'connecting';
10065
10088
  this._connectionStateChanged = new Subject();
10089
+ this.wasRestored = false;
10090
+ this.subscribeAttempt = 0;
10066
10091
  this.messageMap = new Map();
10067
10092
  this._messageReceived = new Subject();
10068
10093
  this._messageUpdated = new Subject();
@@ -10083,21 +10108,19 @@ class ChatSource extends SocketRPC {
10083
10108
  }
10084
10109
  async bind(socket) {
10085
10110
  super.bind(socket);
10086
- this.state = 'connected';
10087
- this.markReady();
10088
- await this.subscribeToTopic();
10089
10111
  this.subscription.add(this.backend.userChanged.subscribe(() => this.authenticate()));
10090
10112
  socket.addEventListener('open', async () => {
10091
- this.state = 'connected';
10113
+ console.log(`[Banta/${this.identifier}] Socket is open`);
10092
10114
  });
10093
10115
  socket.addEventListener('lost', async () => {
10094
10116
  this.state = 'lost';
10095
10117
  });
10096
10118
  socket.addEventListener('restore', async () => {
10097
- this.state = 'restored';
10119
+ this.wasRestored = true;
10098
10120
  await this.authenticate();
10099
10121
  await this.subscribeToTopic();
10100
10122
  });
10123
+ await this.subscribeToTopic();
10101
10124
  return this;
10102
10125
  }
10103
10126
  mapOrUpdateMessages(messages) {
@@ -10115,9 +10138,16 @@ class ChatSource extends SocketRPC {
10115
10138
  return message;
10116
10139
  }
10117
10140
  async getExistingMessages() {
10118
- let messages = await this.peer.getExistingMessages();
10119
- messages = this.mapOrUpdateMessages(messages);
10120
- return messages;
10141
+ try {
10142
+ let messages = await this.peer.getExistingMessages();
10143
+ messages = this.mapOrUpdateMessages(messages);
10144
+ return messages;
10145
+ }
10146
+ catch (e) {
10147
+ console.error(`[Banta/${this.identifier}] Error occurred while trying to get existing messages:`);
10148
+ console.error(e);
10149
+ return [];
10150
+ }
10121
10151
  }
10122
10152
  async ensureConnection(errorMessage) {
10123
10153
  let reason = `Connection to chat services is not currently available.`;
@@ -10132,8 +10162,30 @@ class ChatSource extends SocketRPC {
10132
10162
  await this.ensureConnection();
10133
10163
  await this.peer.editMessage(messageId, text);
10134
10164
  }
10165
+ get errorState() {
10166
+ return this._errorState;
10167
+ }
10135
10168
  async subscribeToTopic() {
10136
- await this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder);
10169
+ try {
10170
+ await this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder);
10171
+ this.subscribeAttempt = 0;
10172
+ this._errorState = undefined;
10173
+ this.state = this.wasRestored ? 'restored' : 'connected';
10174
+ this.markReady();
10175
+ }
10176
+ catch (e) {
10177
+ console.error(`[Banta/${this.identifier}] Error while subscribing to topic`);
10178
+ console.error(e);
10179
+ this.state = 'lost';
10180
+ this._errorState = 'server-issue';
10181
+ this.subscribeAttempt += 1;
10182
+ let delay = Math.min(30 * 1000, (3 * 1000 * this.subscribeAttempt) * (1 + Math.random()));
10183
+ console.error(`[Banta/${this.identifier}] Waiting ${delay}ms before attempting to reconnect...`);
10184
+ setTimeout(() => {
10185
+ console.info(`Attempting reconnection after error in subscribeToTopic...`);
10186
+ this.reconnect();
10187
+ }, delay);
10188
+ }
10137
10189
  }
10138
10190
  async authenticate() {
10139
10191
  if (this.backend.user) {
@@ -10251,12 +10303,24 @@ class ChatBackend extends ChatBackendBase {
10251
10303
  return await new ChatSource(this, topicId, messageId, options?.sortOrder || CommentsOrder.NEWEST)
10252
10304
  .bind(await this.connectToService());
10253
10305
  }
10306
+ /**
10307
+ * Get the count of the given topic
10308
+ * @param topicId
10309
+ * @returns
10310
+ */
10254
10311
  async getSourceCountForTopic(topicId) {
10255
- let response = await fetch(`${this.serviceUrl}/topics/${topicId}`);
10256
- if (response.status >= 400)
10257
- return 0;
10258
- let topic = await response.json();
10259
- return topic.messageCount || 0;
10312
+ try {
10313
+ let response = await fetch(`${this.serviceUrl}/topics/${topicId}`);
10314
+ if (response.status >= 400)
10315
+ return 0;
10316
+ let topic = await response.json();
10317
+ return topic.messageCount || 0;
10318
+ }
10319
+ catch (e) {
10320
+ console.error(`[Banta/${topicId}] Failed to get message count for topic:`);
10321
+ console.error(e);
10322
+ return undefined;
10323
+ }
10260
10324
  }
10261
10325
  refreshMessage(message) {
10262
10326
  throw new Error("Method not implemented.");
@@ -10426,9 +10490,7 @@ BantaSdkModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version:
10426
10490
  BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, declarations: [BantaComponent,
10427
10491
  BantaLogoComponent,
10428
10492
  LiveMessageComponent], imports: [CommonModule,
10429
- FormsModule,
10430
- BantaCommonModule,
10431
- CommentsModule,
10493
+ FormsModule, BantaCommonModule, CommentsModule,
10432
10494
  ChatModule,
10433
10495
  EmojiModule,
10434
10496
  MatIconModule,
@@ -10450,7 +10512,7 @@ BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
10450
10512
  EmojiModule] });
10451
10513
  BantaSdkModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, imports: [CommonModule,
10452
10514
  FormsModule,
10453
- BantaCommonModule,
10515
+ BantaCommonModule.forRoot(),
10454
10516
  CommentsModule,
10455
10517
  ChatModule,
10456
10518
  EmojiModule,
@@ -10474,7 +10536,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
10474
10536
  imports: [
10475
10537
  CommonModule,
10476
10538
  FormsModule,
10477
- BantaCommonModule,
10539
+ BantaCommonModule.forRoot(),
10478
10540
  CommentsModule,
10479
10541
  ChatModule,
10480
10542
  EmojiModule,