@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';
@@ -32,7 +34,7 @@ import * as i7$1 from '@angular/material/menu';
32
34
  import { MatMenuModule } from '@angular/material/menu';
33
35
  import * as i11 from '@angular/material/tooltip';
34
36
  import { MatTooltipModule } from '@angular/material/tooltip';
35
- import * as i1$2 from '@banta/common';
37
+ import * as i1$3 from '@banta/common';
36
38
  import { CommentsOrder, SocketRPC, RpcEvent, DurableSocket } from '@banta/common';
37
39
  import * as i2$3 from '@angular/router';
38
40
  import * as i3$3 from '@angular/material/snack-bar';
@@ -57,20 +59,26 @@ 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
+ var _a;
72
+ this._destroyed = true;
73
+ (_a = this.timerUnsubscribe) === null || _a === void 0 ? void 0 : _a.call(this);
69
74
  }
70
75
  get value() {
71
76
  return this._value;
72
77
  }
73
78
  update() {
79
+ var _a;
80
+ if (this._destroyed)
81
+ return;
74
82
  let now = Date.now();
75
83
  let diff = now - this.value;
76
84
  let minute = 1000 * 60;
@@ -132,18 +140,23 @@ class TimestampComponent {
132
140
  updateTime = 1000 * 30;
133
141
  }
134
142
  if (typeof window !== 'undefined') {
135
- clearInterval(this.updateInterval);
136
- if (updateTime > 0) {
137
- this.updateInterval = setInterval(() => this.update());
143
+ if (this.timerInterval !== updateTime) {
144
+ this.timerInterval = updateTime;
145
+ (_a = this.timerUnsubscribe) === null || _a === void 0 ? void 0 : _a.call(this);
146
+ if (updateTime > 0) {
147
+ this.timerUnsubscribe = this.timerPool.addTimer(updateTime, () => this.update());
148
+ }
138
149
  }
139
150
  }
140
151
  }
141
152
  set value(v) {
142
- this._value = v;
143
- this.update();
153
+ if (this._value !== v) {
154
+ this._value = v;
155
+ this.update();
156
+ }
144
157
  }
145
158
  }
146
- TimestampComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: TimestampComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
159
+ 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
160
  TimestampComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: TimestampComponent, selector: "banta-timestamp", inputs: { value: "value" }, ngImport: i0, template: `
148
161
  <span *ngIf="showAbsolute" [title]="value | date : 'short'">
149
162
  {{value | date : 'shortDate'}}
@@ -162,7 +175,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
162
175
  {{relative}}
163
176
  </span>
164
177
  ` }]
165
- }], propDecorators: { value: [{
178
+ }], ctorParameters: function () { return [{ type: i1.TimerPool }]; }, propDecorators: { value: [{
166
179
  type: Input
167
180
  }] } });
168
181
 
@@ -265,7 +278,7 @@ class BantaMarkdownToHtmlPipe {
265
278
  }));
266
279
  }
267
280
  }
268
- 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 });
281
+ 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 });
269
282
  BantaMarkdownToHtmlPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaMarkdownToHtmlPipe, name: "markdownToHtml" });
270
283
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaMarkdownToHtmlPipe, decorators: [{
271
284
  type: Pipe,
@@ -273,7 +286,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
273
286
  name: 'markdownToHtml'
274
287
  }]
275
288
  }], ctorParameters: function () {
276
- return [{ type: i1.DomSanitizer }, { type: undefined, decorators: [{
289
+ return [{ type: i1$1.DomSanitizer }, { type: undefined, decorators: [{
277
290
  type: Inject,
278
291
  args: [BANTA_SDK_OPTIONS]
279
292
  }, {
@@ -291,14 +304,14 @@ class BantaTrustResourceUrlPipe {
291
304
  return this.sanitizer.bypassSecurityTrustResourceUrl(value);
292
305
  }
293
306
  }
294
- BantaTrustResourceUrlPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaTrustResourceUrlPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
307
+ 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 });
295
308
  BantaTrustResourceUrlPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaTrustResourceUrlPipe, name: "trustResourceUrl" });
296
309
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaTrustResourceUrlPipe, decorators: [{
297
310
  type: Pipe,
298
311
  args: [{
299
312
  name: 'trustResourceUrl'
300
313
  }]
301
- }], ctorParameters: function () { return [{ type: i1.DomSanitizer }]; } });
314
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }]; } });
302
315
 
303
316
  class BantaMentionLinkerPipe {
304
317
  transform(value, links) {
@@ -393,15 +406,15 @@ class BantaAttachmentComponent {
393
406
  || !this.attachment.url);
394
407
  }
395
408
  get isImageAttachment() {
396
- if (this.attachment.type.startsWith('image/'))
397
- return true;
398
- return false;
409
+ var _a, _b;
410
+ return (_b = (_a = this.attachment) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.startsWith('image/');
399
411
  }
400
412
  get hasFrame() {
413
+ var _a;
401
414
  if (!this.attachment)
402
415
  return false;
403
416
  return this.attachment.type === 'iframe' || (this.attachment.type === 'card'
404
- && this.attachment.card.player);
417
+ && ((_a = this.attachment.card) === null || _a === void 0 ? void 0 : _a.player));
405
418
  }
406
419
  get frameUrl() {
407
420
  if (!this.attachment)
@@ -480,11 +493,14 @@ class BantaAttachmentsComponent {
480
493
  .filter(x => x.type === 'image/png')
481
494
  .map(x => x.url));
482
495
  }
496
+ get validAttachments() {
497
+ return this.attachments.filter(x => x.type);
498
+ }
483
499
  get inlineAttachments() {
484
- return this.attachments.filter(x => x.type !== 'card' && (x.style === 'inline' || !x.style));
500
+ return this.validAttachments.filter(x => x.type !== 'card' && (x.style === 'inline' || !x.style));
485
501
  }
486
502
  get blockAttachments() {
487
- return this.attachments.filter(x => x.style === 'block' || x.type === 'card');
503
+ return this.validAttachments.filter(x => x.style === 'block' || x.type === 'card');
488
504
  }
489
505
  attachmentId(index, attachment) {
490
506
  return attachment.url;
@@ -518,6 +534,14 @@ const COMPONENTS$3 = [
518
534
  BantaAttachmentsComponent
519
535
  ];
520
536
  class BantaCommonModule {
537
+ static forRoot() {
538
+ return {
539
+ ngModule: BantaCommonModule,
540
+ providers: [
541
+ TimerPool
542
+ ]
543
+ };
544
+ }
521
545
  }
522
546
  BantaCommonModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
523
547
  BantaCommonModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, declarations: [TimestampComponent,
@@ -7135,13 +7159,13 @@ class EmojiSelectorPanelComponent {
7135
7159
  this.categories = this.pairs(cats).map(pair => pair[1]);
7136
7160
  }
7137
7161
  }
7138
- 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 });
7162
+ 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 });
7139
7163
  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"] }] });
7140
7164
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorPanelComponent, decorators: [{
7141
7165
  type: Component,
7142
7166
  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"] }]
7143
7167
  }], ctorParameters: function () {
7144
- return [{ type: i1.DomSanitizer }, { type: undefined, decorators: [{
7168
+ return [{ type: i1$1.DomSanitizer }, { type: undefined, decorators: [{
7145
7169
  type: Inject,
7146
7170
  args: [BANTA_SDK_OPTIONS]
7147
7171
  }, {
@@ -7216,7 +7240,7 @@ class EmojiSelectorButtonComponent {
7216
7240
  this.overlayRef.attach(this.selectorPanelTemplate);
7217
7241
  }
7218
7242
  }
7219
- 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 });
7243
+ 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 });
7220
7244
  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: `
7221
7245
  <button #button type="button" mat-icon-button (click)="show()">
7222
7246
  <mat-icon>emoji_emotions</mat-icon>
@@ -7241,7 +7265,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
7241
7265
  ></emoji-selector-panel>
7242
7266
  </ng-template>
7243
7267
  `, styles: [":host{display:block;position:relative}button{color:#666}\n"] }]
7244
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$1.Overlay }]; }, propDecorators: { selectorPanelTemplate: [{
7268
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$2.Overlay }]; }, propDecorators: { selectorPanelTemplate: [{
7245
7269
  type: ViewChild,
7246
7270
  args: ['selectorPanelTemplate']
7247
7271
  }], selected: [{
@@ -7975,12 +7999,12 @@ class AttachmentButtonComponent {
7975
7999
  });
7976
8000
  }
7977
8001
  }
7978
- 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 });
8002
+ 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 });
7979
8003
  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"] }] });
7980
8004
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AttachmentButtonComponent, decorators: [{
7981
8005
  type: Component,
7982
8006
  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"] }]
7983
- }], ctorParameters: function () { return [{ type: i1$2.CDNProvider }]; }, propDecorators: { fileInput: [{
8007
+ }], ctorParameters: function () { return [{ type: i1$3.CDNProvider }]; }, propDecorators: { fileInput: [{
7984
8008
  type: ViewChild,
7985
8009
  args: ['fileUpload', { static: false }]
7986
8010
  }], addedAttachment: [{
@@ -8135,7 +8159,10 @@ class CommentFieldComponent {
8135
8159
  if (this._source.connectionStateChanged) {
8136
8160
  this._subs.add(this._source.connectionStateChanged.subscribe(state => {
8137
8161
  if (state === 'lost') {
8138
- this.transientMessage = `Reconnecting...`;
8162
+ if (this._source.errorState === 'server-issue')
8163
+ this.transientMessage = `Error occurred, trying again...`;
8164
+ else
8165
+ this.transientMessage = `Reconnecting...`;
8139
8166
  }
8140
8167
  else if (state === 'restored') {
8141
8168
  this.transientMessage = undefined;
@@ -8744,7 +8771,7 @@ class BantaCommentsComponent {
8744
8771
  }
8745
8772
  updateLoading() {
8746
8773
  var _a, _b;
8747
- if (((_a = this.source) === null || _a === void 0 ? void 0 : _a.state) && ((_b = this.source) === null || _b === void 0 ? void 0 : _b.state) !== 'connecting') {
8774
+ if (((_a = this.source) === null || _a === void 0 ? void 0 : _a.state) && !['connecting', 'lost'].includes((_b = this.source) === null || _b === void 0 ? void 0 : _b.state)) {
8748
8775
  clearInterval(this._loadingTimer);
8749
8776
  this.loadingMessage = `Here we go!`;
8750
8777
  setTimeout(() => {
@@ -10170,6 +10197,8 @@ class ChatSource extends SocketRPC {
10170
10197
  this.subscription = new Subscription();
10171
10198
  this._state = 'connecting';
10172
10199
  this._connectionStateChanged = new Subject();
10200
+ this.wasRestored = false;
10201
+ this.subscribeAttempt = 0;
10173
10202
  this.messageMap = new Map();
10174
10203
  this._messageReceived = new Subject();
10175
10204
  this._messageUpdated = new Subject();
@@ -10194,21 +10223,19 @@ class ChatSource extends SocketRPC {
10194
10223
  });
10195
10224
  return __awaiter(this, void 0, void 0, function* () {
10196
10225
  _super.bind.call(this, socket);
10197
- this.state = 'connected';
10198
- this.markReady();
10199
- yield this.subscribeToTopic();
10200
10226
  this.subscription.add(this.backend.userChanged.subscribe(() => this.authenticate()));
10201
10227
  socket.addEventListener('open', () => __awaiter(this, void 0, void 0, function* () {
10202
- this.state = 'connected';
10228
+ console.log(`[Banta/${this.identifier}] Socket is open`);
10203
10229
  }));
10204
10230
  socket.addEventListener('lost', () => __awaiter(this, void 0, void 0, function* () {
10205
10231
  this.state = 'lost';
10206
10232
  }));
10207
10233
  socket.addEventListener('restore', () => __awaiter(this, void 0, void 0, function* () {
10208
- this.state = 'restored';
10234
+ this.wasRestored = true;
10209
10235
  yield this.authenticate();
10210
10236
  yield this.subscribeToTopic();
10211
10237
  }));
10238
+ yield this.subscribeToTopic();
10212
10239
  return this;
10213
10240
  });
10214
10241
  }
@@ -10228,9 +10255,16 @@ class ChatSource extends SocketRPC {
10228
10255
  }
10229
10256
  getExistingMessages() {
10230
10257
  return __awaiter(this, void 0, void 0, function* () {
10231
- let messages = yield this.peer.getExistingMessages();
10232
- messages = this.mapOrUpdateMessages(messages);
10233
- return messages;
10258
+ try {
10259
+ let messages = yield this.peer.getExistingMessages();
10260
+ messages = this.mapOrUpdateMessages(messages);
10261
+ return messages;
10262
+ }
10263
+ catch (e) {
10264
+ console.error(`[Banta/${this.identifier}] Error occurred while trying to get existing messages:`);
10265
+ console.error(e);
10266
+ return [];
10267
+ }
10234
10268
  });
10235
10269
  }
10236
10270
  ensureConnection(errorMessage) {
@@ -10250,9 +10284,31 @@ class ChatSource extends SocketRPC {
10250
10284
  yield this.peer.editMessage(messageId, text);
10251
10285
  });
10252
10286
  }
10287
+ get errorState() {
10288
+ return this._errorState;
10289
+ }
10253
10290
  subscribeToTopic() {
10254
10291
  return __awaiter(this, void 0, void 0, function* () {
10255
- yield this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder);
10292
+ try {
10293
+ yield this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder);
10294
+ this.subscribeAttempt = 0;
10295
+ this._errorState = undefined;
10296
+ this.state = this.wasRestored ? 'restored' : 'connected';
10297
+ this.markReady();
10298
+ }
10299
+ catch (e) {
10300
+ console.error(`[Banta/${this.identifier}] Error while subscribing to topic`);
10301
+ console.error(e);
10302
+ this.state = 'lost';
10303
+ this._errorState = 'server-issue';
10304
+ this.subscribeAttempt += 1;
10305
+ let delay = Math.min(30 * 1000, (3 * 1000 * this.subscribeAttempt) * (1 + Math.random()));
10306
+ console.error(`[Banta/${this.identifier}] Waiting ${delay}ms before attempting to reconnect...`);
10307
+ setTimeout(() => {
10308
+ console.info(`Attempting reconnection after error in subscribeToTopic...`);
10309
+ this.reconnect();
10310
+ }, delay);
10311
+ }
10256
10312
  });
10257
10313
  }
10258
10314
  authenticate() {
@@ -10395,13 +10451,25 @@ class ChatBackend extends ChatBackendBase {
10395
10451
  .bind(yield this.connectToService());
10396
10452
  });
10397
10453
  }
10454
+ /**
10455
+ * Get the count of the given topic
10456
+ * @param topicId
10457
+ * @returns
10458
+ */
10398
10459
  getSourceCountForTopic(topicId) {
10399
10460
  return __awaiter(this, void 0, void 0, function* () {
10400
- let response = yield fetch(`${this.serviceUrl}/topics/${topicId}`);
10401
- if (response.status >= 400)
10402
- return 0;
10403
- let topic = yield response.json();
10404
- return topic.messageCount || 0;
10461
+ try {
10462
+ let response = yield fetch(`${this.serviceUrl}/topics/${topicId}`);
10463
+ if (response.status >= 400)
10464
+ return 0;
10465
+ let topic = yield response.json();
10466
+ return topic.messageCount || 0;
10467
+ }
10468
+ catch (e) {
10469
+ console.error(`[Banta/${topicId}] Failed to get message count for topic:`);
10470
+ console.error(e);
10471
+ return undefined;
10472
+ }
10405
10473
  });
10406
10474
  }
10407
10475
  refreshMessage(message) {
@@ -10586,9 +10654,7 @@ BantaSdkModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version:
10586
10654
  BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, declarations: [BantaComponent,
10587
10655
  BantaLogoComponent,
10588
10656
  LiveMessageComponent], imports: [CommonModule,
10589
- FormsModule,
10590
- BantaCommonModule,
10591
- CommentsModule,
10657
+ FormsModule, BantaCommonModule, CommentsModule,
10592
10658
  ChatModule,
10593
10659
  EmojiModule,
10594
10660
  MatIconModule,
@@ -10610,7 +10676,7 @@ BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
10610
10676
  EmojiModule] });
10611
10677
  BantaSdkModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, imports: [CommonModule,
10612
10678
  FormsModule,
10613
- BantaCommonModule,
10679
+ BantaCommonModule.forRoot(),
10614
10680
  CommentsModule,
10615
10681
  ChatModule,
10616
10682
  EmojiModule,
@@ -10634,7 +10700,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
10634
10700
  imports: [
10635
10701
  CommonModule,
10636
10702
  FormsModule,
10637
- BantaCommonModule,
10703
+ BantaCommonModule.forRoot(),
10638
10704
  CommentsModule,
10639
10705
  ChatModule,
10640
10706
  EmojiModule,