@banta/sdk 4.7.10 → 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) {
@@ -512,6 +523,14 @@ const COMPONENTS$3 = [
512
523
  BantaAttachmentsComponent
513
524
  ];
514
525
  class BantaCommonModule {
526
+ static forRoot() {
527
+ return {
528
+ ngModule: BantaCommonModule,
529
+ providers: [
530
+ TimerPool
531
+ ]
532
+ };
533
+ }
515
534
  }
516
535
  BantaCommonModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
517
536
  BantaCommonModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, declarations: [TimestampComponent,
@@ -7128,12 +7147,12 @@ class EmojiSelectorPanelComponent {
7128
7147
  this.categories = this.pairs(cats).map(pair => pair[1]);
7129
7148
  }
7130
7149
  }
7131
- 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 });
7132
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"] }] });
7133
7152
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorPanelComponent, decorators: [{
7134
7153
  type: Component,
7135
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"] }]
7136
- }], ctorParameters: function () { return [{ type: i1.DomSanitizer }, { type: undefined, decorators: [{
7155
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }, { type: undefined, decorators: [{
7137
7156
  type: Inject,
7138
7157
  args: [BANTA_SDK_OPTIONS]
7139
7158
  }, {
@@ -7207,7 +7226,7 @@ class EmojiSelectorButtonComponent {
7207
7226
  this.overlayRef.attach(this.selectorPanelTemplate);
7208
7227
  }
7209
7228
  }
7210
- 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 });
7211
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: `
7212
7231
  <button #button type="button" mat-icon-button (click)="show()">
7213
7232
  <mat-icon>emoji_emotions</mat-icon>
@@ -7232,7 +7251,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
7232
7251
  ></emoji-selector-panel>
7233
7252
  </ng-template>
7234
7253
  `, styles: [":host{display:block;position:relative}button{color:#666}\n"] }]
7235
- }], 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: [{
7236
7255
  type: ViewChild,
7237
7256
  args: ['selectorPanelTemplate']
7238
7257
  }], selected: [{
@@ -7947,12 +7966,12 @@ class AttachmentButtonComponent {
7947
7966
  }
7948
7967
  }
7949
7968
  }
7950
- 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 });
7951
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"] }] });
7952
7971
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AttachmentButtonComponent, decorators: [{
7953
7972
  type: Component,
7954
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"] }]
7955
- }], ctorParameters: function () { return [{ type: i1$2.CDNProvider }]; }, propDecorators: { fileInput: [{
7974
+ }], ctorParameters: function () { return [{ type: i1$3.CDNProvider }]; }, propDecorators: { fileInput: [{
7956
7975
  type: ViewChild,
7957
7976
  args: ['fileUpload', { static: false }]
7958
7977
  }], addedAttachment: [{
@@ -8105,7 +8124,10 @@ class CommentFieldComponent {
8105
8124
  if (this._source.connectionStateChanged) {
8106
8125
  this._subs.add(this._source.connectionStateChanged.subscribe(state => {
8107
8126
  if (state === 'lost') {
8108
- this.transientMessage = `Reconnecting...`;
8127
+ if (this._source.errorState === 'server-issue')
8128
+ this.transientMessage = `Error occurred, trying again...`;
8129
+ else
8130
+ this.transientMessage = `Reconnecting...`;
8109
8131
  }
8110
8132
  else if (state === 'restored') {
8111
8133
  this.transientMessage = undefined;
@@ -8696,7 +8718,7 @@ class BantaCommentsComponent {
8696
8718
  return this.threadView;
8697
8719
  }
8698
8720
  updateLoading() {
8699
- if (this.source?.state && this.source?.state !== 'connecting') {
8721
+ if (this.source?.state && !['connecting', 'lost'].includes(this.source?.state)) {
8700
8722
  clearInterval(this._loadingTimer);
8701
8723
  this.loadingMessage = `Here we go!`;
8702
8724
  setTimeout(() => {
@@ -10064,6 +10086,8 @@ class ChatSource extends SocketRPC {
10064
10086
  this.subscription = new Subscription();
10065
10087
  this._state = 'connecting';
10066
10088
  this._connectionStateChanged = new Subject();
10089
+ this.wasRestored = false;
10090
+ this.subscribeAttempt = 0;
10067
10091
  this.messageMap = new Map();
10068
10092
  this._messageReceived = new Subject();
10069
10093
  this._messageUpdated = new Subject();
@@ -10084,21 +10108,19 @@ class ChatSource extends SocketRPC {
10084
10108
  }
10085
10109
  async bind(socket) {
10086
10110
  super.bind(socket);
10087
- this.state = 'connected';
10088
- this.markReady();
10089
- await this.subscribeToTopic();
10090
10111
  this.subscription.add(this.backend.userChanged.subscribe(() => this.authenticate()));
10091
10112
  socket.addEventListener('open', async () => {
10092
- this.state = 'connected';
10113
+ console.log(`[Banta/${this.identifier}] Socket is open`);
10093
10114
  });
10094
10115
  socket.addEventListener('lost', async () => {
10095
10116
  this.state = 'lost';
10096
10117
  });
10097
10118
  socket.addEventListener('restore', async () => {
10098
- this.state = 'restored';
10119
+ this.wasRestored = true;
10099
10120
  await this.authenticate();
10100
10121
  await this.subscribeToTopic();
10101
10122
  });
10123
+ await this.subscribeToTopic();
10102
10124
  return this;
10103
10125
  }
10104
10126
  mapOrUpdateMessages(messages) {
@@ -10116,9 +10138,16 @@ class ChatSource extends SocketRPC {
10116
10138
  return message;
10117
10139
  }
10118
10140
  async getExistingMessages() {
10119
- let messages = await this.peer.getExistingMessages();
10120
- messages = this.mapOrUpdateMessages(messages);
10121
- 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
+ }
10122
10151
  }
10123
10152
  async ensureConnection(errorMessage) {
10124
10153
  let reason = `Connection to chat services is not currently available.`;
@@ -10133,8 +10162,30 @@ class ChatSource extends SocketRPC {
10133
10162
  await this.ensureConnection();
10134
10163
  await this.peer.editMessage(messageId, text);
10135
10164
  }
10165
+ get errorState() {
10166
+ return this._errorState;
10167
+ }
10136
10168
  async subscribeToTopic() {
10137
- 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
+ }
10138
10189
  }
10139
10190
  async authenticate() {
10140
10191
  if (this.backend.user) {
@@ -10252,12 +10303,24 @@ class ChatBackend extends ChatBackendBase {
10252
10303
  return await new ChatSource(this, topicId, messageId, options?.sortOrder || CommentsOrder.NEWEST)
10253
10304
  .bind(await this.connectToService());
10254
10305
  }
10306
+ /**
10307
+ * Get the count of the given topic
10308
+ * @param topicId
10309
+ * @returns
10310
+ */
10255
10311
  async getSourceCountForTopic(topicId) {
10256
- let response = await fetch(`${this.serviceUrl}/topics/${topicId}`);
10257
- if (response.status >= 400)
10258
- return 0;
10259
- let topic = await response.json();
10260
- 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
+ }
10261
10324
  }
10262
10325
  refreshMessage(message) {
10263
10326
  throw new Error("Method not implemented.");
@@ -10427,9 +10490,7 @@ BantaSdkModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version:
10427
10490
  BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, declarations: [BantaComponent,
10428
10491
  BantaLogoComponent,
10429
10492
  LiveMessageComponent], imports: [CommonModule,
10430
- FormsModule,
10431
- BantaCommonModule,
10432
- CommentsModule,
10493
+ FormsModule, BantaCommonModule, CommentsModule,
10433
10494
  ChatModule,
10434
10495
  EmojiModule,
10435
10496
  MatIconModule,
@@ -10451,7 +10512,7 @@ BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
10451
10512
  EmojiModule] });
10452
10513
  BantaSdkModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, imports: [CommonModule,
10453
10514
  FormsModule,
10454
- BantaCommonModule,
10515
+ BantaCommonModule.forRoot(),
10455
10516
  CommentsModule,
10456
10517
  ChatModule,
10457
10518
  EmojiModule,
@@ -10475,7 +10536,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
10475
10536
  imports: [
10476
10537
  CommonModule,
10477
10538
  FormsModule,
10478
- BantaCommonModule,
10539
+ BantaCommonModule.forRoot(),
10479
10540
  CommentsModule,
10480
10541
  ChatModule,
10481
10542
  EmojiModule,