@banta/sdk 4.7.10 → 4.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  import { Observable, Subject, BehaviorSubject, Subscription } from 'rxjs';
2
2
  import { publish, take } from 'rxjs/operators';
3
3
  import * as i0 from '@angular/core';
4
- import { Component, Input, ViewChild, Pipe, Inject, Optional, Output, HostBinding, NgModule, ViewChildren, Directive, TemplateRef, ContentChild, Injectable } from '@angular/core';
4
+ import { Component, Input, ViewChild, Pipe, Inject, Optional, Output, HostBinding, NgModule, ViewChildren, Directive, TemplateRef, ContentChild, Injectable as Injectable$1 } from '@angular/core';
5
+ import * as i1 from 'projects/sdk/src/lib/common/timer-pool.service';
6
+ import { TimerPool as TimerPool$1 } 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,31 +11,31 @@ 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';
16
18
  import * as i6 from '@angular/material/button';
17
19
  import { MatButtonModule } from '@angular/material/button';
20
+ import { __decorate, __awaiter } from 'tslib';
21
+ import * as i1$3 from '@banta/common';
22
+ import { Injectable, CommentsOrder, SocketRPC, RpcEvent, DurableSocket } from '@banta/common';
18
23
  import * as i4$1 from '@angular/forms';
19
24
  import { FormsModule } from '@angular/forms';
20
25
  import * as i6$1 from '@angular/material/form-field';
21
26
  import { MatFormFieldModule } from '@angular/material/form-field';
22
27
  import * as i7 from '@angular/material/input';
23
28
  import { MatInputModule } from '@angular/material/input';
24
- import * as i1$1 from '@angular/cdk/overlay';
29
+ import * as i1$2 from '@angular/cdk/overlay';
25
30
  import { OverlayModule } from '@angular/cdk/overlay';
26
31
  import * as i4$2 from '@angular/cdk/portal';
27
32
  import { PortalModule } from '@angular/cdk/portal';
28
- import { __awaiter, __decorate } from 'tslib';
29
33
  import * as i2$4 from '@angular/material/dialog';
30
34
  import { MatDialogModule } from '@angular/material/dialog';
31
35
  import * as i7$1 from '@angular/material/menu';
32
36
  import { MatMenuModule } from '@angular/material/menu';
33
37
  import * as i11 from '@angular/material/tooltip';
34
38
  import { MatTooltipModule } from '@angular/material/tooltip';
35
- import * as i1$2 from '@banta/common';
36
- 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';
39
41
  import { MatSnackBarModule } 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) {
@@ -511,6 +524,89 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
511
524
  type: Output
512
525
  }] } });
513
526
 
527
+ /**
528
+ * Provides a way to hook in to a shared set of timers, instead of creating a timer per instance.
529
+ * This is very useful for cases where the update is not extremely time-sensitive, but happens at scale.
530
+ * The principal use case is the TimestampComponent. When several hundred (or several thousand) comments are
531
+ * being displayed, we do not want to trigger thousands of independent relative timestamp updates, because over
532
+ * time the updates will saturate the CPU since they don't perfectly align.
533
+ */
534
+ let TimerPool = class TimerPool {
535
+ constructor() {
536
+ this.subscriptions = new Map();
537
+ this.newSubscriptions = new Map();
538
+ this.removedSubscriptions = new Map();
539
+ }
540
+ addTimer(interval, callback) {
541
+ var _a;
542
+ if (interval <= 0) {
543
+ console.warn(`Refusing to set timer with interval of ${interval}!`);
544
+ return () => { };
545
+ }
546
+ let state;
547
+ let sizeWas = this.subscriptions.size;
548
+ if (!this.subscriptions.has(interval)) {
549
+ state = { subscribers: [] };
550
+ state.handle = setInterval(() => {
551
+ console.debug(`[Banta/TimerPool] Notifying ${state.subscribers.length} subs [${interval}ms]`);
552
+ state.subscribers.forEach(sub => sub());
553
+ }, interval);
554
+ this.subscriptions.set(interval, state);
555
+ }
556
+ else {
557
+ state = this.subscriptions.get(interval);
558
+ }
559
+ state.subscribers.push(callback);
560
+ // Debug information //////////////////////////
561
+ //
562
+ if (!this.newSubscriptions.has(interval))
563
+ this.newSubscriptions.set(interval, 0);
564
+ this.newSubscriptions.set(interval, ((_a = this.newSubscriptions.get(interval)) !== null && _a !== void 0 ? _a : 0) + 1);
565
+ clearTimeout(this.newSubscriptionsNotice);
566
+ this.newSubscriptionsNotice = setTimeout(() => {
567
+ for (let [interval, count] of this.newSubscriptions) {
568
+ console.debug(`[Banta/TimerPool] ${count} new subscriptions to ${interval}ms [${state.subscribers.length} total]`);
569
+ }
570
+ this.newSubscriptions.clear();
571
+ });
572
+ //
573
+ ///////////////////////////////////////////////
574
+ if (sizeWas === 0) {
575
+ console.debug(`[Banta/TimerPool] No longer idle.`);
576
+ }
577
+ // Unsubscribe function
578
+ return () => {
579
+ var _a;
580
+ let state = this.subscriptions.get(interval);
581
+ let index = state.subscribers.indexOf(callback);
582
+ if (index >= 0)
583
+ state.subscribers.splice(index, 1);
584
+ if (state.subscribers.length === 0) {
585
+ clearInterval(state.handle);
586
+ this.subscriptions.delete(interval);
587
+ }
588
+ if (!this.removedSubscriptions.has(interval))
589
+ this.removedSubscriptions.set(interval, 0);
590
+ this.removedSubscriptions.set(interval, ((_a = this.removedSubscriptions.get(interval)) !== null && _a !== void 0 ? _a : 0) + 1);
591
+ // Debug information ////////////////////////////////////////////////////////////////////
592
+ clearTimeout(this.removedSubscriptionsNotice);
593
+ this.removedSubscriptionsNotice = setTimeout(() => {
594
+ var _a, _b;
595
+ for (let [interval, count] of this.removedSubscriptions) {
596
+ let state = this.subscriptions.get(interval);
597
+ console.debug(`[Banta/TimerPool] ${count} unsubscribed from ${interval}ms [${(_b = (_a = state === null || state === void 0 ? void 0 : state.subscribers) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0} remain]`);
598
+ }
599
+ if (this.subscriptions.size === 0)
600
+ console.debug(`[Banta/TimerPool] All subscriptions have been removed. Now idle.`);
601
+ this.removedSubscriptions.clear();
602
+ });
603
+ };
604
+ }
605
+ };
606
+ TimerPool = __decorate([
607
+ Injectable()
608
+ ], TimerPool);
609
+
514
610
  const COMPONENTS$3 = [
515
611
  TimestampComponent,
516
612
  LightboxComponent,
@@ -521,6 +617,14 @@ const COMPONENTS$3 = [
521
617
  BantaAttachmentsComponent
522
618
  ];
523
619
  class BantaCommonModule {
620
+ static forRoot() {
621
+ return {
622
+ ngModule: BantaCommonModule,
623
+ providers: [
624
+ TimerPool$1
625
+ ]
626
+ };
627
+ }
524
628
  }
525
629
  BantaCommonModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
526
630
  BantaCommonModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaCommonModule, declarations: [TimestampComponent,
@@ -7138,13 +7242,13 @@ class EmojiSelectorPanelComponent {
7138
7242
  this.categories = this.pairs(cats).map(pair => pair[1]);
7139
7243
  }
7140
7244
  }
7141
- 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 });
7245
+ 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 });
7142
7246
  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"] }] });
7143
7247
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: EmojiSelectorPanelComponent, decorators: [{
7144
7248
  type: Component,
7145
7249
  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"] }]
7146
7250
  }], ctorParameters: function () {
7147
- return [{ type: i1.DomSanitizer }, { type: undefined, decorators: [{
7251
+ return [{ type: i1$1.DomSanitizer }, { type: undefined, decorators: [{
7148
7252
  type: Inject,
7149
7253
  args: [BANTA_SDK_OPTIONS]
7150
7254
  }, {
@@ -7219,7 +7323,7 @@ class EmojiSelectorButtonComponent {
7219
7323
  this.overlayRef.attach(this.selectorPanelTemplate);
7220
7324
  }
7221
7325
  }
7222
- 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 });
7326
+ 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 });
7223
7327
  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: `
7224
7328
  <button #button type="button" mat-icon-button (click)="show()">
7225
7329
  <mat-icon>emoji_emotions</mat-icon>
@@ -7244,7 +7348,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
7244
7348
  ></emoji-selector-panel>
7245
7349
  </ng-template>
7246
7350
  `, styles: [":host{display:block;position:relative}button{color:#666}\n"] }]
7247
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$1.Overlay }]; }, propDecorators: { selectorPanelTemplate: [{
7351
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1$2.Overlay }]; }, propDecorators: { selectorPanelTemplate: [{
7248
7352
  type: ViewChild,
7249
7353
  args: ['selectorPanelTemplate']
7250
7354
  }], selected: [{
@@ -7978,12 +8082,12 @@ class AttachmentButtonComponent {
7978
8082
  });
7979
8083
  }
7980
8084
  }
7981
- 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 });
8085
+ 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 });
7982
8086
  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"] }] });
7983
8087
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: AttachmentButtonComponent, decorators: [{
7984
8088
  type: Component,
7985
8089
  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"] }]
7986
- }], ctorParameters: function () { return [{ type: i1$2.CDNProvider }]; }, propDecorators: { fileInput: [{
8090
+ }], ctorParameters: function () { return [{ type: i1$3.CDNProvider }]; }, propDecorators: { fileInput: [{
7987
8091
  type: ViewChild,
7988
8092
  args: ['fileUpload', { static: false }]
7989
8093
  }], addedAttachment: [{
@@ -8138,7 +8242,10 @@ class CommentFieldComponent {
8138
8242
  if (this._source.connectionStateChanged) {
8139
8243
  this._subs.add(this._source.connectionStateChanged.subscribe(state => {
8140
8244
  if (state === 'lost') {
8141
- this.transientMessage = `Reconnecting...`;
8245
+ if (this._source.errorState === 'server-issue')
8246
+ this.transientMessage = `Error occurred, trying again...`;
8247
+ else
8248
+ this.transientMessage = `Reconnecting...`;
8142
8249
  }
8143
8250
  else if (state === 'restored') {
8144
8251
  this.transientMessage = undefined;
@@ -8747,7 +8854,7 @@ class BantaCommentsComponent {
8747
8854
  }
8748
8855
  updateLoading() {
8749
8856
  var _a, _b;
8750
- if (((_a = this.source) === null || _a === void 0 ? void 0 : _a.state) && ((_b = this.source) === null || _b === void 0 ? void 0 : _b.state) !== 'connecting') {
8857
+ 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)) {
8751
8858
  clearInterval(this._loadingTimer);
8752
8859
  this.loadingMessage = `Here we go!`;
8753
8860
  setTimeout(() => {
@@ -10173,6 +10280,8 @@ class ChatSource extends SocketRPC {
10173
10280
  this.subscription = new Subscription();
10174
10281
  this._state = 'connecting';
10175
10282
  this._connectionStateChanged = new Subject();
10283
+ this.wasRestored = false;
10284
+ this.subscribeAttempt = 0;
10176
10285
  this.messageMap = new Map();
10177
10286
  this._messageReceived = new Subject();
10178
10287
  this._messageUpdated = new Subject();
@@ -10197,21 +10306,19 @@ class ChatSource extends SocketRPC {
10197
10306
  });
10198
10307
  return __awaiter(this, void 0, void 0, function* () {
10199
10308
  _super.bind.call(this, socket);
10200
- this.state = 'connected';
10201
- this.markReady();
10202
- yield this.subscribeToTopic();
10203
10309
  this.subscription.add(this.backend.userChanged.subscribe(() => this.authenticate()));
10204
10310
  socket.addEventListener('open', () => __awaiter(this, void 0, void 0, function* () {
10205
- this.state = 'connected';
10311
+ console.log(`[Banta/${this.identifier}] Socket is open`);
10206
10312
  }));
10207
10313
  socket.addEventListener('lost', () => __awaiter(this, void 0, void 0, function* () {
10208
10314
  this.state = 'lost';
10209
10315
  }));
10210
10316
  socket.addEventListener('restore', () => __awaiter(this, void 0, void 0, function* () {
10211
- this.state = 'restored';
10317
+ this.wasRestored = true;
10212
10318
  yield this.authenticate();
10213
10319
  yield this.subscribeToTopic();
10214
10320
  }));
10321
+ yield this.subscribeToTopic();
10215
10322
  return this;
10216
10323
  });
10217
10324
  }
@@ -10231,9 +10338,16 @@ class ChatSource extends SocketRPC {
10231
10338
  }
10232
10339
  getExistingMessages() {
10233
10340
  return __awaiter(this, void 0, void 0, function* () {
10234
- let messages = yield this.peer.getExistingMessages();
10235
- messages = this.mapOrUpdateMessages(messages);
10236
- return messages;
10341
+ try {
10342
+ let messages = yield this.peer.getExistingMessages();
10343
+ messages = this.mapOrUpdateMessages(messages);
10344
+ return messages;
10345
+ }
10346
+ catch (e) {
10347
+ console.error(`[Banta/${this.identifier}] Error occurred while trying to get existing messages:`);
10348
+ console.error(e);
10349
+ return [];
10350
+ }
10237
10351
  });
10238
10352
  }
10239
10353
  ensureConnection(errorMessage) {
@@ -10253,9 +10367,31 @@ class ChatSource extends SocketRPC {
10253
10367
  yield this.peer.editMessage(messageId, text);
10254
10368
  });
10255
10369
  }
10370
+ get errorState() {
10371
+ return this._errorState;
10372
+ }
10256
10373
  subscribeToTopic() {
10257
10374
  return __awaiter(this, void 0, void 0, function* () {
10258
- yield this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder);
10375
+ try {
10376
+ yield this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder);
10377
+ this.subscribeAttempt = 0;
10378
+ this._errorState = undefined;
10379
+ this.state = this.wasRestored ? 'restored' : 'connected';
10380
+ this.markReady();
10381
+ }
10382
+ catch (e) {
10383
+ console.error(`[Banta/${this.identifier}] Error while subscribing to topic`);
10384
+ console.error(e);
10385
+ this.state = 'lost';
10386
+ this._errorState = 'server-issue';
10387
+ this.subscribeAttempt += 1;
10388
+ let delay = Math.min(30 * 1000, (3 * 1000 * this.subscribeAttempt) * (1 + Math.random()));
10389
+ console.error(`[Banta/${this.identifier}] Waiting ${delay}ms before attempting to reconnect...`);
10390
+ setTimeout(() => {
10391
+ console.info(`Attempting reconnection after error in subscribeToTopic...`);
10392
+ this.reconnect();
10393
+ }, delay);
10394
+ }
10259
10395
  });
10260
10396
  }
10261
10397
  authenticate() {
@@ -10398,13 +10534,25 @@ class ChatBackend extends ChatBackendBase {
10398
10534
  .bind(yield this.connectToService());
10399
10535
  });
10400
10536
  }
10537
+ /**
10538
+ * Get the count of the given topic
10539
+ * @param topicId
10540
+ * @returns
10541
+ */
10401
10542
  getSourceCountForTopic(topicId) {
10402
10543
  return __awaiter(this, void 0, void 0, function* () {
10403
- let response = yield fetch(`${this.serviceUrl}/topics/${topicId}`);
10404
- if (response.status >= 400)
10405
- return 0;
10406
- let topic = yield response.json();
10407
- return topic.messageCount || 0;
10544
+ try {
10545
+ let response = yield fetch(`${this.serviceUrl}/topics/${topicId}`);
10546
+ if (response.status >= 400)
10547
+ return 0;
10548
+ let topic = yield response.json();
10549
+ return topic.messageCount || 0;
10550
+ }
10551
+ catch (e) {
10552
+ console.error(`[Banta/${topicId}] Failed to get message count for topic:`);
10553
+ console.error(e);
10554
+ return undefined;
10555
+ }
10408
10556
  });
10409
10557
  }
10410
10558
  refreshMessage(message) {
@@ -10441,7 +10589,7 @@ class ChatBackend extends ChatBackendBase {
10441
10589
  ChatBackend.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: ChatBackend, deps: [{ token: BANTA_SDK_OPTIONS }], target: i0.ɵɵFactoryTarget.Injectable });
10442
10590
  ChatBackend.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: ChatBackend });
10443
10591
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: ChatBackend, decorators: [{
10444
- type: Injectable
10592
+ type: Injectable$1
10445
10593
  }], ctorParameters: function () {
10446
10594
  return [{ type: undefined, decorators: [{
10447
10595
  type: Inject,
@@ -10488,7 +10636,7 @@ class UrlAttachmentResolver {
10488
10636
  UrlAttachmentResolver.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlAttachmentResolver, deps: [{ token: ChatBackendBase }], target: i0.ɵɵFactoryTarget.Injectable });
10489
10637
  UrlAttachmentResolver.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlAttachmentResolver });
10490
10638
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlAttachmentResolver, decorators: [{
10491
- type: Injectable
10639
+ type: Injectable$1
10492
10640
  }], ctorParameters: function () { return [{ type: ChatBackendBase }]; } });
10493
10641
 
10494
10642
  class YouTubeAttachmentResolver {
@@ -10589,9 +10737,7 @@ BantaSdkModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version:
10589
10737
  BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, declarations: [BantaComponent,
10590
10738
  BantaLogoComponent,
10591
10739
  LiveMessageComponent], imports: [CommonModule,
10592
- FormsModule,
10593
- BantaCommonModule,
10594
- CommentsModule,
10740
+ FormsModule, BantaCommonModule, CommentsModule,
10595
10741
  ChatModule,
10596
10742
  EmojiModule,
10597
10743
  MatIconModule,
@@ -10613,7 +10759,7 @@ BantaSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version:
10613
10759
  EmojiModule] });
10614
10760
  BantaSdkModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: BantaSdkModule, imports: [CommonModule,
10615
10761
  FormsModule,
10616
- BantaCommonModule,
10762
+ BantaCommonModule.forRoot(),
10617
10763
  CommentsModule,
10618
10764
  ChatModule,
10619
10765
  EmojiModule,
@@ -10637,7 +10783,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
10637
10783
  imports: [
10638
10784
  CommonModule,
10639
10785
  FormsModule,
10640
- BantaCommonModule,
10786
+ BantaCommonModule.forRoot(),
10641
10787
  CommentsModule,
10642
10788
  ChatModule,
10643
10789
  EmojiModule,
@@ -10678,5 +10824,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImpo
10678
10824
  * Generated bundle index. Do not edit.
10679
10825
  */
10680
10826
 
10681
- export { AttachmentButtonComponent, AttachmentScraperDirective, BANTA_SDK_OPTIONS, BantaAttachmentComponent, BantaAttachmentsComponent, BantaChatComponent, BantaCommentsComponent, BantaCommonModule, BantaComponent, BantaLogoComponent, BantaMarkdownToHtmlPipe, BantaMentionLinkerPipe, BantaReplySendOptionsDirective, BantaSdkModule, BantaTrustResourceUrlPipe, ChatBackend, ChatBackendBase, ChatMessageComponent, ChatModule, ChatSource, ChatViewComponent, CommentComponent, CommentFieldComponent, CommentSortComponent, CommentViewComponent, CommentsModule, EMOJIS, EmojiModule, EmojiSelectorButtonComponent, EmojiSelectorPanelComponent, GiphyAttachmentResolver, LightboxComponent, LiveChatMessageComponent, LiveCommentComponent, LiveMessageComponent, TimestampComponent, TweetAttachmentResolver, UrlAttachmentResolver, UrlAttachmentScraper, YouTubeAttachmentResolver, lazyConnection };
10827
+ export { AttachmentButtonComponent, AttachmentScraperDirective, BANTA_SDK_OPTIONS, BantaAttachmentComponent, BantaAttachmentsComponent, BantaChatComponent, BantaCommentsComponent, BantaCommonModule, BantaComponent, BantaLogoComponent, BantaMarkdownToHtmlPipe, BantaMentionLinkerPipe, BantaReplySendOptionsDirective, BantaSdkModule, BantaTrustResourceUrlPipe, ChatBackend, ChatBackendBase, ChatMessageComponent, ChatModule, ChatSource, ChatViewComponent, CommentComponent, CommentFieldComponent, CommentSortComponent, CommentViewComponent, CommentsModule, EMOJIS, EmojiModule, EmojiSelectorButtonComponent, EmojiSelectorPanelComponent, GiphyAttachmentResolver, LightboxComponent, LiveChatMessageComponent, LiveCommentComponent, LiveMessageComponent, TimerPool, TimestampComponent, TweetAttachmentResolver, UrlAttachmentResolver, UrlAttachmentScraper, YouTubeAttachmentResolver, lazyConnection };
10682
10828
  //# sourceMappingURL=banta-sdk.mjs.map