@banta/sdk 3.3.11 → 4.0.2

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.
Files changed (47) hide show
  1. package/banta-sdk.metadata.json +1 -1
  2. package/bundles/banta-sdk.umd.js +1157 -413
  3. package/bundles/banta-sdk.umd.js.map +1 -1
  4. package/bundles/banta-sdk.umd.min.js +1 -1
  5. package/bundles/banta-sdk.umd.min.js.map +1 -1
  6. package/esm2015/lib/banta/banta.component.js +9 -15
  7. package/esm2015/lib/banta-sdk.module.js +10 -4
  8. package/esm2015/lib/chat/banta-chat/banta-chat.component.js +16 -19
  9. package/esm2015/lib/chat/chat-message/chat-message.component.js +2 -2
  10. package/esm2015/lib/chat/chat-view/chat-view.component.js +7 -6
  11. package/esm2015/lib/chat/live-chat-message.component.js +3 -3
  12. package/esm2015/lib/chat-backend-base.js +17 -0
  13. package/esm2015/lib/chat-backend.js +74 -0
  14. package/esm2015/lib/chat-source-base.js +2 -0
  15. package/esm2015/lib/chat-source.js +151 -0
  16. package/esm2015/lib/comments/banta-comments/banta-comments.component.js +344 -174
  17. package/esm2015/lib/comments/comment/comment.component.js +56 -20
  18. package/esm2015/lib/comments/comment-field/comment-field.component.js +19 -17
  19. package/esm2015/lib/comments/comment-view/comment-view.component.js +77 -39
  20. package/esm2015/lib/comments/live-comment.component.js +3 -3
  21. package/esm2015/lib/common/index.js +1 -3
  22. package/esm2015/lib/index.js +6 -1
  23. package/esm2015/lib/sdk-options.js +2 -0
  24. package/fesm2015/banta-sdk.js +761 -310
  25. package/fesm2015/banta-sdk.js.map +1 -1
  26. package/lib/banta/banta.component.d.ts +7 -8
  27. package/lib/banta-sdk.module.d.ts +2 -1
  28. package/lib/chat/banta-chat/banta-chat.component.d.ts +10 -12
  29. package/lib/chat/chat-view/chat-view.component.d.ts +7 -4
  30. package/lib/chat/live-chat-message.component.d.ts +2 -2
  31. package/lib/chat-backend-base.d.ts +22 -0
  32. package/lib/chat-backend.d.ts +21 -0
  33. package/lib/chat-source-base.d.ts +31 -0
  34. package/lib/chat-source.d.ts +38 -0
  35. package/lib/comments/banta-comments/banta-comments.component.d.ts +68 -61
  36. package/lib/comments/comment/comment.component.d.ts +25 -6
  37. package/lib/comments/comment-field/comment-field.component.d.ts +9 -5
  38. package/lib/comments/comment-view/comment-view.component.d.ts +26 -8
  39. package/lib/comments/live-comment.component.d.ts +2 -2
  40. package/lib/common/index.d.ts +0 -2
  41. package/lib/index.d.ts +5 -0
  42. package/lib/sdk-options.d.ts +4 -0
  43. package/package.json +1 -1
  44. package/esm2015/lib/common/banta.service.js +0 -21
  45. package/esm2015/lib/common/chat-backend.service.js +0 -7
  46. package/lib/common/banta.service.d.ts +0 -9
  47. package/lib/common/chat-backend.service.d.ts +0 -14
@@ -17,34 +17,6 @@
17
17
  return obs.pipe(operators.publish()).refCount();
18
18
  }
19
19
 
20
- var BantaService = /** @class */ (function () {
21
- function BantaService() {
22
- this._userChanged = new rxjs.BehaviorSubject(null);
23
- }
24
- Object.defineProperty(BantaService.prototype, "userChanged", {
25
- get: function () {
26
- return this._userChanged;
27
- },
28
- enumerable: false,
29
- configurable: true
30
- });
31
- Object.defineProperty(BantaService.prototype, "user", {
32
- get: function () {
33
- return this._user;
34
- },
35
- set: function (user) {
36
- this._user = user;
37
- this._userChanged.next(user);
38
- },
39
- enumerable: false,
40
- configurable: true
41
- });
42
- return BantaService;
43
- }());
44
- BantaService.decorators = [
45
- { type: core.Injectable }
46
- ];
47
-
48
20
  var TimestampComponent = /** @class */ (function () {
49
21
  function TimestampComponent() {
50
22
  this.relative = '';
@@ -149,15 +121,6 @@
149
121
  value: [{ type: core.Input }]
150
122
  };
151
123
 
152
- var ChatBackendService = /** @class */ (function () {
153
- function ChatBackendService() {
154
- }
155
- return ChatBackendService;
156
- }());
157
- ChatBackendService.decorators = [
158
- { type: core.Injectable }
159
- ];
160
-
161
124
  var COMPONENTS = [
162
125
  TimestampComponent
163
126
  ];
@@ -7220,12 +7183,36 @@
7220
7183
  },] }
7221
7184
  ];
7222
7185
 
7186
+ var ChatBackendBase = /** @class */ (function () {
7187
+ function ChatBackendBase() {
7188
+ this._userChanged = new rxjs.BehaviorSubject(null);
7189
+ }
7190
+ Object.defineProperty(ChatBackendBase.prototype, "userChanged", {
7191
+ get: function () {
7192
+ return this._userChanged;
7193
+ },
7194
+ enumerable: false,
7195
+ configurable: true
7196
+ });
7197
+ Object.defineProperty(ChatBackendBase.prototype, "user", {
7198
+ get: function () {
7199
+ return this._user;
7200
+ },
7201
+ set: function (user) {
7202
+ this._user = user;
7203
+ this._userChanged.next(user);
7204
+ },
7205
+ enumerable: false,
7206
+ configurable: true
7207
+ });
7208
+ return ChatBackendBase;
7209
+ }());
7210
+
7223
7211
  /**
7224
7212
  * Unified chat and comments component
7225
7213
  */
7226
7214
  var BantaComponent = /** @class */ (function () {
7227
- function BantaComponent(banta, backend, matDialog) {
7228
- this.banta = banta;
7215
+ function BantaComponent(backend, matDialog) {
7229
7216
  this.backend = backend;
7230
7217
  this.matDialog = matDialog;
7231
7218
  this._subs = new rxjs.Subscription();
@@ -7244,7 +7231,7 @@
7244
7231
  }
7245
7232
  BantaComponent.prototype.ngOnInit = function () {
7246
7233
  var _this = this;
7247
- this._subs.add(this.banta.userChanged.subscribe(function (user) { return _this.currentUser = user; }));
7234
+ this._subs.add(this.backend.userChanged.subscribe(function (user) { return _this.currentUser = user; }));
7248
7235
  this._subs.add(this.backend.notificationsChanged.subscribe(function (notifs) { return _this.notifications = notifs; }));
7249
7236
  this._subs.add(this.backend.newNotification.subscribe(function (notif) {
7250
7237
  _this.newNotifications = true;
@@ -7263,7 +7250,7 @@
7263
7250
  message = {
7264
7251
  user: null,
7265
7252
  sentAt: Date.now(),
7266
- upvotes: 0,
7253
+ likes: 0,
7267
7254
  message: text
7268
7255
  };
7269
7256
  _a.label = 1;
@@ -7427,19 +7414,7 @@
7427
7414
  BantaComponent.prototype.upvoteMessage = function (message) {
7428
7415
  return __awaiter(this, void 0, void 0, function () {
7429
7416
  return __generator(this, function (_a) {
7430
- switch (_a.label) {
7431
- case 0:
7432
- if (!message.parentMessageId) return [3 /*break*/, 2];
7433
- return [4 /*yield*/, this.backend.upvoteMessage(message.topicId, message.parentMessageId, message.id)];
7434
- case 1:
7435
- _a.sent();
7436
- return [3 /*break*/, 4];
7437
- case 2: return [4 /*yield*/, this.backend.upvoteMessage(message.topicId, message.id)];
7438
- case 3:
7439
- _a.sent();
7440
- _a.label = 4;
7441
- case 4: return [2 /*return*/];
7442
- }
7417
+ return [2 /*return*/];
7443
7418
  });
7444
7419
  });
7445
7420
  };
@@ -7460,13 +7435,12 @@
7460
7435
  BantaComponent.decorators = [
7461
7436
  { type: core.Component, args: [{
7462
7437
  selector: "banta",
7463
- template: "\r\n<mat-menu #userMenu=\"matMenu\">\r\n <ng-container *ngIf=\"currentUser\">\r\n <button [disabled]=\"true\" mat-menu-item>{{currentUser.displayName}} (@{{currentUser.username}})</button>\r\n <button mat-menu-item (click)=\"signOut()\">Sign Out</button>\r\n </ng-container>\r\n <ng-container *ngIf=\"!currentUser\">\r\n <button mat-menu-item>Sign In</button>\r\n </ng-container>\r\n <button mat-menu-item>Help</button>\r\n</mat-menu>\r\n\r\n<div class=\"tabs\">\r\n <div>\r\n <a mat-button (click)=\"mobileFocus = 'chat'\">{{chatLabel}}</a>\r\n <a mat-button (click)=\"mobileFocus = 'comments'\">{{commentsLabel}}</a>\r\n </div>\r\n <div class=\"spacer\"></div>\r\n <div>\r\n <ng-container *ngIf=\"currentUser\">\r\n <button mat-button [matMenuTriggerFor]=\"userMenu\">\r\n @{{currentUser.username}}\r\n </button>\r\n <button mat-icon-button (click)=\"showNotifications()\">\r\n <mat-icon>notification_important</mat-icon>\r\n </button>\r\n </ng-container>\r\n \r\n <button mat-button *ngIf=\"!currentUser\" (click)=\"showSignIn()\">\r\n Sign In\r\n </button>\r\n </div>\r\n</div>\r\n\r\n<div class=\"firehose\" [class.focus]=\"mobileFocus === 'chat'\">\r\n <header>\r\n <div>\r\n <label (click)=\"mobileFocus = 'chat'\">{{chatLabel}}</label>\r\n <div class=\"spacer\"></div>\r\n\r\n <ng-container *ngIf=\"currentUser\">\r\n <button mat-button [matMenuTriggerFor]=\"userMenu\">\r\n @{{currentUser.username}}\r\n </button>\r\n <button mat-icon-button (click)=\"showNotifications()\">\r\n <mat-icon>notification_important</mat-icon>\r\n </button>\r\n </ng-container>\r\n \r\n <button mat-button *ngIf=\"!currentUser\" (click)=\"showSignIn()\">\r\n Sign In\r\n </button>\r\n </div>\r\n </header>\r\n <banta-chat \r\n #firehose\r\n [source]=\"firehoseSource\"\r\n (signInSelected)=\"showSignIn()\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (userSelected)=\"showProfile($event.user)\"\r\n (reported)=\"reportMessage($event)\"\r\n ></banta-chat>\r\n</div>\r\n\r\n<div class=\"aux\" [class.focus]=\"mobileFocus === 'aux'\" [class.open]=\"auxOpen\">\r\n <header>\r\n <div>\r\n <label>{{auxTitle}}</label>\r\n <div class=\"spacer\"></div>\r\n <button mat-icon-button (click)=\"auxOpen = false\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </header>\r\n <div class=\"aux-contents\">\r\n <ng-container *ngIf=\"auxMode === 'profile'\">\r\n <ng-container *ngIf=\"profileUser\">\r\n\r\n <div>\r\n <strong style=\"font-size: 125%;\">\r\n {{profileUser.displayName}}\r\n </strong>\r\n @{{profileUser.username}}\r\n </div>\r\n\r\n <br/>\r\n <strong>Top Messages</strong>\r\n\r\n <div>\r\n <em>Not yet available</em>\r\n </div>\r\n\r\n <br/>\r\n <strong>Recent Messages</strong>\r\n\r\n <div>\r\n <em>Not yet available</em>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"auxMode === 'report'\">\r\n <p>Are you sure you want to report this message?</p>\r\n\r\n <banta-live-message [message]=\"reportedMessage\"></banta-live-message>\r\n\r\n <div style=\"text-align: center;\">\r\n <button mat-raised-button color=\"primary\" (click)=\"sendReport(reportedMessage)\">Yes, Report</button>\r\n &nbsp;\r\n <button mat-raised-button color=\"secondary\" (click)=\"auxOpen = false\">No, Cancel</button>\r\n </div>\r\n\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"auxMode === 'notifications'\">\r\n\r\n <div *ngIf=\"!notifications || notifications.length === 0\">\r\n <em>You do not have any notifications yet</em>\r\n </div>\r\n \r\n <div class=\"notifications\">\r\n <div class=\"notification\" *ngFor=\"let notif of notifications\">\r\n <div>\r\n <ng-container *ngIf=\"notif.type === 'upvote'\">\r\n @{{notif.message?.user?.username}} upvoted your post\r\n \r\n <banta-live-message\r\n [message]=\"notif.message\"\r\n (upvoted)=\"upvoteMessage(notif.message)\"\r\n (reported)=\"reportMessage(notif.message)\"\r\n (selected)=\"goToMessage(notif.message)\">\r\n </banta-live-message>\r\n\r\n </ng-container>\r\n <ng-container *ngIf=\"notif.type === 'notice'\">\r\n <div>\r\n {{notif.message}}\r\n </div>\r\n <a mat-button target=\"_blank\" href=\"{{notif.actionUrl}}\">\r\n {{notif.actionLabel}}\r\n </a>\r\n </ng-container>\r\n <ng-container *ngIf=\"notif.type === 'mention'\">\r\n You were mentioned by @{{notif.message?.user?.username}}\r\n\r\n <banta-live-message\r\n [message]=\"notif.message\"\r\n (upvoted)=\"upvoteMessage(notif.message)\"\r\n (reported)=\"reportMessage(notif.message)\"\r\n (selected)=\"goToMessage(notif.message)\">\r\n </banta-live-message>\r\n\r\n </ng-container>\r\n <ng-container *ngIf=\"notif.type === 'reply'\">\r\n @{{notif.replyMessage?.user?.username}} replied to your post\r\n \r\n <banta-live-message\r\n [message]=\"notif.replyMessage\"\r\n (upvoted)=\"upvoteMessage(notif.replyMessage)\"\r\n (reported)=\"reportMessage(notif.replyMessage)\"\r\n (selected)=\"goToMessage(notif.replyMessage)\">\r\n </banta-live-message>\r\n </ng-container>\r\n </div>\r\n\r\n <banta-timestamp [value]=\"notif.sentAt\"></banta-timestamp>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n</div>\r\n<div class=\"points\" [class.focus]=\"mobileFocus === 'points'\">\r\n <header>\r\n <div>\r\n <label>{{commentsLabel}}</label>\r\n </div>\r\n </header>\r\n <div class=\"point-focus\">\r\n <div class=\"actions\">\r\n <button mat-button (click)=\"pointUnfocus()\">\r\n <mat-icon>arrow_back</mat-icon>\r\n Back\r\n </button>\r\n\r\n <div class=\"spacer\"></div>\r\n \r\n <ng-container *ngIf=\"pointOpen\">\r\n <div class=\"counted-action\">\r\n <div class=\"count-indicator\"> \r\n {{pointOpen.upvotes}}\r\n </div>\r\n <button mat-icon-button>\r\n <mat-icon>thumb_up</mat-icon>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </div>\r\n\r\n <div *ngIf=\"!pointSubChat\">\r\n Error: No subchat\r\n </div>\r\n \r\n <banta-comment-view\r\n class=\"subcomments\"\r\n *ngIf=\"pointSubChat\"\r\n [newestLast]=\"true\"\r\n [allowReplies]=\"false\"\r\n [source]=\"pointSubChat\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (reported)=\"reportMessage($event)\"\r\n (userSelected)=\"showProfile($event.user)\"\r\n >\r\n \r\n <banta-comment\r\n class=\"focused-comment\"\r\n data-before\r\n *ngIf=\"pointOpen\"\r\n (upvoted)=\"upvoteMessage(pointOpen)\"\r\n (userSelected)=\"showProfile(pointOpen.user)\"\r\n (reported)=\"reportMessage(pointOpen)\"\r\n [showReplyAction]=\"false\"\r\n [message]=\"pointOpen\"\r\n ></banta-comment>\r\n \r\n <div class=\"message reply\">\r\n Reply:\r\n <form class=\"new-message\" (submit)=\"sendPointSubMessage()\">\r\n <textarea \r\n name=\"message\" \r\n (keydown)=\"newPointSubMessageKeyDown($event)\"\r\n [(ngModel)]=\"newPointSubMessage.message\"></textarea>\r\n \r\n <div class=\"actions\">\r\n <button [disabled]=\"!newPointSubMessage.message\" \r\n mat-raised-button color=\"primary\">Send</button>\r\n </div>\r\n </form>\r\n </div>\r\n </banta-comment-view>\r\n </div>\r\n <div class=\"points-section\">\r\n <banta-comments\r\n [source]=\"pointSource\"\r\n (signInSelected)=\"showSignIn()\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (reported)=\"reportMessage($event)\"\r\n (selected)=\"goToMessage($event)\"\r\n (userSelected)=\"showProfile($event.user)\"\r\n ></banta-comments>\r\n </div>\r\n</div>",
7438
+ template: "\r\n<mat-menu #userMenu=\"matMenu\">\r\n <ng-container *ngIf=\"currentUser\">\r\n <button [disabled]=\"true\" mat-menu-item>{{currentUser.displayName}} (@{{currentUser.username}})</button>\r\n <button mat-menu-item (click)=\"signOut()\">Sign Out</button>\r\n </ng-container>\r\n <ng-container *ngIf=\"!currentUser\">\r\n <button mat-menu-item>Sign In</button>\r\n </ng-container>\r\n <button mat-menu-item>Help</button>\r\n</mat-menu>\r\n\r\n<div class=\"tabs\">\r\n <div>\r\n <a mat-button (click)=\"mobileFocus = 'chat'\">{{chatLabel}}</a>\r\n <a mat-button (click)=\"mobileFocus = 'comments'\">{{commentsLabel}}</a>\r\n </div>\r\n <div class=\"spacer\"></div>\r\n <div>\r\n <ng-container *ngIf=\"currentUser\">\r\n <button mat-button [matMenuTriggerFor]=\"userMenu\">\r\n @{{currentUser.username}}\r\n </button>\r\n <button mat-icon-button (click)=\"showNotifications()\">\r\n <mat-icon>notification_important</mat-icon>\r\n </button>\r\n </ng-container>\r\n \r\n <button mat-button *ngIf=\"!currentUser\" (click)=\"showSignIn()\">\r\n Sign In\r\n </button>\r\n </div>\r\n</div>\r\n\r\n<div class=\"firehose\" [class.focus]=\"mobileFocus === 'chat'\">\r\n <header>\r\n <div>\r\n <label (click)=\"mobileFocus = 'chat'\">{{chatLabel}}</label>\r\n <div class=\"spacer\"></div>\r\n\r\n <ng-container *ngIf=\"currentUser\">\r\n <button mat-button [matMenuTriggerFor]=\"userMenu\">\r\n @{{currentUser.username}}\r\n </button>\r\n <button mat-icon-button (click)=\"showNotifications()\">\r\n <mat-icon>notification_important</mat-icon>\r\n </button>\r\n </ng-container>\r\n \r\n <button mat-button *ngIf=\"!currentUser\" (click)=\"showSignIn()\">\r\n Sign In\r\n </button>\r\n </div>\r\n </header>\r\n <banta-chat \r\n #firehose\r\n [source]=\"firehoseSource\"\r\n (signInSelected)=\"showSignIn()\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (userSelected)=\"showProfile($event.user)\"\r\n (reported)=\"reportMessage($event)\"\r\n ></banta-chat>\r\n</div>\r\n\r\n<div class=\"aux\" [class.focus]=\"mobileFocus === 'aux'\" [class.open]=\"auxOpen\">\r\n <header>\r\n <div>\r\n <label>{{auxTitle}}</label>\r\n <div class=\"spacer\"></div>\r\n <button mat-icon-button (click)=\"auxOpen = false\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </header>\r\n <div class=\"aux-contents\">\r\n <ng-container *ngIf=\"auxMode === 'profile'\">\r\n <ng-container *ngIf=\"profileUser\">\r\n\r\n <div>\r\n <strong style=\"font-size: 125%;\">\r\n {{profileUser.displayName}}\r\n </strong>\r\n @{{profileUser.username}}\r\n </div>\r\n\r\n <br/>\r\n <strong>Top Messages</strong>\r\n\r\n <div>\r\n <em>Not yet available</em>\r\n </div>\r\n\r\n <br/>\r\n <strong>Recent Messages</strong>\r\n\r\n <div>\r\n <em>Not yet available</em>\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"auxMode === 'report'\">\r\n <p>Are you sure you want to report this message?</p>\r\n\r\n <banta-live-message [message]=\"reportedMessage\"></banta-live-message>\r\n\r\n <div style=\"text-align: center;\">\r\n <button mat-raised-button color=\"primary\" (click)=\"sendReport(reportedMessage)\">Yes, Report</button>\r\n &nbsp;\r\n <button mat-raised-button color=\"secondary\" (click)=\"auxOpen = false\">No, Cancel</button>\r\n </div>\r\n\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"auxMode === 'notifications'\">\r\n\r\n <div *ngIf=\"!notifications || notifications.length === 0\">\r\n <em>You do not have any notifications yet</em>\r\n </div>\r\n \r\n <div class=\"notifications\">\r\n <div class=\"notification\" *ngFor=\"let notif of notifications\">\r\n <div>\r\n <ng-container *ngIf=\"notif.type === 'upvote'\">\r\n @{{notif.message?.user?.username}} upvoted your post\r\n \r\n <banta-live-message\r\n [message]=\"notif.message\"\r\n (upvoted)=\"upvoteMessage(notif.message)\"\r\n (reported)=\"reportMessage(notif.message)\"\r\n (selected)=\"goToMessage(notif.message)\">\r\n </banta-live-message>\r\n\r\n </ng-container>\r\n <ng-container *ngIf=\"notif.type === 'notice'\">\r\n <div>\r\n {{notif.message}}\r\n </div>\r\n <a mat-button target=\"_blank\" href=\"{{notif.actionUrl}}\">\r\n {{notif.actionLabel}}\r\n </a>\r\n </ng-container>\r\n <ng-container *ngIf=\"notif.type === 'mention'\">\r\n You were mentioned by @{{notif.message?.user?.username}}\r\n\r\n <banta-live-message\r\n [message]=\"notif.message\"\r\n (upvoted)=\"upvoteMessage(notif.message)\"\r\n (reported)=\"reportMessage(notif.message)\"\r\n (selected)=\"goToMessage(notif.message)\">\r\n </banta-live-message>\r\n\r\n </ng-container>\r\n <ng-container *ngIf=\"notif.type === 'reply'\">\r\n @{{notif.replyMessage?.user?.username}} replied to your post\r\n \r\n <banta-live-message\r\n [message]=\"notif.replyMessage\"\r\n (upvoted)=\"upvoteMessage(notif.replyMessage)\"\r\n (reported)=\"reportMessage(notif.replyMessage)\"\r\n (selected)=\"goToMessage(notif.replyMessage)\">\r\n </banta-live-message>\r\n </ng-container>\r\n </div>\r\n\r\n <banta-timestamp [value]=\"notif.sentAt\"></banta-timestamp>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n</div>\r\n<div class=\"points\" [class.focus]=\"mobileFocus === 'points'\">\r\n <header>\r\n <div>\r\n <label>{{commentsLabel}}</label>\r\n </div>\r\n </header>\r\n <div class=\"point-focus\">\r\n <div class=\"actions\">\r\n <button mat-button (click)=\"pointUnfocus()\">\r\n <mat-icon>arrow_back</mat-icon>\r\n Back\r\n </button>\r\n\r\n <div class=\"spacer\"></div>\r\n \r\n <ng-container *ngIf=\"pointOpen\">\r\n <div class=\"counted-action\">\r\n <div class=\"count-indicator\"> \r\n {{pointOpen.likes}}\r\n </div>\r\n <button mat-icon-button>\r\n <mat-icon>thumb_up</mat-icon>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </div>\r\n\r\n <div *ngIf=\"!pointSubChat\">\r\n Error: No subchat\r\n </div>\r\n \r\n <banta-comment-view\r\n class=\"subcomments\"\r\n *ngIf=\"pointSubChat\"\r\n [newestLast]=\"true\"\r\n [allowReplies]=\"false\"\r\n [source]=\"pointSubChat\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (reported)=\"reportMessage($event)\"\r\n (userSelected)=\"showProfile($event.user)\"\r\n >\r\n \r\n <banta-comment\r\n class=\"focused-comment\"\r\n data-before\r\n *ngIf=\"pointOpen\"\r\n (upvoted)=\"upvoteMessage(pointOpen)\"\r\n (userSelected)=\"showProfile(pointOpen.user)\"\r\n (reported)=\"reportMessage(pointOpen)\"\r\n [showReplyAction]=\"false\"\r\n [message]=\"pointOpen\"\r\n ></banta-comment>\r\n \r\n <div class=\"message reply\">\r\n Reply:\r\n <form class=\"new-message\" (submit)=\"sendPointSubMessage()\">\r\n <textarea \r\n name=\"message\" \r\n (keydown)=\"newPointSubMessageKeyDown($event)\"\r\n [(ngModel)]=\"newPointSubMessage.message\"></textarea>\r\n \r\n <div class=\"actions\">\r\n <button [disabled]=\"!newPointSubMessage.message\" \r\n mat-raised-button color=\"primary\">Send</button>\r\n </div>\r\n </form>\r\n </div>\r\n </banta-comment-view>\r\n </div>\r\n <div class=\"points-section\">\r\n <banta-comments\r\n [source]=\"pointSource\"\r\n (signInSelected)=\"showSignIn()\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (reported)=\"reportMessage($event)\"\r\n (selected)=\"goToMessage($event)\"\r\n (userSelected)=\"showProfile($event.user)\"\r\n ></banta-comments>\r\n </div>\r\n</div>",
7464
7439
  styles: [":host{display:flex;flex-direction:row;padding:.5em;height:40em;position:relative}.counted-action{display:flex;align-items:center}.count-indicator{font-size:9pt;padding:0 3px;border-radius:3px;border:1px solid #333}header{position:relative;margin-bottom:1em}header div{display:flex;align-items:center;height:30px}header button{color:#666}header label{text-transform:uppercase;z-index:1;font-size:12pt;letter-spacing:2px;font-weight:100;color:#333;margin:0 auto 0 0;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis}header:after,header label{display:block;position:relative}header:after{content:\"\";border:1px solid #ccc;height:0;width:100%;z-index:0}.points{max-width:50em;display:flex;flex-direction:column}:host.point-focus .points{width:66%;max-width:50em}:host.point-focus .points .points-section{opacity:0;pointer-events:none}:host.point-focus .points .point-focus{opacity:1;pointer-events:auto}:host.point-focus .points .point-focus .actions{display:flex}banta-comments{flex-grow:1}.points{width:33%;margin-left:.5em;font-size:12pt;flex-shrink:0;max-width:30em;transition:width .2s ease-in,max-width .2s ease-in;position:relative}.points .points-section{opacity:1;z-index:2}.points .point-focus,.points .points-section{flex-grow:1;display:flex;flex-direction:column;transition:opacity .2s ease-in}.points .point-focus{position:absolute;width:100%;bottom:0;top:1.75em;right:0;left:0;padding:.5em;opacity:0}.firehose{flex-grow:1;font-size:10pt;display:flex;flex-direction:column}form{display:flex;padding:.5em 0;align-items:center}form textarea{font-size:14pt;min-height:6em}form input[type=text],form textarea{background:#000;color:#fff;border:1px solid #333;width:100%}form input[type=text]{height:1em}form .actions{margin-left:1em}form button{display:block;margin:0 0 0 auto}.subcomments ::ng-deep banta-comment{font-size:10pt}.subcomments ::ng-deep banta-comment.focused-comment{background:#001321;color:#fff;font-size:12pt}.aux{width:0;min-width:0;overflow-x:hidden;transition:width .4s ease-out,min-width .4s ease-out;display:flex;flex-direction:column}.aux.open{width:30em;min-width:18em}.aux .aux-contents{width:30em;min-width:10em;max-width:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;flex-grow:1}.notifications .notification{border-bottom:1px solid #333;padding:1em}.notifications .notification banta-timestamp{display:block;text-align:right;font-size:9pt;color:#999}.message.reply{padding:1em}.tabs{display:none}@media (max-width:1015px){:host{flex-direction:column}.tabs{display:flex;position:absolute;top:0;left:0;right:0;width:100%;z-index:10;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background:rgba(0,0,0,.5)}.points{width:100%;max-width:100%;margin-left:0}header{display:none}.aux,:host.point-focus .points{width:100%;max-width:100%}.aux{min-width:0}.aux,.firehose,.points{position:absolute;top:2em;left:0;right:0;bottom:0;z-index:0;background:#000}.aux.focus,.firehose.focus,.points.focus{z-index:2}}:host-context(.mat-dark-theme) :host{background:#090909;color:#fff}:host-context(.mat-dark-theme) form textarea{background:#ccc;color:#333}:host-context(.mat-dark-theme) header:after{border-color:#222}:host-context(.mat-dark-theme) header label{color:#aaa}"]
7465
7440
  },] }
7466
7441
  ];
7467
7442
  BantaComponent.ctorParameters = function () { return [
7468
- { type: BantaService },
7469
- { type: ChatBackendService },
7443
+ { type: ChatBackendBase },
7470
7444
  { type: dialog.MatDialog }
7471
7445
  ]; };
7472
7446
  BantaComponent.propDecorators = {
@@ -7550,7 +7524,7 @@
7550
7524
  ChatMessageComponent.decorators = [
7551
7525
  { type: core.Component, args: [{
7552
7526
  selector: 'banta-chat-message',
7553
- template: "<div class=\"message-content\">\r\n <div class=\"user\" (click)=\"selectUser()\">\r\n <div class=\"avatar\" [style.background-image]=\"avatarForUser(message.user)\"></div>\r\n <label>{{message.user.username}}</label>\r\n </div>\r\n <div class=\"content\">\r\n <div (click)=\"select()\">\r\n {{message.message}}\r\n </div>\r\n <div class=\"status\">\r\n <div class=\"count-indicator\" *ngIf=\"message.upvotes > 0\">\r\n {{message.upvotes}} <mat-icon [inline]=\"true\">star</mat-icon>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n<div class=\"actions\">\r\n <button mat-icon-button matTooltip=\"Upvote\" matTooltipPosition=\"below\" (click)=\"upvote()\">\r\n <mat-icon [inline]=\"true\">thumb_up</mat-icon>\r\n </button>\r\n <button mat-icon-button matTooltip=\"Report\" matTooltipPosition=\"below\" (click)=\"report()\">\r\n <mat-icon [inline]=\"true\">report</mat-icon>\r\n </button>\r\n</div>",
7527
+ template: "<div class=\"message-content\">\r\n <div class=\"user\" (click)=\"selectUser()\">\r\n <div class=\"avatar\" [style.background-image]=\"avatarForUser(message.user)\"></div>\r\n <label>{{message.user.username}}</label>\r\n </div>\r\n <div class=\"content\">\r\n <div (click)=\"select()\">\r\n {{message.message}}\r\n </div>\r\n <div class=\"status\">\r\n <div class=\"count-indicator\" *ngIf=\"message.likes > 0\">\r\n {{message.likes}} <mat-icon [inline]=\"true\">star</mat-icon>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n<div class=\"actions\">\r\n <button mat-icon-button matTooltip=\"Upvote\" matTooltipPosition=\"below\" (click)=\"upvote()\">\r\n <mat-icon [inline]=\"true\">thumb_up</mat-icon>\r\n </button>\r\n <button mat-icon-button matTooltip=\"Report\" matTooltipPosition=\"below\" (click)=\"report()\">\r\n <mat-icon [inline]=\"true\">report</mat-icon>\r\n </button>\r\n</div>",
7554
7528
  styles: [":host{display:flex;flex-direction:row;align-items:center;padding:0 1em;background-color:#fff;color:#000;transition:background-color .4s ease-out}:host .message-content .content{color:#111}:host:hover{background-color:#ddd}:host.highlight{background:#00121b}:host.highlight:hover{background:#01324d}:host:nth-child(2n){background-color:#eee}:host:nth-child(2n):hover{background:#ddd}:host:nth-child(2n) .message-content .content{color:#222}:host:nth-child(2n).highlight{background:#001a2a}:host:nth-child(2n).highlight:hover{background:#002b44}:host .message-content{display:flex;flex-direction:row;flex-grow:1;align-items:center}:host .message-content .content{display:flex;flex-direction:row;padding:5px 0}:host .message-content .content .status{display:flex;flex-direction:row;align-items:center;margin-left:1em}:host .message-content .content .status mat-icon{margin-left:.5em}:host .user{color:#999;font-weight:400;text-align:right;margin-right:.25em;flex-shrink:0;display:flex;align-items:center}:host .user .avatar{background-position:50%;background-size:cover;background-color:#333;border-radius:100%;flex-shrink:0;flex-grow:0;margin-right:1em;width:2em;height:2em}:host .user:after{content:\":\";margin-right:1em}:host .content{flex-grow:1}:host .actions{flex-shrink:0;white-space:nowrap;opacity:0;transition:opacity .4s ease-out}:host:hover .actions{opacity:1}.count-indicator{white-space:nowrap}:host-context(.mat-dark-theme){background-color:#000;color:#fff}:host-context(.mat-dark-theme) .message-content .content{color:#ddd}:host-context(.mat-dark-theme):hover{background-color:#111}:host-context(.mat-dark-theme):nth-child(2n).highlight{background:#001a2a}:host-context(.mat-dark-theme):nth-child(2n).highlight:hover{background:#002b44}:host-context(.mat-dark-theme):nth-child(2n):hover{background-color:#111}:host-context(.mat-dark-theme):nth-child(2n){background-color:#080808}:host-context(.mat-dark-theme):nth-child(2n) .message-content .content{color:#eee}label{margin:0}"]
7555
7529
  },] }
7556
7530
  ];
@@ -7631,7 +7605,7 @@
7631
7605
  },] }
7632
7606
  ];
7633
7607
  LiveChatMessageComponent.ctorParameters = function () { return [
7634
- { type: ChatBackendService }
7608
+ { type: ChatBackendBase }
7635
7609
  ]; };
7636
7610
  LiveChatMessageComponent.propDecorators = {
7637
7611
  upvoted: [{ type: core.Output }],
@@ -7641,7 +7615,8 @@
7641
7615
  };
7642
7616
 
7643
7617
  var ChatViewComponent = /** @class */ (function () {
7644
- function ChatViewComponent(elementRef) {
7618
+ function ChatViewComponent(backend, elementRef) {
7619
+ this.backend = backend;
7645
7620
  this.elementRef = elementRef;
7646
7621
  this._sourceSubs = new rxjs.Subscription();
7647
7622
  this._selected = new rxjs.Subject();
@@ -7672,10 +7647,8 @@
7672
7647
  console.dir(this.messages);
7673
7648
  this._sourceSubs.add(this._source.messageReceived.subscribe(function (msg) { return _this.messageReceived(msg); }));
7674
7649
  this._sourceSubs.add(this._source.messageSent.subscribe(function (msg) { return _this.messageSent(msg); }));
7675
- if (this._source.currentUserChanged) {
7676
- this._sourceSubs.add(this._source.currentUserChanged
7677
- .subscribe(function (user) { return _this.currentUser = user; }));
7678
- }
7650
+ this._sourceSubs.add(this.backend.userChanged
7651
+ .subscribe(function (user) { return _this.currentUser = user; }));
7679
7652
  }
7680
7653
  },
7681
7654
  enumerable: false,
@@ -7795,6 +7768,7 @@
7795
7768
  },] }
7796
7769
  ];
7797
7770
  ChatViewComponent.ctorParameters = function () { return [
7771
+ { type: ChatBackendBase },
7798
7772
  { type: core.ElementRef }
7799
7773
  ]; };
7800
7774
  ChatViewComponent.propDecorators = {
@@ -7811,10 +7785,8 @@
7811
7785
  * Chat component
7812
7786
  */
7813
7787
  var BantaChatComponent = /** @class */ (function () {
7814
- function BantaChatComponent(banta, backend, elementRef) {
7815
- this.banta = banta;
7788
+ function BantaChatComponent(backend) {
7816
7789
  this.backend = backend;
7817
- this.elementRef = elementRef;
7818
7790
  this._subs = new rxjs.Subscription();
7819
7791
  this.user = null;
7820
7792
  this.signInLabel = 'Sign In';
@@ -7831,7 +7803,7 @@
7831
7803
  }
7832
7804
  BantaChatComponent.prototype.ngOnInit = function () {
7833
7805
  var _this = this;
7834
- this._subs.add(this.banta.userChanged.subscribe(function (user) { return _this.user = user; }));
7806
+ this._subs.add(this.backend.userChanged.subscribe(function (user) { return _this.user = user; }));
7835
7807
  };
7836
7808
  BantaChatComponent.prototype.ngOnDestroy = function () {
7837
7809
  this._subs.unsubscribe();
@@ -7891,8 +7863,8 @@
7891
7863
  BantaChatComponent.prototype.showSignIn = function () {
7892
7864
  this._signInSelected.next();
7893
7865
  };
7894
- BantaChatComponent.prototype.sendPermissionError = function () {
7895
- this._permissionDeniedError.next();
7866
+ BantaChatComponent.prototype.sendPermissionError = function (message) {
7867
+ this._permissionDeniedError.next(message);
7896
7868
  };
7897
7869
  BantaChatComponent.prototype.insertEmoji = function (emoji) {
7898
7870
  var message = this.newMessage.message || '';
@@ -7947,14 +7919,15 @@
7947
7919
  });
7948
7920
  Object.defineProperty(BantaChatComponent.prototype, "canChat", {
7949
7921
  get: function () {
7950
- var _a;
7951
7922
  if (!this.user)
7952
7923
  return false;
7953
- if (!this.user.permissions)
7954
- return true;
7955
- if (!this.user.permissions.canChat)
7956
- return true;
7957
- return (_a = this.user.permissions) === null || _a === void 0 ? void 0 : _a.canChat(this.source);
7924
+ // TODO
7925
+ // if (!this.user.permissions)
7926
+ // return true;
7927
+ // if (!this.user.permissions.canChat)
7928
+ // return true;
7929
+ // return this.user.permissions?.canChat(this.source);
7930
+ return true;
7958
7931
  },
7959
7932
  enumerable: false,
7960
7933
  configurable: true
@@ -7975,7 +7948,7 @@
7975
7948
  message = {
7976
7949
  user: null,
7977
7950
  sentAt: Date.now(),
7978
- upvotes: 0,
7951
+ likes: 0,
7979
7952
  url: location.href,
7980
7953
  message: text
7981
7954
  };
@@ -8011,9 +7984,7 @@
8011
7984
  },] }
8012
7985
  ];
8013
7986
  BantaChatComponent.ctorParameters = function () { return [
8014
- { type: BantaService },
8015
- { type: ChatBackendService },
8016
- { type: core.ElementRef }
7987
+ { type: ChatBackendBase }
8017
7988
  ]; };
8018
7989
  BantaChatComponent.propDecorators = {
8019
7990
  shouldInterceptMessageSend: [{ type: core.Input }],
@@ -8131,7 +8102,8 @@
8131
8102
  function CommentComponent() {
8132
8103
  this._reported = new rxjs.Subject();
8133
8104
  this._selected = new rxjs.Subject();
8134
- this._upvoted = new rxjs.Subject();
8105
+ this._liked = new rxjs.Subject();
8106
+ this._unliked = new rxjs.Subject();
8135
8107
  this._shared = new rxjs.Subject();
8136
8108
  this._userSelected = new rxjs.Subject();
8137
8109
  this._avatarSelected = new rxjs.Subject();
@@ -8139,7 +8111,12 @@
8139
8111
  this.isNew = false;
8140
8112
  this.visible = false;
8141
8113
  this.showReplyAction = true;
8142
- this.upvoting = false;
8114
+ this.mine = false;
8115
+ this.editing = false;
8116
+ this._editStarted = new rxjs.Subject();
8117
+ this._deleted = new rxjs.Subject();
8118
+ this._editEnded = new rxjs.Subject();
8119
+ this._edited = new rxjs.Subject();
8143
8120
  }
8144
8121
  CommentComponent.prototype.ngOnInit = function () {
8145
8122
  var _this = this;
@@ -8152,6 +8129,14 @@
8152
8129
  setTimeout(function () { return _this.isNew = false; }, 1000);
8153
8130
  }, randomTime);
8154
8131
  };
8132
+ Object.defineProperty(CommentComponent.prototype, "isHighlighted", {
8133
+ get: function () {
8134
+ var _a, _b, _c;
8135
+ return (_c = (_b = (_a = this.message) === null || _a === void 0 ? void 0 : _a.transientState) === null || _b === void 0 ? void 0 : _b.highlighted) !== null && _c !== void 0 ? _c : false;
8136
+ },
8137
+ enumerable: false,
8138
+ configurable: true
8139
+ });
8155
8140
  Object.defineProperty(CommentComponent.prototype, "userSelected", {
8156
8141
  get: function () {
8157
8142
  return this._userSelected.asObservable();
@@ -8180,9 +8165,29 @@
8180
8165
  enumerable: false,
8181
8166
  configurable: true
8182
8167
  });
8183
- Object.defineProperty(CommentComponent.prototype, "upvoted", {
8168
+ CommentComponent.prototype.saveEdit = function () {
8169
+ this._edited.next(this.editedMessage);
8170
+ };
8171
+ CommentComponent.prototype.endEditing = function () {
8172
+ this._editEnded.next();
8173
+ };
8174
+ CommentComponent.prototype.startEdit = function () {
8175
+ this._editStarted.next();
8176
+ this.editedMessage = this.message.message;
8177
+ };
8178
+ CommentComponent.prototype.delete = function () {
8179
+ this._deleted.next();
8180
+ };
8181
+ Object.defineProperty(CommentComponent.prototype, "liked", {
8182
+ get: function () {
8183
+ return this._liked.asObservable();
8184
+ },
8185
+ enumerable: false,
8186
+ configurable: true
8187
+ });
8188
+ Object.defineProperty(CommentComponent.prototype, "unliked", {
8184
8189
  get: function () {
8185
- return this._upvoted.asObservable();
8190
+ return this._unliked.asObservable();
8186
8191
  },
8187
8192
  enumerable: false,
8188
8193
  configurable: true
@@ -8194,26 +8199,44 @@
8194
8199
  enumerable: false,
8195
8200
  configurable: true
8196
8201
  });
8197
- Object.defineProperty(CommentComponent.prototype, "commentId", {
8198
- get: function () {
8199
- var _a;
8200
- return (_a = this.message) === null || _a === void 0 ? void 0 : _a.id;
8201
- },
8202
+ Object.defineProperty(CommentComponent.prototype, "edited", {
8203
+ get: function () { return this._edited.asObservable(); },
8204
+ enumerable: false,
8205
+ configurable: true
8206
+ });
8207
+ Object.defineProperty(CommentComponent.prototype, "deleted", {
8208
+ get: function () { return this._deleted.asObservable(); },
8209
+ enumerable: false,
8210
+ configurable: true
8211
+ });
8212
+ Object.defineProperty(CommentComponent.prototype, "editStarted", {
8213
+ get: function () { return this._editStarted.asObservable(); },
8214
+ enumerable: false,
8215
+ configurable: true
8216
+ });
8217
+ Object.defineProperty(CommentComponent.prototype, "editEnded", {
8218
+ get: function () { return this._editEnded.asObservable(); },
8202
8219
  enumerable: false,
8203
8220
  configurable: true
8204
8221
  });
8205
8222
  Object.defineProperty(CommentComponent.prototype, "shared", {
8206
- get: function () {
8207
- return this._shared.asObservable();
8208
- },
8223
+ get: function () { return this._shared.asObservable(); },
8224
+ enumerable: false,
8225
+ configurable: true
8226
+ });
8227
+ Object.defineProperty(CommentComponent.prototype, "commentId", {
8228
+ get: function () { var _a; return (_a = this.message) === null || _a === void 0 ? void 0 : _a.id; },
8209
8229
  enumerable: false,
8210
8230
  configurable: true
8211
8231
  });
8212
8232
  CommentComponent.prototype.report = function () {
8213
8233
  this._reported.next();
8214
8234
  };
8215
- CommentComponent.prototype.upvote = function () {
8216
- this._upvoted.next();
8235
+ CommentComponent.prototype.like = function () {
8236
+ this._liked.next();
8237
+ };
8238
+ CommentComponent.prototype.unlike = function () {
8239
+ this._unliked.next();
8217
8240
  };
8218
8241
  CommentComponent.prototype.share = function () {
8219
8242
  this._shared.next(this.message);
@@ -8244,12 +8267,13 @@
8244
8267
  CommentComponent.decorators = [
8245
8268
  { type: core.Component, args: [{
8246
8269
  selector: 'banta-comment',
8247
- template: "\r\n<mat-menu #pointItemMenu=\"matMenu\">\r\n <button mat-menu-item (click)=\"report()\">Report</button>\r\n <button mat-menu-item>Help</button>\r\n</mat-menu>\r\n\r\n<div class=\"message-content\">\r\n <div class=\"user\">\r\n <a\r\n href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"selectAvatar(message.user)\"\r\n [style.background-image]=\"avatarForUser(message.user)\"></a>\r\n <a href=\"javascript:;\" class=\"display-name\" (click)=\"selectUser()\">{{message.user.displayName}}</a>\r\n <a href=\"javascript:;\" class=\"username\" (click)=\"selectUsername(message.user)\">@{{message.user.username}}</a>\r\n </div>\r\n <div class=\"content\">\r\n {{message.message}}\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <banta-timestamp [value]=\"message.sentAt\"></banta-timestamp>\r\n <div class=\"spacer\"></div>\r\n <div class=\"counted-action\" *ngIf=\"showReplyAction\">\r\n <div class=\"count-indicator\">\r\n {{message.submessages?.length || message.submessageCount || 0}}\r\n </div>\r\n <button mat-icon-button matTooltip=\"Comment\" matTooltipPosition=\"below\" (click)=\"select()\">\r\n <mat-icon [inline]=\"true\">comment</mat-icon>\r\n </button>\r\n </div>\r\n <div class=\"counted-action\">\r\n <div class=\"count-indicator\">\r\n {{message.upvotes}}\r\n </div>\r\n <button mat-icon-button [matTooltip]=\"upvoting ? 'Please wait...' : 'Like'\" matTooltipPosition=\"below\" (click)=\"upvote()\">\r\n\r\n <mat-spinner *ngIf=\"upvoting\" [diameter]=\"15\" style=\"margin-left: 1em;\"></mat-spinner>\r\n <mat-icon *ngIf=\"!upvoting\" [inline]=\"true\">thumb_up</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"counted-action\">\r\n <button mat-icon-button matTooltip=\"Share this comment\" matTooltipPosition=\"below\" (click)=\"share()\">\r\n <mat-icon [inline]=\"true\" >share</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button mat-icon-button [matMenuTriggerFor]=\"pointItemMenu\">\r\n <mat-icon [inline]=\"true\">more_vert</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n",
8248
- styles: ["@-webkit-keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}@keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}:host{display:flex;flex-direction:column;position:relative;padding:.5em;visibility:hidden}:host.new{-webkit-animation-name:comment-appear;animation-name:comment-appear;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}:host.new,:host.visible{visibility:visible}:host:hover{background:#eee}:host .message-content .content{margin-left:60px;margin-right:.5em}:host.abbreviated .message-content .content{max-height:8.5em;text-overflow:ellipsis;overflow-y:hidden}:host .actions{display:flex;padding-right:10px;margin-left:60px;align-items:center}:host .actions button{color:#666}:host .actions banta-timestamp{color:#666;font-size:10pt}.user{position:relative;margin:1em 0 0;display:flex;align-items:center}.user .display-name,.user .username{z-index:1;position:relative;padding:0 0 0 1em;font-size:10pt;color:#000;margin:0 auto 0 0;display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex-shrink:1;flex-grow:0}.user .display-name.username,.user .username.username{color:#666;flex-shrink:0;flex-grow:1}.avatar{height:48px;width:48px;background-position:50%;background-size:cover;background-color:#333;border-radius:100%;flex-shrink:0;flex-grow:0}.counted-action{display:flex;align-items:center}.count-indicator{font-size:9pt;padding:0 0 0 3px;color:#666}:host-context(.mat-dark-theme) .count-indicator{border-color:#333}:host-context(.mat-dark-theme):hover{background:#060606}:host-context(.mat-dark-theme) .user .display-name,:host-context(.mat-dark-theme) .user .username{color:#fff}@media (max-width:400px){.avatar{height:32px;width:32px}:host .actions{margin-left:44px}:host .message-content .content{margin-left:44px;margin-right:.5em}}"]
8270
+ template: "\r\n<mat-menu #pointItemMenu=\"matMenu\">\r\n <button *ngIf=\"!mine\" mat-menu-item (click)=\"report()\">Report</button>\r\n <button *ngIf=\"mine\" [disabled]=\"!permissions?.canEdit\" mat-menu-item (click)=\"startEdit()\">Edit</button>\r\n <button *ngIf=\"mine\" [disabled]=\"!permissions?.canDelete\" mat-menu-item (click)=\"delete()\">Delete</button>\r\n</mat-menu>\r\n\r\n<div class=\"message-content\">\r\n <div class=\"user\">\r\n <a\r\n href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"selectAvatar(message.user)\"\r\n [style.background-image]=\"avatarForUser(message.user)\"></a>\r\n <a href=\"javascript:;\" class=\"display-name\" (click)=\"selectUser()\">{{message.user.displayName}}</a>\r\n <a href=\"javascript:;\" class=\"username\" (click)=\"selectUsername(message.user)\">@{{message.user.username}}</a>\r\n <span class=\"user-tag\" *ngIf=\"message.user.tag\">{{message.user.tag}}</span>\r\n <span class=\"spacer\"></span>\r\n </div>\r\n <div class=\"content\" *ngIf=\"!editing\">\r\n {{message.message}}\r\n </div>\r\n <div class=\"content\" *ngIf=\"editing\" style=\"padding-bottom: 2em;\">\r\n <div>\r\n <mat-form-field floatLabel=\"always\" appearance=\"outline\" style=\"width: 100%;\">\r\n <mat-label>Edit Message</mat-label>\r\n <textarea matInput [(ngModel)]=\"editedMessage\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n <button mat-raised-button (click)=\"saveEdit()\">Save</button> &nbsp;\r\n <button mat-button (click)=\"endEditing()\">Cancel</button>\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <banta-timestamp [value]=\"message.sentAt\"></banta-timestamp>\r\n <ul class=\"message-facts\">\r\n <li *ngIf=\"message.edits?.length > 0\">Edited</li>\r\n </ul>\r\n <div class=\"spacer\"></div>\r\n <div class=\"counted-action\" *ngIf=\"showReplyAction\">\r\n <div class=\"count-indicator\">\r\n {{message.submessages?.length || message.submessageCount || 0}}\r\n </div>\r\n <button mat-icon-button matTooltip=\"Comment\" matTooltipPosition=\"below\" (click)=\"select()\">\r\n <mat-icon [inline]=\"true\">comment</mat-icon>\r\n </button>\r\n </div>\r\n <div class=\"counted-action\" [class.active]=\"message.userState?.liked\">\r\n <div class=\"count-indicator\">\r\n {{message.likes}}\r\n </div>\r\n <button \r\n *ngIf=\"message.transientState?.liking\"\r\n mat-icon-button \r\n [disabled]=\"true\" \r\n [matTooltip]=\"upvoting ? 'Please wait...' : message.userState?.liked ? 'Unlike' : 'Like'\" \r\n matTooltipPosition=\"below\" \r\n >\r\n <mat-spinner [diameter]=\"15\" style=\"margin-left: 1em;\"></mat-spinner>\r\n </button>\r\n <button \r\n *ngIf=\"!message.transientState?.liking\"\r\n mat-icon-button \r\n [disabled]=\"!permissions?.canLike\" \r\n [matTooltip]=\"upvoting ? 'Please wait...' : 'Like'\" \r\n matTooltipPosition=\"below\" \r\n (click)=\"message.userState?.liked ? unlike() : like()\" \r\n >\r\n <mat-icon [inline]=\"true\">thumb_up</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"counted-action\">\r\n <button mat-icon-button matTooltip=\"Share this comment\" matTooltipPosition=\"below\" (click)=\"share()\">\r\n <mat-icon [inline]=\"true\" >share</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button mat-icon-button [matMenuTriggerFor]=\"pointItemMenu\">\r\n <mat-icon [inline]=\"true\">more_vert</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n",
8271
+ styles: ["@-webkit-keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}@keyframes comment-appear{0%{transform:translate(100vw)}to{transform:translate(0)}}:host{display:flex;flex-direction:column;position:relative;padding:.5em;visibility:hidden}:host.new{visibility:visible;-webkit-animation-name:comment-appear;animation-name:comment-appear;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}:host.highlighted{background:#00223a;outline:2px solid #003277}:host.visible{visibility:visible}:host:hover{background:#eee}:host .message-content .content{margin-left:60px;margin-right:.5em}:host.abbreviated .message-content .content{max-height:8.5em;text-overflow:ellipsis;overflow-y:hidden}:host .actions{display:flex;padding-right:10px;margin-left:60px;align-items:center}:host .actions button{color:#666}:host .actions banta-timestamp{color:#666;font-size:10pt}.user{position:relative;margin:1em 0 0;display:flex;align-items:center}.user .display-name,.user .username{z-index:1;position:relative;padding:0 0 0 1em;font-size:10pt;color:#000;margin:0 auto 0 0;display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex-shrink:1;flex-grow:0}.user .display-name.username.username.username,.user .username.username.username.username{color:#666}.avatar{height:48px;width:48px;background-position:50%;background-size:cover;background-color:#333;border-radius:100%;flex-shrink:0;flex-grow:0}.counted-action{display:flex;align-items:center}.counted-action.active .count-indicator,.counted-action.active button{color:#00a5ff}.count-indicator{font-size:9pt;padding:0 0 0 3px;color:#666}:host-context(.mat-dark-theme) .count-indicator{border-color:#333}:host-context(.mat-dark-theme):hover{background:#060606}:host-context(.mat-dark-theme) .user .display-name,:host-context(.mat-dark-theme) .user .username{color:#fff}@media (max-width:400px){.avatar{height:32px;width:32px}:host .actions{margin-left:44px}:host .message-content .content{margin-left:44px;margin-right:.5em}}.user-tag{text-transform:uppercase;font-size:12px;border:1px solid #b27373;background:#7a412b;color:#fff;padding:3px 5px;margin:0 .5em 0 1em;border-radius:3px}.spacer{flex-shrink:1;flex-grow:1}ul.message-facts{margin:0;padding:0;color:#666}ul.message-facts li{list-style-type:none;border-left:1px solid #666;font-size:10pt;padding-left:.5em;margin-left:.5em}"]
8249
8272
  },] }
8250
8273
  ];
8251
8274
  CommentComponent.propDecorators = {
8252
8275
  isNew: [{ type: core.HostBinding, args: ['class.new',] }],
8276
+ isHighlighted: [{ type: core.HostBinding, args: ['class.highlighted',] }],
8253
8277
  visible: [{ type: core.HostBinding, args: ['class.visible',] }],
8254
8278
  message: [{ type: core.Input }],
8255
8279
  showReplyAction: [{ type: core.Input }],
@@ -8257,11 +8281,18 @@
8257
8281
  usernameSelected: [{ type: core.Output }],
8258
8282
  avatarSelected: [{ type: core.Output }],
8259
8283
  reported: [{ type: core.Output }],
8260
- upvoting: [{ type: core.Input }],
8261
- upvoted: [{ type: core.Output }],
8284
+ permissions: [{ type: core.Input }],
8285
+ mine: [{ type: core.Input }],
8286
+ editing: [{ type: core.Input }],
8287
+ liked: [{ type: core.Output }],
8288
+ unliked: [{ type: core.Output }],
8262
8289
  selected: [{ type: core.Output }],
8263
- commentId: [{ type: core.HostBinding, args: ['attr.data-comment-id',] }],
8264
- shared: [{ type: core.Output }]
8290
+ edited: [{ type: core.Output }],
8291
+ deleted: [{ type: core.Output }],
8292
+ editStarted: [{ type: core.Output }],
8293
+ editEnded: [{ type: core.Output }],
8294
+ shared: [{ type: core.Output }],
8295
+ commentId: [{ type: core.HostBinding, args: ['attr.data-comment-id',] }]
8265
8296
  };
8266
8297
 
8267
8298
  var CommentViewComponent = /** @class */ (function () {
@@ -8269,16 +8300,20 @@
8269
8300
  this.backend = backend;
8270
8301
  this._sourceSubs = new rxjs.Subscription();
8271
8302
  this._selected = new rxjs.Subject();
8272
- this._upvoted = new rxjs.Subject();
8303
+ this._liked = new rxjs.Subject();
8304
+ this._unliked = new rxjs.Subject();
8273
8305
  this._reported = new rxjs.Subject();
8274
8306
  this._userSelected = new rxjs.Subject();
8275
8307
  this._usernameSelected = new rxjs.Subject();
8276
8308
  this._avatarSelected = new rxjs.Subject();
8277
8309
  this._shared = new rxjs.Subject();
8310
+ this._deleted = new rxjs.Subject();
8311
+ this._messageEdited = new rxjs.Subject();
8278
8312
  this.showEmptyState = true;
8279
8313
  this.allowReplies = true;
8280
8314
  this.menuMessage = null;
8281
8315
  this.messages = [];
8316
+ this.customSortEnabled = false;
8282
8317
  this.maxMessages = 2000;
8283
8318
  this.maxVisibleMessages = 200;
8284
8319
  this.newestLast = false;
@@ -8287,6 +8322,7 @@
8287
8322
  this.hasMore = false;
8288
8323
  this.newMessages = [];
8289
8324
  this.olderMessages = [];
8325
+ this.sortOrderChanged = new rxjs.Subject();
8290
8326
  }
8291
8327
  Object.defineProperty(CommentViewComponent.prototype, "selected", {
8292
8328
  get: function () {
@@ -8295,45 +8331,53 @@
8295
8331
  enumerable: false,
8296
8332
  configurable: true
8297
8333
  });
8298
- Object.defineProperty(CommentViewComponent.prototype, "userSelected", {
8334
+ Object.defineProperty(CommentViewComponent.prototype, "messageEdited", {
8299
8335
  get: function () {
8300
- return this._userSelected;
8336
+ return this._messageEdited.asObservable();
8301
8337
  },
8302
8338
  enumerable: false,
8303
8339
  configurable: true
8304
8340
  });
8341
+ CommentViewComponent.prototype.saveEdit = function (message, newMessage) {
8342
+ this._messageEdited.next({ message: message, newMessage: newMessage });
8343
+ };
8344
+ Object.defineProperty(CommentViewComponent.prototype, "userSelected", {
8345
+ get: function () { return this._userSelected; },
8346
+ enumerable: false,
8347
+ configurable: true
8348
+ });
8305
8349
  Object.defineProperty(CommentViewComponent.prototype, "reported", {
8306
- get: function () {
8307
- return this._reported;
8308
- },
8350
+ get: function () { return this._reported; },
8309
8351
  enumerable: false,
8310
8352
  configurable: true
8311
8353
  });
8312
- Object.defineProperty(CommentViewComponent.prototype, "upvoted", {
8313
- get: function () {
8314
- return this._upvoted;
8315
- },
8354
+ Object.defineProperty(CommentViewComponent.prototype, "liked", {
8355
+ get: function () { return this._liked; },
8356
+ enumerable: false,
8357
+ configurable: true
8358
+ });
8359
+ Object.defineProperty(CommentViewComponent.prototype, "unliked", {
8360
+ get: function () { return this._unliked; },
8316
8361
  enumerable: false,
8317
8362
  configurable: true
8318
8363
  });
8319
8364
  Object.defineProperty(CommentViewComponent.prototype, "usernameSelected", {
8320
- get: function () {
8321
- return this._usernameSelected;
8322
- },
8365
+ get: function () { return this._usernameSelected; },
8323
8366
  enumerable: false,
8324
8367
  configurable: true
8325
8368
  });
8326
8369
  Object.defineProperty(CommentViewComponent.prototype, "avatarSelected", {
8327
- get: function () {
8328
- return this._avatarSelected;
8329
- },
8370
+ get: function () { return this._avatarSelected; },
8330
8371
  enumerable: false,
8331
8372
  configurable: true
8332
8373
  });
8333
8374
  Object.defineProperty(CommentViewComponent.prototype, "shared", {
8334
- get: function () {
8335
- return this._shared;
8336
- },
8375
+ get: function () { return this._shared; },
8376
+ enumerable: false,
8377
+ configurable: true
8378
+ });
8379
+ Object.defineProperty(CommentViewComponent.prototype, "deleted", {
8380
+ get: function () { return this._deleted; },
8337
8381
  enumerable: false,
8338
8382
  configurable: true
8339
8383
  });
@@ -8343,30 +8387,35 @@
8343
8387
  },
8344
8388
  set: function (value) {
8345
8389
  var _this = this;
8390
+ this.customSortEnabled = (value === null || value === void 0 ? void 0 : value.sortOrder) !== common$1.CommentsOrder.NEWEST;
8391
+ this.newMessages = [];
8392
+ this.olderMessages = [];
8393
+ window.bantaSourceDebug = value;
8346
8394
  if (this._sourceSubs) {
8347
8395
  this._sourceSubs.unsubscribe();
8348
8396
  this._sourceSubs = null;
8349
8397
  }
8350
8398
  this._source = value;
8351
8399
  if (value) {
8352
- console.log("[banta-comment-view] Subscribing to source...");
8353
8400
  var messages = (value.messages || []).slice();
8354
8401
  this.messages = messages;
8355
8402
  this.olderMessages = messages.splice(this.maxVisibleMessages, messages.length);
8356
- this.hasMore = this.olderMessages.length > 0;
8403
+ this.hasMore = true; //this.olderMessages.length > 0;
8357
8404
  this._sourceSubs = new rxjs.Subscription();
8358
8405
  this._sourceSubs.add(this._source.messageReceived.subscribe(function (msg) { return _this.messageReceived(msg); }));
8359
8406
  this._sourceSubs.add(this._source.messageSent.subscribe(function (msg) { return _this.messageSent(msg); }));
8360
- if (this._source.currentUserChanged) {
8361
- this._sourceSubs.add(this._source.currentUserChanged.subscribe(function (user) { return _this.currentUser = user; }));
8362
- }
8407
+ this._sourceSubs.add(this.backend.userChanged.subscribe(function (user) { return _this.currentUser = user; }));
8408
+ this.getInitialMessages();
8363
8409
  }
8364
8410
  },
8365
8411
  enumerable: false,
8366
8412
  configurable: true
8367
8413
  });
8368
- CommentViewComponent.prototype.upvoteMessage = function (message) {
8369
- this._upvoted.next(message);
8414
+ CommentViewComponent.prototype.likeMessage = function (message) {
8415
+ this._liked.next(message);
8416
+ };
8417
+ CommentViewComponent.prototype.unlikeMessage = function (message) {
8418
+ this._unliked.next(message);
8370
8419
  };
8371
8420
  CommentViewComponent.prototype.reportMessage = function (message) {
8372
8421
  this._reported.next(message);
@@ -8386,13 +8435,39 @@
8386
8435
  CommentViewComponent.prototype.sharedMessage = function (message) {
8387
8436
  this._shared.next(message);
8388
8437
  };
8438
+ CommentViewComponent.prototype.startEditing = function (message) {
8439
+ this.messages.forEach(function (m) { return m.transientState.editing = false; });
8440
+ message.transientState.editing = true;
8441
+ };
8442
+ CommentViewComponent.prototype.deleteMessage = function (message) {
8443
+ this._deleted.next(message);
8444
+ };
8445
+ CommentViewComponent.prototype.getInitialMessages = function () {
8446
+ return __awaiter(this, void 0, void 0, function () {
8447
+ var messages;
8448
+ return __generator(this, function (_b) {
8449
+ switch (_b.label) {
8450
+ case 0: return [4 /*yield*/, this._source.getExistingMessages()];
8451
+ case 1:
8452
+ messages = (_b.sent());
8453
+ messages.forEach(function (m) { var _a; return (_a = m.transientState) !== null && _a !== void 0 ? _a : (m.transientState = {}); });
8454
+ this.messages = messages;
8455
+ return [2 /*return*/];
8456
+ }
8457
+ });
8458
+ });
8459
+ };
8389
8460
  CommentViewComponent.prototype.messageIdentity = function (index, chatMessage) {
8390
8461
  return chatMessage.id;
8391
8462
  };
8392
8463
  CommentViewComponent.prototype.showNew = function () {
8393
8464
  return __awaiter(this, void 0, void 0, function () {
8394
8465
  var overflow;
8395
- return __generator(this, function (_a) {
8466
+ return __generator(this, function (_b) {
8467
+ if (this.source && this.source.sortOrder !== common$1.CommentsOrder.NEWEST) {
8468
+ this.sortOrderChanged.next(common$1.CommentsOrder.NEWEST);
8469
+ return [2 /*return*/];
8470
+ }
8396
8471
  this.isViewingMore = false;
8397
8472
  this.messages = this.newMessages.splice(0, this.newMessages.length).concat(this.messages);
8398
8473
  overflow = this.messages.splice(this.maxVisibleMessages, this.messages.length);
@@ -8404,43 +8479,53 @@
8404
8479
  };
8405
8480
  CommentViewComponent.prototype.showMore = function () {
8406
8481
  return __awaiter(this, void 0, void 0, function () {
8407
- var lastMessage, messages;
8408
- return __generator(this, function (_a) {
8409
- switch (_a.label) {
8482
+ var nextPageSize, lastMessage, messages;
8483
+ return __generator(this, function (_b) {
8484
+ switch (_b.label) {
8410
8485
  case 0:
8411
8486
  this.isViewingMore = true;
8412
8487
  if (!(this.olderMessages.length > 0)) return [3 /*break*/, 1];
8413
8488
  this.isLoadingMore = false;
8414
8489
  this.messages = this.messages.concat(this.olderMessages.splice(0, 50));
8415
- return [3 /*break*/, 4];
8490
+ return [3 /*break*/, 3];
8416
8491
  case 1:
8417
- if (!this.source.loadAfter) return [3 /*break*/, 3];
8418
8492
  this.isLoadingMore = true;
8493
+ nextPageSize = 20;
8494
+ lastMessage = void 0;
8419
8495
  lastMessage = this.messages[this.messages.length - 1];
8420
- return [4 /*yield*/, this.source.loadAfter(lastMessage, 100)];
8496
+ if (!lastMessage) {
8497
+ this.isLoadingMore = false;
8498
+ this.hasMore = false;
8499
+ return [2 /*return*/];
8500
+ }
8501
+ return [4 /*yield*/, this.source.loadAfter(lastMessage, nextPageSize)];
8421
8502
  case 2:
8422
- messages = _a.sent();
8503
+ messages = _b.sent();
8504
+ messages.forEach(function (m) { var _a; return (_a = m.transientState) !== null && _a !== void 0 ? _a : (m.transientState = {}); });
8423
8505
  this.messages = this.messages.concat(messages);
8424
8506
  this.isLoadingMore = false;
8425
- if (messages.length === 0)
8507
+ if (messages.length === 0) {
8508
+ console.log("Reached the end of the list.");
8426
8509
  this.hasMore = false;
8427
- return [3 /*break*/, 4];
8428
- case 3:
8429
- this.hasMore = false;
8430
- _a.label = 4;
8431
- case 4: return [2 /*return*/];
8510
+ }
8511
+ _b.label = 3;
8512
+ case 3: return [2 /*return*/];
8432
8513
  }
8433
8514
  });
8434
8515
  });
8435
8516
  };
8436
8517
  CommentViewComponent.prototype.addMessage = function (message) {
8518
+ var _a;
8519
+ if (!message.transientState)
8520
+ (_a = message.transientState) !== null && _a !== void 0 ? _a : (message.transientState = {});
8437
8521
  var destination = this.messages;
8438
8522
  var bucket = this.olderMessages;
8439
8523
  if (this.isViewingMore) {
8440
8524
  destination = this.newMessages;
8441
8525
  bucket = null;
8442
8526
  }
8443
- if (this.newestLast) {
8527
+ var newestLast = this.newestLast;
8528
+ if (newestLast) {
8444
8529
  destination.push(message);
8445
8530
  var overflow = destination.splice(this.maxVisibleMessages, destination.length);
8446
8531
  bucket === null || bucket === void 0 ? void 0 : bucket.push.apply(bucket, __spread(overflow));
@@ -8497,41 +8582,63 @@
8497
8582
  CommentViewComponent.decorators = [
8498
8583
  { type: core.Component, args: [{
8499
8584
  selector: 'banta-comment-view',
8500
- template: "<div class=\"message-container\">\r\n <ng-content select=\"[data-before]\"></ng-content>\r\n\r\n <a mat-button class=\"nav\" [class.visible]=\"isViewingMore\" href=\"javascript:;\" (click)=\"showNew()\">\r\n <mat-icon>file_upload</mat-icon>\r\n New\r\n <ng-container *ngIf=\"newMessages.length >= 1\">\r\n ({{newMessages.length}})\r\n </ng-container>\r\n </a>\r\n\r\n <ng-container *ngIf=\"messages.length === 0\">\r\n <div class=\"empty-state\" *ngIf=\"showEmptyState\">\r\n Be the first to comment!\r\n </div>\r\n </ng-container>\r\n <ng-container *ngFor=\"let message of messages; trackBy: messageIdentity\">\r\n <banta-comment\r\n *ngIf=\"!message.hidden\"\r\n class=\"abbreviated\"\r\n [message]=\"message\"\r\n [upvoting]=\"message['$upvoting']\"\r\n (click)=\"isViewingMore = true\"\r\n [showReplyAction]=\"allowReplies\"\r\n (userSelected)=\"selectMessageUser(message)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (upvoted)=\"upvoteMessage(message)\"\r\n (reported)=\"reportMessage(message)\"\r\n (selected)=\"selectMessage(message)\"\r\n (shared)=\"sharedMessage($event)\"\r\n ></banta-comment>\r\n </ng-container>\r\n\r\n <a mat-button class=\"nav\" [class.visible]=\"hasMore && !isLoadingMore\" href=\"javascript:;\" (click)=\"showMore()\">Show more</a>\r\n\r\n <div class=\"loading-more\" *ngIf=\"isLoadingMore\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n\r\n <!-- <div style=\"color: #666\">\r\n n={{newMessages.length}}, m={{messages.length}}, o={{olderMessages.length}},\r\n v={{maxVisibleMessages}}, M={{maxMessages}}\r\n </div> -->\r\n\r\n <ng-content select=\":not([data-before])\"></ng-content>\r\n</div>\r\n",
8585
+ template: "<div class=\"message-container\">\r\n <ng-content select=\"[data-before]\"></ng-content>\r\n\r\n <a mat-button class=\"nav\" [class.visible]=\"isViewingMore || customSortEnabled\" href=\"javascript:;\" (click)=\"showNew()\">\r\n <mat-icon>file_upload</mat-icon>\r\n New\r\n <ng-container *ngIf=\"newMessages.length >= 1\">\r\n ({{newMessages.length}})\r\n </ng-container>\r\n </a>\r\n\r\n <ng-container *ngIf=\"messages.length === 0\">\r\n <div class=\"empty-state\" *ngIf=\"showEmptyState\">\r\n Be the first to comment!\r\n </div>\r\n </ng-container>\r\n <ng-container *ngFor=\"let message of messages; trackBy: messageIdentity\">\r\n <banta-comment\r\n *ngIf=\"!message.hidden\"\r\n class=\"abbreviated\"\r\n \r\n [message]=\"message\"\r\n [mine]=\"currentUser?.id === message.user?.id\"\r\n [permissions]=\"source?.permissions\"\r\n (click)=\"isViewingMore = true\"\r\n [showReplyAction]=\"allowReplies\"\r\n [editing]=\"message.transientState.editing\"\r\n (editStarted)=\"startEditing(message)\"\r\n (deleted)=\"deleteMessage(message)\"\r\n (editEnded)=\"message.transientState.editing = false\"\r\n (edited)=\"saveEdit(message, $event)\"\r\n (userSelected)=\"selectMessageUser(message)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (liked)=\"likeMessage(message)\"\r\n (unliked)=\"unlikeMessage(message)\"\r\n (reported)=\"reportMessage(message)\"\r\n (selected)=\"selectMessage(message)\"\r\n (shared)=\"sharedMessage($event)\"\r\n ></banta-comment>\r\n </ng-container>\r\n\r\n <a mat-button class=\"nav\" [class.visible]=\"hasMore && !isLoadingMore\" href=\"javascript:;\" (click)=\"showMore()\">Show more</a>\r\n\r\n <div class=\"loading-more\" *ngIf=\"isLoadingMore\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n\r\n <!-- <div style=\"color: #666\">\r\n n={{newMessages.length}}, m={{messages.length}}, o={{olderMessages.length}},\r\n v={{maxVisibleMessages}}, M={{maxMessages}}\r\n </div> -->\r\n\r\n <ng-content select=\":not([data-before])\"></ng-content>\r\n</div>\r\n",
8501
8586
  styles: [":host{flex-grow:1;display:flex;flex-direction:column;opacity:1;transition:opacity .2s ease-in}.message-container{flex-grow:1;overflow-x:hidden;color:#111;background:#fff;padding:.5em 1em 3em .5em;opacity:1;transition:opacity .5s ease-in-out;position:relative}.message-container.no-scroll{height:auto;overflow-y:visible}.message-container.faded{opacity:.25}.message-container .overlay{position:absolute;top:0;left:0;right:0;bottom:0;z-index:10}:host.fixed-height .message-container{overflow-y:auto}:host-context(.mat-dark-theme) .message-container{color:#fff;background:#111}.empty-state{text-align:center;margin:3em;color:#666}:host-context(.mat-dark-theme) .empty-state{color:#666}a.nav{position:absolute;right:.5em;z-index:10;text-align:center;opacity:0;transition:opacity .4s ease-in-out;pointer-events:none;border-radius:2em;background:#222}a.nav.visible{opacity:1;pointer-events:auto}.loading-more{padding:2em;text-align:center;margin:0 auto;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}@media (max-width:400px){.message-container{padding:0 0 3em}}"]
8502
8587
  },] }
8503
8588
  ];
8504
8589
  CommentViewComponent.ctorParameters = function () { return [
8505
- { type: ChatBackendService }
8590
+ { type: ChatBackendBase }
8506
8591
  ]; };
8507
8592
  CommentViewComponent.propDecorators = {
8508
8593
  showEmptyState: [{ type: core.Input }],
8509
8594
  allowReplies: [{ type: core.Input }],
8510
8595
  fixedHeight: [{ type: core.Input }, { type: core.HostBinding, args: ['class.fixed-height',] }],
8511
8596
  selected: [{ type: core.Output }],
8597
+ messageEdited: [{ type: core.Output }],
8512
8598
  userSelected: [{ type: core.Output }],
8513
8599
  reported: [{ type: core.Output }],
8514
- upvoted: [{ type: core.Output }],
8600
+ liked: [{ type: core.Output }],
8601
+ unliked: [{ type: core.Output }],
8515
8602
  usernameSelected: [{ type: core.Output }],
8516
8603
  avatarSelected: [{ type: core.Output }],
8517
8604
  shared: [{ type: core.Output }],
8605
+ deleted: [{ type: core.Output }],
8518
8606
  source: [{ type: core.Input }],
8519
8607
  genericAvatarUrl: [{ type: core.Input }],
8520
8608
  messageContainer: [{ type: core.ViewChild, args: ['messageContainer',] }],
8521
8609
  maxMessages: [{ type: core.Input }],
8522
8610
  maxVisibleMessages: [{ type: core.Input }],
8523
- newestLast: [{ type: core.Input }]
8611
+ newestLast: [{ type: core.Input }],
8612
+ sortOrderChanged: [{ type: core.Output }]
8524
8613
  };
8525
8614
 
8526
8615
  /**
8527
8616
  * Comments component
8528
8617
  */
8529
8618
  var BantaCommentsComponent = /** @class */ (function () {
8530
- function BantaCommentsComponent(banta, backend, elementRef, activatedRoute) {
8531
- this.banta = banta;
8619
+ function BantaCommentsComponent(backend, elementRef, activatedRoute) {
8620
+ var _this = this;
8532
8621
  this.backend = backend;
8533
8622
  this.elementRef = elementRef;
8534
8623
  this.activatedRoute = activatedRoute;
8624
+ // Loading Screen
8625
+ this._loadingMessage = '';
8626
+ this.loadingMessageVisible = false;
8627
+ this.loading = true;
8628
+ this.showLoadingScreen = false;
8629
+ this._loadingMessageIndex = 0;
8630
+ this.loadingMessages = [
8631
+ "Just a second...",
8632
+ "We're definitely working on it.",
8633
+ "There's no need to refresh.",
8634
+ "It's definitely worth the wait!",
8635
+ "This has never happened before.",
8636
+ "We'll keep trying, but it's not looking great. \n Commenting & chat services may be down. \n If you continue to experience issues, please contact support.\n "
8637
+ ];
8638
+ // Properties
8639
+ this._signInSelected = new rxjs.Subject();
8640
+ this._permissionDeniedError = new rxjs.Subject();
8641
+ this._editAvatarSelected = new rxjs.Subject();
8535
8642
  this._upvoted = new rxjs.Subject();
8536
8643
  this._reported = new rxjs.Subject();
8537
8644
  this._selected = new rxjs.Subject();
@@ -8541,12 +8648,8 @@
8541
8648
  this._avatarSelected = new rxjs.Subject();
8542
8649
  this._subs = new rxjs.Subscription();
8543
8650
  this._sortOrder = common$1.CommentsOrder.NEWEST;
8544
- this.hashtags = [
8545
- { hashtag: 'error', description: 'Cause an error' },
8546
- { hashtag: 'timeout', description: 'Cause a slow timeout error' },
8547
- { hashtag: 'slow', description: 'Be slow when this message is posted' },
8548
- ];
8549
- this.participants = [];
8651
+ this.selectedMessageVisible = false;
8652
+ // Inputs
8550
8653
  this.signInLabel = 'Sign In';
8551
8654
  this.sendLabel = 'Send';
8552
8655
  this.replyLabel = 'Reply';
@@ -8554,97 +8657,131 @@
8554
8657
  this.permissionDeniedLabel = 'Send';
8555
8658
  this.postCommentLabel = 'Post a comment';
8556
8659
  this.postReplyLabel = 'Post a reply';
8557
- this._signInSelected = new rxjs.Subject();
8558
- this._permissionDeniedError = new rxjs.Subject();
8559
- this._editAvatarSelected = new rxjs.Subject();
8560
- this.sending = false;
8561
- this.expandError = false;
8562
- this.selectedMessageVisible = false;
8660
+ this.participants = [];
8661
+ this.hashtags = [
8662
+ { hashtag: 'error', description: 'Cause an error' },
8663
+ { hashtag: 'timeout', description: 'Cause a slow timeout error' },
8664
+ { hashtag: 'slow', description: 'Be slow when this message is posted' },
8665
+ ];
8666
+ this.sendMessage = function (message) { return __awaiter(_this, void 0, void 0, function () {
8667
+ var _a, intercept, e_1;
8668
+ return __generator(this, function (_d) {
8669
+ switch (_d.label) {
8670
+ case 0:
8671
+ _d.trys.push([0, 4, , 5]);
8672
+ return [4 /*yield*/, ((_a = this.shouldInterceptMessageSend) === null || _a === void 0 ? void 0 : _a.call(this, message, this.source))];
8673
+ case 1:
8674
+ intercept = _d.sent();
8675
+ if (!!intercept) return [3 /*break*/, 3];
8676
+ return [4 /*yield*/, this.source.send(message)];
8677
+ case 2:
8678
+ _d.sent();
8679
+ _d.label = 3;
8680
+ case 3:
8681
+ if (this.source.sortOrder !== common$1.CommentsOrder.NEWEST) {
8682
+ this.sortOrder = common$1.CommentsOrder.NEWEST;
8683
+ }
8684
+ return [2 /*return*/, true];
8685
+ case 4:
8686
+ e_1 = _d.sent();
8687
+ this.handleBackendException(e_1, 'Could not send: ');
8688
+ return [3 /*break*/, 5];
8689
+ case 5: return [2 /*return*/];
8690
+ }
8691
+ });
8692
+ }); };
8693
+ this.sendReply = function (message) { return __awaiter(_this, void 0, void 0, function () {
8694
+ var _b, intercept, e_2;
8695
+ return __generator(this, function (_d) {
8696
+ switch (_d.label) {
8697
+ case 0:
8698
+ _d.trys.push([0, 4, , 5]);
8699
+ return [4 /*yield*/, ((_b = this.shouldInterceptMessageSend) === null || _b === void 0 ? void 0 : _b.call(this, message, this.source))];
8700
+ case 1:
8701
+ intercept = _d.sent();
8702
+ if (!!intercept) return [3 /*break*/, 3];
8703
+ return [4 /*yield*/, this.selectedMessageThread.send(message)];
8704
+ case 2:
8705
+ _d.sent();
8706
+ _d.label = 3;
8707
+ case 3: return [2 /*return*/, true];
8708
+ case 4:
8709
+ e_2 = _d.sent();
8710
+ this.handleBackendException(e_2, 'Could not send reply: ');
8711
+ return [3 /*break*/, 5];
8712
+ case 5: return [2 /*return*/];
8713
+ }
8714
+ });
8715
+ }); };
8563
8716
  }
8564
- Object.defineProperty(BantaCommentsComponent.prototype, "sortOrder", {
8565
- get: function () {
8566
- return this._sortOrder;
8567
- },
8568
- set: function (value) {
8569
- var _this = this;
8570
- if (this._sortOrder !== value) {
8571
- this._sortOrder = value;
8572
- setTimeout(function () {
8573
- _this.setSourceFromTopicID(_this.topicID);
8574
- });
8717
+ BantaCommentsComponent.prototype.handleBackendExceptionAsAlert = function (e, prefix) {
8718
+ if (prefix === void 0) { prefix = ''; }
8719
+ try {
8720
+ this.handleBackendException(e, prefix);
8721
+ }
8722
+ catch (e) {
8723
+ alert(e.message);
8724
+ }
8725
+ };
8726
+ BantaCommentsComponent.prototype.handleBackendException = function (e, prefix) {
8727
+ if (prefix === void 0) { prefix = ''; }
8728
+ var errorMessage = e.message;
8729
+ if (errorMessage.startsWith('permission-denied|')) {
8730
+ errorMessage = errorMessage.replace(/^permission-denied\|/, '');
8731
+ if (errorMessage.startsWith("app-handle|")) {
8732
+ // If this is an error during authorizeAction on the backend, pass control to the user-provided
8733
+ // permission-denied handler.
8734
+ this.sendPermissionDenied(errorMessage.replace(/^app-handle\|/, ''));
8735
+ return;
8575
8736
  }
8576
- },
8577
- enumerable: false,
8578
- configurable: true
8579
- });
8737
+ }
8738
+ throw new Error("" + prefix + errorMessage);
8739
+ };
8740
+ // Lifecycle Events / Initialization
8580
8741
  BantaCommentsComponent.prototype.ngOnInit = function () {
8581
8742
  var _this = this;
8582
- this._subs.add(this.banta.userChanged.subscribe(function (user) { return _this.user = user; }));
8583
- };
8584
- BantaCommentsComponent.prototype.ngAfterViewInit = function () {
8585
- if (typeof window !== 'undefined')
8586
- this.checkForSharedComment();
8587
- };
8588
- BantaCommentsComponent.prototype.scrollToComment = function (commentId) {
8589
- setTimeout(function () {
8590
- var comment = document.querySelectorAll("[data-comment-id=\"" + commentId + "\"]");
8591
- console.log(comment);
8592
- if (comment.length > 0) {
8593
- // comment.item(0).scroll({behavior: 'smooth'});
8594
- comment.item(0).scrollIntoView();
8743
+ this._subs.add(this.backend.userChanged.subscribe(function (user) { return _this.user = user; }));
8744
+ this.startLoading();
8745
+ if (typeof window !== 'undefined') {
8746
+ var queryString = window.location.search.substring(1);
8747
+ var query = queryString.split('&')
8748
+ .map(function (s) { return s.split('='); })
8749
+ .reduce(function (o, _d) {
8750
+ var _e = __read(_d, 2), k = _e[0], v = _e[1];
8751
+ return (o[k] = v, o);
8752
+ }, {});
8753
+ var commentID = query['comment'];
8754
+ if (commentID) {
8755
+ this.sharedCommentID = commentID;
8595
8756
  }
8596
- }, 1000);
8597
- };
8598
- BantaCommentsComponent.prototype.checkForSharedComment = function () {
8599
- var commentID = this.activatedRoute.snapshot.queryParamMap.get('comment');
8600
- if (commentID)
8601
- this.scrollToComment(commentID);
8757
+ }
8602
8758
  };
8603
8759
  BantaCommentsComponent.prototype.ngOnDestroy = function () {
8604
8760
  this._subs.unsubscribe();
8605
8761
  };
8606
- Object.defineProperty(BantaCommentsComponent.prototype, "source", {
8607
- get: function () {
8608
- return this._source;
8609
- },
8610
- set: function (value) {
8611
- this._source = value;
8612
- },
8613
- enumerable: false,
8614
- configurable: true
8615
- });
8616
- Object.defineProperty(BantaCommentsComponent.prototype, "topicID", {
8617
- get: function () {
8618
- return this._topicID;
8619
- },
8620
- set: function (value) {
8621
- var _this = this;
8622
- if (this._topicID !== value) {
8623
- this._topicID = value;
8624
- setTimeout(function () { return _this.setSourceFromTopicID(value); });
8625
- }
8626
- },
8627
- enumerable: false,
8628
- configurable: true
8629
- });
8630
8762
  BantaCommentsComponent.prototype.setSourceFromTopicID = function (topicID) {
8631
- var _a, _b;
8632
8763
  return __awaiter(this, void 0, void 0, function () {
8633
8764
  var _this = this;
8634
- return __generator(this, function (_c) {
8635
- (_b = (_a = this._source) === null || _a === void 0 ? void 0 : _a.close) === null || _b === void 0 ? void 0 : _b.call(_a);
8636
- this._source = null;
8765
+ return __generator(this, function (_d) {
8766
+ if (this._source) {
8767
+ this._source.close();
8768
+ this._source = null;
8769
+ }
8637
8770
  setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
8638
- var _c;
8771
+ var _d;
8639
8772
  var _this = this;
8640
- return __generator(this, function (_d) {
8641
- switch (_d.label) {
8773
+ return __generator(this, function (_e) {
8774
+ switch (_e.label) {
8642
8775
  case 0:
8643
- _c = this;
8776
+ console.log("[banta-comments] Subscribing to topic source '" + topicID + "'");
8777
+ _d = this;
8644
8778
  return [4 /*yield*/, this.backend.getSourceForTopic(topicID, { sortOrder: this.sortOrder })];
8645
8779
  case 1:
8646
- _c._source = _d.sent();
8647
- console.log("[banta-comments] Subscribing to source for topic '" + topicID + "'");
8780
+ _d._source = _e.sent();
8781
+ if (this.sharedCommentID) {
8782
+ this.navigateToSharedComment(this.sharedCommentID);
8783
+ this.sharedCommentID = null;
8784
+ }
8648
8785
  this._source.messageReceived.subscribe(function (m) { return _this.addParticipant(m); });
8649
8786
  this._source.messageSent.subscribe(function (m) { return _this.addParticipant(m); });
8650
8787
  this._source.messages.forEach(function (m) { return _this.addParticipant(m); });
@@ -8656,168 +8793,329 @@
8656
8793
  });
8657
8794
  });
8658
8795
  };
8659
- BantaCommentsComponent.prototype.addParticipant = function (message) {
8660
- if (!message || !message.user || !message.user.id)
8661
- return;
8662
- var existing = this.participants.find(function (x) { return x.id === message.user.id; });
8663
- if (existing)
8664
- return;
8665
- this.participants.push(message.user);
8666
- };
8667
- BantaCommentsComponent.prototype.showSignIn = function () {
8668
- this._signInSelected.next();
8669
- };
8670
- BantaCommentsComponent.prototype.showEditAvatar = function () {
8671
- this._editAvatarSelected.next();
8672
- };
8673
- Object.defineProperty(BantaCommentsComponent.prototype, "newMessageText", {
8796
+ Object.defineProperty(BantaCommentsComponent.prototype, "loadingMessage", {
8674
8797
  get: function () {
8675
- return this._newMessageText;
8798
+ return this._loadingMessage;
8676
8799
  },
8677
8800
  set: function (value) {
8678
8801
  var _this = this;
8679
- this._newMessageText = value;
8680
- if (this._newMessageText === '' && this.sendError)
8681
- setTimeout(function () { return _this.sendError = null; });
8802
+ this.loadingMessageVisible = false;
8803
+ setTimeout(function () {
8804
+ _this._loadingMessage = value;
8805
+ _this._loadingMessage = value;
8806
+ setTimeout(function () {
8807
+ _this.loadingMessageVisible = true;
8808
+ });
8809
+ }, 500);
8682
8810
  },
8683
8811
  enumerable: false,
8684
8812
  configurable: true
8685
8813
  });
8686
- Object.defineProperty(BantaCommentsComponent.prototype, "signInSelected", {
8687
- get: function () {
8688
- return this._signInSelected;
8814
+ BantaCommentsComponent.prototype.startLoading = function () {
8815
+ return __awaiter(this, void 0, void 0, function () {
8816
+ var _this = this;
8817
+ return __generator(this, function (_d) {
8818
+ switch (_d.label) {
8819
+ case 0:
8820
+ this.loadingStartedAt = this.messageChangedAt = Date.now();
8821
+ if (this.updateLoading())
8822
+ return [2 /*return*/];
8823
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 100); })];
8824
+ case 1:
8825
+ _d.sent();
8826
+ if (this.updateLoading())
8827
+ return [2 /*return*/];
8828
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 250); })];
8829
+ case 2:
8830
+ _d.sent();
8831
+ if (this.updateLoading())
8832
+ return [2 /*return*/];
8833
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 500); })];
8834
+ case 3:
8835
+ _d.sent();
8836
+ if (this.updateLoading())
8837
+ return [2 /*return*/];
8838
+ console.log("[Banta] Loading is taking a long time! Showing loading screen.");
8839
+ this.showLoadingScreen = true;
8840
+ if (typeof window !== 'undefined')
8841
+ this._loadingTimer = setInterval(function () { return _this.updateLoading(); }, 1000);
8842
+ return [2 /*return*/];
8843
+ }
8844
+ });
8845
+ });
8846
+ };
8847
+ BantaCommentsComponent.prototype.updateLoading = function () {
8848
+ var _this = this;
8849
+ var _a, _b;
8850
+ if (((_a = this.source) === null || _a === void 0 ? void 0 : _a.state) && ((_b = this.source) === null || _b === void 0 ? void 0 : _b.state) !== 'connecting') {
8851
+ clearInterval(this._loadingTimer);
8852
+ this.loadingMessage = "Here we go!";
8853
+ setTimeout(function () {
8854
+ _this.loading = false;
8855
+ }, 750);
8856
+ return true;
8857
+ }
8858
+ var messageSwitchTime = 5 * 1000;
8859
+ if (this.messageChangedAt + messageSwitchTime < Date.now()) {
8860
+ if (this.loadingMessages[this._loadingMessageIndex]) {
8861
+ this.loadingMessage = this.loadingMessages[this._loadingMessageIndex++];
8862
+ this.messageChangedAt = Date.now();
8863
+ }
8864
+ }
8865
+ return false;
8866
+ };
8867
+ Object.defineProperty(BantaCommentsComponent.prototype, "source", {
8868
+ get: function () { return this._source; },
8869
+ set: function (value) {
8870
+ this._source = value;
8871
+ if (value && this.sharedCommentID) {
8872
+ this.navigateToSharedComment(this.sharedCommentID);
8873
+ this.sharedCommentID = null;
8874
+ }
8689
8875
  },
8690
8876
  enumerable: false,
8691
8877
  configurable: true
8692
8878
  });
8693
- Object.defineProperty(BantaCommentsComponent.prototype, "editAvatarSelected", {
8694
- get: function () {
8695
- return this._editAvatarSelected;
8879
+ Object.defineProperty(BantaCommentsComponent.prototype, "topicID", {
8880
+ get: function () { return this._topicID; },
8881
+ set: function (value) {
8882
+ var _this = this;
8883
+ if (this._topicID !== value) {
8884
+ this._topicID = value;
8885
+ setTimeout(function () { return _this.setSourceFromTopicID(value); });
8886
+ }
8696
8887
  },
8697
8888
  enumerable: false,
8698
8889
  configurable: true
8699
8890
  });
8700
- Object.defineProperty(BantaCommentsComponent.prototype, "permissionDeniedError", {
8701
- get: function () {
8702
- return this._permissionDeniedError;
8703
- },
8891
+ Object.defineProperty(BantaCommentsComponent.prototype, "signInSelected", {
8892
+ // Outputs
8893
+ get: function () { return this._signInSelected; },
8704
8894
  enumerable: false,
8705
8895
  configurable: true
8706
8896
  });
8707
- BantaCommentsComponent.prototype.showPermissionDenied = function () {
8708
- this._permissionDeniedError.next();
8709
- };
8710
- Object.defineProperty(BantaCommentsComponent.prototype, "canComment", {
8711
- get: function () {
8712
- var _a;
8713
- if (!this.user)
8714
- return false;
8715
- if (!this.user.permissions)
8716
- return true;
8717
- if (!this.user.permissions.canComment)
8718
- return true;
8719
- return (_a = this.user.permissions) === null || _a === void 0 ? void 0 : _a.canComment(this.source);
8720
- },
8897
+ Object.defineProperty(BantaCommentsComponent.prototype, "editAvatarSelected", {
8898
+ get: function () { return this._editAvatarSelected; },
8899
+ enumerable: false,
8900
+ configurable: true
8901
+ });
8902
+ Object.defineProperty(BantaCommentsComponent.prototype, "permissionDeniedError", {
8903
+ get: function () { return this._permissionDeniedError; },
8721
8904
  enumerable: false,
8722
8905
  configurable: true
8723
8906
  });
8724
8907
  Object.defineProperty(BantaCommentsComponent.prototype, "upvoted", {
8725
- get: function () {
8726
- return this._upvoted.asObservable();
8727
- },
8908
+ get: function () { return this._upvoted.asObservable(); },
8728
8909
  enumerable: false,
8729
8910
  configurable: true
8730
8911
  });
8731
8912
  Object.defineProperty(BantaCommentsComponent.prototype, "reported", {
8732
- get: function () {
8733
- return this._reported.asObservable();
8734
- },
8913
+ get: function () { return this._reported.asObservable(); },
8735
8914
  enumerable: false,
8736
8915
  configurable: true
8737
8916
  });
8738
8917
  Object.defineProperty(BantaCommentsComponent.prototype, "selected", {
8739
- get: function () {
8740
- return this._selected.asObservable();
8741
- },
8918
+ get: function () { return this._selected.asObservable(); },
8742
8919
  enumerable: false,
8743
8920
  configurable: true
8744
8921
  });
8745
8922
  Object.defineProperty(BantaCommentsComponent.prototype, "userSelected", {
8746
- get: function () {
8747
- return this._userSelected.asObservable();
8748
- },
8923
+ get: function () { return this._userSelected.asObservable(); },
8749
8924
  enumerable: false,
8750
8925
  configurable: true
8751
8926
  });
8752
8927
  Object.defineProperty(BantaCommentsComponent.prototype, "usernameSelected", {
8753
- get: function () {
8754
- return this._usernameSelected.asObservable();
8755
- },
8928
+ get: function () { return this._usernameSelected.asObservable(); },
8756
8929
  enumerable: false,
8757
8930
  configurable: true
8758
8931
  });
8759
8932
  Object.defineProperty(BantaCommentsComponent.prototype, "avatarSelected", {
8760
- get: function () {
8761
- return this._avatarSelected.asObservable();
8762
- },
8933
+ get: function () { return this._avatarSelected.asObservable(); },
8763
8934
  enumerable: false,
8764
8935
  configurable: true
8765
8936
  });
8766
8937
  Object.defineProperty(BantaCommentsComponent.prototype, "shared", {
8767
- get: function () {
8768
- return this._shared.asObservable();
8938
+ get: function () { return this._shared.asObservable(); },
8939
+ enumerable: false,
8940
+ configurable: true
8941
+ });
8942
+ Object.defineProperty(BantaCommentsComponent.prototype, "sortOrder", {
8943
+ get: function () { return this._sortOrder; },
8944
+ set: function (value) {
8945
+ var _this = this;
8946
+ if (this._sortOrder !== value) {
8947
+ this._sortOrder = value;
8948
+ setTimeout(function () {
8949
+ _this.setSourceFromTopicID(_this.topicID);
8950
+ });
8951
+ }
8769
8952
  },
8770
8953
  enumerable: false,
8771
8954
  configurable: true
8772
8955
  });
8773
- BantaCommentsComponent.prototype.onKeyDown = function (event) {
8956
+ // UI Interactions
8957
+ BantaCommentsComponent.prototype.scrollToComment = function (commentId) {
8958
+ setTimeout(function () {
8959
+ var comment = document.querySelectorAll("[data-comment-id=\"" + commentId + "\"]");
8960
+ if (comment.length > 0) {
8961
+ // comment.item(0).scroll({behavior: 'smooth'});
8962
+ comment.item(0).scrollIntoView();
8963
+ }
8964
+ }, 1000);
8965
+ };
8966
+ BantaCommentsComponent.prototype.navigateToSharedComment = function (id) {
8967
+ var _a, _b, _c;
8968
+ return __awaiter(this, void 0, void 0, function () {
8969
+ var source, message, e_3, parentMessage, thread;
8970
+ return __generator(this, function (_d) {
8971
+ switch (_d.label) {
8972
+ case 0:
8973
+ source = this.source;
8974
+ return [4 /*yield*/, source.ready];
8975
+ case 1:
8976
+ _d.sent();
8977
+ console.log("Navigating to shared comment with ID '" + id + "'...");
8978
+ _d.label = 2;
8979
+ case 2:
8980
+ _d.trys.push([2, 4, , 5]);
8981
+ return [4 /*yield*/, this.source.get(id)];
8982
+ case 3:
8983
+ message = _d.sent();
8984
+ return [3 /*break*/, 5];
8985
+ case 4:
8986
+ e_3 = _d.sent();
8987
+ console.error("Failed to find comment from URL: " + e_3.message);
8988
+ return [2 /*return*/];
8989
+ case 5:
8990
+ (_a = message.transientState) !== null && _a !== void 0 ? _a : (message.transientState = {});
8991
+ if (!message.parentMessageId) return [3 /*break*/, 11];
8992
+ return [4 /*yield*/, this.source.get(message.parentMessageId)];
8993
+ case 6:
8994
+ parentMessage = _d.sent();
8995
+ (_b = parentMessage.transientState) !== null && _b !== void 0 ? _b : (parentMessage.transientState = {});
8996
+ return [4 /*yield*/, this.selectMessage(parentMessage)];
8997
+ case 7:
8998
+ thread = _d.sent();
8999
+ // Need to re-retrieve the message within the new chat source to affect its
9000
+ // transient state.
9001
+ return [4 /*yield*/, thread.ready];
9002
+ case 8:
9003
+ // Need to re-retrieve the message within the new chat source to affect its
9004
+ // transient state.
9005
+ _d.sent();
9006
+ return [4 /*yield*/, thread.get(message.id)];
9007
+ case 9:
9008
+ message = _d.sent();
9009
+ (_c = message.transientState) !== null && _c !== void 0 ? _c : (message.transientState = {});
9010
+ message.transientState.highlighted = true;
9011
+ console.dir(message);
9012
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 500); })];
9013
+ case 10:
9014
+ _d.sent();
9015
+ return [3 /*break*/, 12];
9016
+ case 11:
9017
+ this.selectMessage(message);
9018
+ _d.label = 12;
9019
+ case 12:
9020
+ this.scrollToComment(id);
9021
+ return [2 /*return*/];
9022
+ }
9023
+ });
9024
+ });
8774
9025
  };
8775
- BantaCommentsComponent.prototype.insertEmoji = function (text) {
8776
- this.newMessageText += text;
9026
+ BantaCommentsComponent.prototype.sendPermissionDenied = function (message) {
9027
+ this._permissionDeniedError.next(message);
8777
9028
  };
8778
- BantaCommentsComponent.prototype.onReplyKeyDown = function (event) {
9029
+ BantaCommentsComponent.prototype.scrollToMessage = function (message) {
9030
+ var el = this.elementRef.nativeElement.querySelector("[data-comment-id=\"" + message.id + "\"]");
9031
+ if (!el)
9032
+ return;
9033
+ el.scrollIntoView({ block: 'center', inline: 'start' });
8779
9034
  };
8780
- BantaCommentsComponent.prototype.insertReplyEmoji = function (text) {
8781
- this.replyMessage += text;
9035
+ BantaCommentsComponent.prototype.addParticipant = function (message) {
9036
+ if (!message || !message.user || !message.user.id)
9037
+ return;
9038
+ var existing = this.participants.find(function (x) { return x.id === message.user.id; });
9039
+ if (existing)
9040
+ return;
9041
+ this.participants.push(message.user);
8782
9042
  };
8783
- BantaCommentsComponent.prototype.indicateError = function (message) {
8784
- var _this = this;
8785
- this.sendError = new Error(message);
8786
- setTimeout(function () {
8787
- _this.expandError = true;
8788
- setTimeout(function () {
8789
- _this.expandError = false;
8790
- }, 5 * 1000);
9043
+ // Actions
9044
+ BantaCommentsComponent.prototype.likeMessage = function (source, message) {
9045
+ var _a;
9046
+ return __awaiter(this, void 0, void 0, function () {
9047
+ var e_4;
9048
+ return __generator(this, function (_d) {
9049
+ switch (_d.label) {
9050
+ case 0:
9051
+ this._upvoted.next(message);
9052
+ message.transientState.liking = true;
9053
+ if (!((_a = message.userState) === null || _a === void 0 ? void 0 : _a.liked))
9054
+ message.likes = (message.likes || 0) + 1;
9055
+ _d.label = 1;
9056
+ case 1:
9057
+ _d.trys.push([1, 3, , 4]);
9058
+ return [4 /*yield*/, source.likeMessage(message.id)];
9059
+ case 2:
9060
+ _d.sent();
9061
+ return [3 /*break*/, 4];
9062
+ case 3:
9063
+ e_4 = _d.sent();
9064
+ this.handleBackendExceptionAsAlert(e_4, 'Could not like this message: ');
9065
+ return [3 /*break*/, 4];
9066
+ case 4: return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 250); })];
9067
+ case 5:
9068
+ _d.sent();
9069
+ message.transientState.liking = false;
9070
+ return [2 /*return*/];
9071
+ }
9072
+ });
8791
9073
  });
8792
9074
  };
8793
- BantaCommentsComponent.prototype.upvoteMessage = function (message) {
9075
+ BantaCommentsComponent.prototype.unlikeMessage = function (source, message) {
9076
+ var _a;
8794
9077
  return __awaiter(this, void 0, void 0, function () {
8795
- return __generator(this, function (_c) {
8796
- switch (_c.label) {
9078
+ var e_5;
9079
+ return __generator(this, function (_d) {
9080
+ switch (_d.label) {
8797
9081
  case 0:
8798
9082
  this._upvoted.next(message);
8799
- message.$upvoting = true;
8800
- message.upvotes = (message.upvotes || 0) + 1;
8801
- return [4 /*yield*/, this.backend.upvoteMessage(message.topicId, message.parentMessageId ? message.parentMessageId : message.id, message.parentMessageId ? message.id : undefined)];
9083
+ message.transientState.liking = true;
9084
+ if ((_a = message.userState) === null || _a === void 0 ? void 0 : _a.liked)
9085
+ message.likes = (message.likes || 0) - 1;
9086
+ _d.label = 1;
8802
9087
  case 1:
8803
- _c.sent();
8804
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 250); })];
9088
+ _d.trys.push([1, 3, , 4]);
9089
+ return [4 /*yield*/, source.unlikeMessage(message.id)];
8805
9090
  case 2:
8806
- _c.sent();
8807
- message.$upvoting = false;
9091
+ _d.sent();
9092
+ return [3 /*break*/, 4];
9093
+ case 3:
9094
+ e_5 = _d.sent();
9095
+ this.handleBackendExceptionAsAlert(e_5, 'Failed to unlike this message: ');
9096
+ return [3 /*break*/, 4];
9097
+ case 4: return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 250); })];
9098
+ case 5:
9099
+ _d.sent();
9100
+ message.transientState.liking = false;
8808
9101
  return [2 /*return*/];
8809
9102
  }
8810
9103
  });
8811
9104
  });
8812
9105
  };
8813
9106
  BantaCommentsComponent.prototype.reportMessage = function (message) {
8814
- this._reported.next(message);
9107
+ return __awaiter(this, void 0, void 0, function () {
9108
+ return __generator(this, function (_d) {
9109
+ this._reported.next(message);
9110
+ return [2 /*return*/];
9111
+ });
9112
+ });
8815
9113
  };
8816
9114
  BantaCommentsComponent.prototype.unselectMessage = function () {
8817
9115
  return __awaiter(this, void 0, void 0, function () {
8818
9116
  var message;
8819
9117
  var _this = this;
8820
- return __generator(this, function (_c) {
9118
+ return __generator(this, function (_d) {
8821
9119
  message = this.selectedMessage;
8822
9120
  this._selected.next(null);
8823
9121
  this.selectedMessage = null;
@@ -8834,94 +9132,169 @@
8834
9132
  };
8835
9133
  BantaCommentsComponent.prototype.selectMessage = function (message) {
8836
9134
  return __awaiter(this, void 0, void 0, function () {
9135
+ var selectedMessageThread;
8837
9136
  var _this = this;
8838
- return __generator(this, function (_c) {
8839
- this._selected.next(message);
8840
- this.selectedMessage = message;
8841
- setTimeout(function () { return _this.selectedMessageVisible = true; });
8842
- setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
8843
- var _c;
8844
- return __generator(this, function (_d) {
8845
- switch (_d.label) {
8846
- case 0:
8847
- _c = this;
8848
- return [4 /*yield*/, this.backend.getSourceForThread(this.topicID, message.id)];
8849
- case 1:
8850
- _c.selectedMessageThread = _d.sent();
9137
+ return __generator(this, function (_d) {
9138
+ switch (_d.label) {
9139
+ case 0:
9140
+ this._selected.next(message);
9141
+ this.selectedMessage = message;
9142
+ return [4 /*yield*/, this.backend.getSourceForThread(this.topicID, message.id)];
9143
+ case 1:
9144
+ selectedMessageThread = _d.sent();
9145
+ setTimeout(function () { return _this.selectedMessageVisible = true; });
9146
+ setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
9147
+ return __generator(this, function (_d) {
9148
+ this.selectedMessageThread = selectedMessageThread;
8851
9149
  return [2 /*return*/];
8852
- }
8853
- });
8854
- }); }, 250);
9150
+ });
9151
+ }); }, 250);
9152
+ return [2 /*return*/, selectedMessageThread];
9153
+ }
9154
+ });
9155
+ });
9156
+ };
9157
+ BantaCommentsComponent.prototype.showSignIn = function () {
9158
+ return __awaiter(this, void 0, void 0, function () {
9159
+ return __generator(this, function (_d) {
9160
+ this._signInSelected.next();
9161
+ return [2 /*return*/];
9162
+ });
9163
+ });
9164
+ };
9165
+ BantaCommentsComponent.prototype.showEditAvatar = function () {
9166
+ return __awaiter(this, void 0, void 0, function () {
9167
+ return __generator(this, function (_d) {
9168
+ this._editAvatarSelected.next();
8855
9169
  return [2 /*return*/];
8856
9170
  });
8857
9171
  });
8858
9172
  };
8859
9173
  BantaCommentsComponent.prototype.selectMessageUser = function (message) {
8860
- this._userSelected.next(message);
9174
+ return __awaiter(this, void 0, void 0, function () {
9175
+ return __generator(this, function (_d) {
9176
+ this._userSelected.next(message);
9177
+ return [2 /*return*/];
9178
+ });
9179
+ });
8861
9180
  };
8862
9181
  BantaCommentsComponent.prototype.selectUsername = function (user) {
8863
- this._usernameSelected.next(user);
9182
+ return __awaiter(this, void 0, void 0, function () {
9183
+ return __generator(this, function (_d) {
9184
+ this._usernameSelected.next(user);
9185
+ return [2 /*return*/];
9186
+ });
9187
+ });
8864
9188
  };
8865
9189
  BantaCommentsComponent.prototype.selectAvatar = function (user) {
8866
- this._avatarSelected.next(user);
9190
+ return __awaiter(this, void 0, void 0, function () {
9191
+ return __generator(this, function (_d) {
9192
+ this._avatarSelected.next(user);
9193
+ return [2 /*return*/];
9194
+ });
9195
+ });
8867
9196
  };
8868
9197
  BantaCommentsComponent.prototype.shareMessage = function (message) {
8869
- this._shared.next(message);
9198
+ return __awaiter(this, void 0, void 0, function () {
9199
+ return __generator(this, function (_d) {
9200
+ this._shared.next(message);
9201
+ return [2 /*return*/];
9202
+ });
9203
+ });
8870
9204
  };
8871
- BantaCommentsComponent.prototype.sendReply = function () {
9205
+ BantaCommentsComponent.prototype.deleteMessage = function (message) {
8872
9206
  return __awaiter(this, void 0, void 0, function () {
8873
- return __generator(this, function (_c) {
8874
- switch (_c.label) {
8875
- case 0: return [4 /*yield*/, this.selectedMessageThread.send({
8876
- message: this.replyMessage,
8877
- parentMessageId: this.selectedMessage.id,
8878
- upvotes: 0,
8879
- user: this.user,
8880
- submessages: [],
8881
- submessageCount: 0,
8882
- topicId: this.topicID,
8883
- sentAt: Date.now(),
8884
- updatedAt: Date.now()
8885
- })];
9207
+ var e_6;
9208
+ return __generator(this, function (_d) {
9209
+ switch (_d.label) {
9210
+ case 0:
9211
+ if (!confirm("Are you sure you want to delete this comment? You cannot undo this action."))
9212
+ return [2 /*return*/];
9213
+ _d.label = 1;
8886
9214
  case 1:
8887
- _c.sent();
8888
- this.replyMessage = '';
9215
+ _d.trys.push([1, 3, , 4]);
9216
+ return [4 /*yield*/, this.source.deleteMessage(message.id)];
9217
+ case 2:
9218
+ _d.sent();
9219
+ return [3 /*break*/, 4];
9220
+ case 3:
9221
+ e_6 = _d.sent();
9222
+ this.handleBackendExceptionAsAlert(e_6, "Could not delete message: ");
9223
+ return [3 /*break*/, 4];
9224
+ case 4: return [2 /*return*/];
9225
+ }
9226
+ });
9227
+ });
9228
+ };
9229
+ BantaCommentsComponent.prototype.editMessage = function (source, message, newText) {
9230
+ return __awaiter(this, void 0, void 0, function () {
9231
+ var e_7;
9232
+ return __generator(this, function (_d) {
9233
+ switch (_d.label) {
9234
+ case 0:
9235
+ _d.trys.push([0, 2, , 3]);
9236
+ return [4 /*yield*/, source.editMessage(message.id, newText)];
9237
+ case 1:
9238
+ _d.sent();
9239
+ return [3 /*break*/, 3];
9240
+ case 2:
9241
+ e_7 = _d.sent();
9242
+ this.handleBackendExceptionAsAlert(e_7, 'Could not edit this message: ');
9243
+ return [2 /*return*/];
9244
+ case 3:
9245
+ message.message = newText;
9246
+ message.transientState.editing = false;
8889
9247
  return [2 /*return*/];
8890
9248
  }
8891
9249
  });
8892
9250
  });
8893
9251
  };
8894
- BantaCommentsComponent.prototype.scrollToMessage = function (message) {
8895
- var el = this.elementRef.nativeElement.querySelector("[data-comment-id=\"" + message.id + "\"]");
8896
- if (!el)
8897
- return;
8898
- el.scrollIntoView({ block: 'center', inline: 'start' });
9252
+ BantaCommentsComponent.prototype.startEditing = function (message) {
9253
+ return __awaiter(this, void 0, void 0, function () {
9254
+ return __generator(this, function (_d) {
9255
+ this.selectedMessage.transientState.editing = false;
9256
+ message.transientState.editing = true;
9257
+ return [2 /*return*/];
9258
+ });
9259
+ });
9260
+ };
9261
+ BantaCommentsComponent.prototype.saveEdit = function (message, text) {
9262
+ return __awaiter(this, void 0, void 0, function () {
9263
+ var e_8;
9264
+ return __generator(this, function (_d) {
9265
+ switch (_d.label) {
9266
+ case 0:
9267
+ _d.trys.push([0, 2, , 3]);
9268
+ return [4 /*yield*/, this.source.editMessage(message.id, text)];
9269
+ case 1:
9270
+ _d.sent();
9271
+ message.transientState.editing = false;
9272
+ return [3 /*break*/, 3];
9273
+ case 2:
9274
+ e_8 = _d.sent();
9275
+ this.handleBackendExceptionAsAlert(e_8, "Could not edit message: ");
9276
+ return [3 /*break*/, 3];
9277
+ case 3: return [2 /*return*/];
9278
+ }
9279
+ });
9280
+ });
8899
9281
  };
8900
9282
  return BantaCommentsComponent;
8901
9283
  }());
8902
9284
  BantaCommentsComponent.decorators = [
8903
9285
  { type: core.Component, args: [{
8904
9286
  selector: 'banta-comments',
8905
- template: "\r\n<div class=\"focused\" [class.visible]=\"selectedMessageVisible\" *ngIf=\"selectedMessage\">\r\n\r\n <div>\r\n <a mat-button href=\"javascript:;\" (click)=\"unselectMessage()\">\r\n <mat-icon>arrow_back</mat-icon>\r\n Latest Comments\r\n </a>\r\n </div>\r\n\r\n <banta-comment\r\n [message]=\"selectedMessage\"\r\n ></banta-comment>\r\n\r\n <div class=\"replies\">\r\n\r\n <ng-container *ngIf=\"!selectedMessageThread\">\r\n <div class=\"loading\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectedMessageThread\">\r\n <banta-comment-view\r\n [source]=\"selectedMessageThread\"\r\n [allowReplies]=\"false\"\r\n [fixedHeight]=\"false\"\r\n [showEmptyState]=\"false\"\r\n [newestLast]=\"true\"\r\n ></banta-comment-view>\r\n\r\n <banta-comment-field\r\n [sendLabel]=\"replyLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n (signInSelected)=\"showSignIn()\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n [source]=\"selectedMessageThread\"\r\n [canComment]=\"canComment\"\r\n [signInLabel]=\"signInLabel\"\r\n [permissionDeniedLabel]=\"permissionDeniedLabel\"\r\n (permissionDeniedError)=\"showPermissionDenied()\"\r\n [shouldInterceptMessageSend]=\"shouldInterceptMessageSend\"\r\n [user]=\"user\"\r\n [label]=\"postReplyLabel\"\r\n >\r\n <ng-content select=\".reply-send-options\"></ng-content>\r\n </banta-comment-field>\r\n </ng-container>\r\n </div>\r\n</div>\r\n\r\n<div class=\"main\" [class.hidden]=\"selectedMessage\">\r\n <banta-comment-field\r\n [source]=\"source\"\r\n [user]=\"user\"\r\n [sendLabel]=\"sendLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [signInLabel]=\"signInLabel\"\r\n [canComment]=\"canComment\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n [label]=\"postCommentLabel\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n (signInSelected)=\"showSignIn()\"\r\n [permissionDeniedLabel]=\"permissionDeniedLabel\"\r\n (permissionDeniedError)=\"showPermissionDenied()\"\r\n [shouldInterceptMessageSend]=\"shouldInterceptMessageSend\"\r\n >\r\n \r\n </banta-comment-field>\r\n\r\n <banta-comment-sort\r\n [(sort)]=\"sortOrder\"></banta-comment-sort>\r\n\r\n <banta-comment-view\r\n [class.faded]=\"selectedMessage\"\r\n [source]=\"source\"\r\n [fixedHeight]=\"fixedHeight\"\r\n [maxMessages]=\"maxMessages\"\r\n [maxVisibleMessages]=\"maxVisibleMessages\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n (userSelected)=\"selectMessageUser($event)\"\r\n (selected)=\"selectMessage($event)\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (reported)=\"reportMessage($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (shared)=\"shareMessage($event)\"\r\n ></banta-comment-view>\r\n</div>\r\n",
8906
- styles: [":host{display:flex;flex-direction:column}@-webkit-keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}@keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}.focused{-webkit-animation-name:select-comment;animation-name:select-comment;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.focused .replies{margin-top:1em;margin-left:4em}banta-comment-view{opacity:1;transition:opacity .4s ease-in-out}banta-comment-view.faded{opacity:.25}.loading{display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:0 auto;min-height:16em}.main.hidden{display:none}@media (max-width:500px){.focused .replies{margin-left:0}}"]
9287
+ template: "<ng-container *ngIf=\"loading\">\r\n <div class=\"loading-screen\" [class.visible]=\"showLoadingScreen\">\r\n <h1>Loading Comments</h1>\r\n <div>\r\n <mat-spinner [diameter]=\"300\" [strokeWidth]=\"2\"></mat-spinner>\r\n </div>\r\n\r\n <p class=\"loading-message\" [class.visible]=\"loadingMessageVisible\">{{loadingMessage}}</p>\r\n </div>\r\n</ng-container>\r\n<ng-container *ngIf=\"!loading\">\r\n <div class=\"focused\" [class.visible]=\"selectedMessageVisible\" *ngIf=\"selectedMessage\">\r\n\r\n <div>\r\n <a mat-button href=\"javascript:;\" (click)=\"unselectMessage()\">\r\n <mat-icon>arrow_back</mat-icon>\r\n Latest Comments\r\n </a>\r\n </div>\r\n\r\n <banta-comment\r\n [message]=\"selectedMessage\"\r\n [liking]=\"selectedMessage.transientState.liking\"\r\n [mine]=\"user?.id === selectedMessage.user?.id\"\r\n [permissions]=\"source?.permissions\"\r\n [showReplyAction]=\"false\"\r\n [editing]=\"selectedMessage.transientState.editing\"\r\n (editStarted)=\"startEditing(selectedMessage)\"\r\n (editEnded)=\"selectedMessage.transientState.editing = false\"\r\n (edited)=\"saveEdit(selectedMessage, $event)\"\r\n (userSelected)=\"selectMessageUser(selectedMessage)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (liked)=\"likeMessage(source, selectedMessage)\"\r\n (unliked)=\"unlikeMessage(source, selectedMessage)\"\r\n (reported)=\"reportMessage(selectedMessage)\"\r\n (selected)=\"selectMessage(selectedMessage)\"\r\n (shared)=\"shareMessage($event)\"\r\n (deleted)=\"deleteMessage(selectedMessage)\"\r\n ></banta-comment>\r\n\r\n <div class=\"replies\">\r\n\r\n <ng-container *ngIf=\"!selectedMessageThread\">\r\n <div class=\"loading\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectedMessageThread\">\r\n <banta-comment-view\r\n [source]=\"selectedMessageThread\"\r\n [allowReplies]=\"false\"\r\n [fixedHeight]=\"false\"\r\n [showEmptyState]=\"false\"\r\n [newestLast]=\"true\"\r\n (liked)=\"likeMessage(selectedMessageThread, $event)\"\r\n (unliked)=\"unlikeMessage(selectedMessageThread, $event)\"\r\n (messageEdited)=\"editMessage(selectedMessageThread, $event.message, $event.newMessage)\"\r\n (reported)=\"reportMessage($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (shared)=\"shareMessage($event)\"\r\n (deleted)=\"deleteMessage($event)\"\r\n ></banta-comment-view>\r\n\r\n <banta-comment-field\r\n [sendLabel]=\"replyLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n (signInSelected)=\"showSignIn()\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n [source]=\"selectedMessageThread\"\r\n [canComment]=\"source?.permissions?.canPost\"\r\n [signInLabel]=\"signInLabel\"\r\n [permissionDeniedLabel]=\"permissionDeniedLabel\"\r\n (permissionDeniedError)=\"sendPermissionDenied($event)\"\r\n [shouldInterceptMessageSend]=\"shouldInterceptMessageSend\"\r\n [user]=\"user\"\r\n [label]=\"postReplyLabel\"\r\n [submit]=\"sendReply\"\r\n >\r\n <ng-content select=\".reply-send-options\"></ng-content>\r\n </banta-comment-field>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"main\" [class.hidden]=\"selectedMessage\">\r\n <banta-comment-field\r\n [source]=\"source\"\r\n [user]=\"user\"\r\n [sendLabel]=\"sendLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [signInLabel]=\"signInLabel\"\r\n [canComment]=\"source?.permissions?.canPost\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n [label]=\"postCommentLabel\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n (signInSelected)=\"showSignIn()\"\r\n [permissionDeniedLabel]=\"permissionDeniedLabel\"\r\n (permissionDeniedError)=\"sendPermissionDenied($event)\"\r\n [shouldInterceptMessageSend]=\"shouldInterceptMessageSend\"\r\n [submit]=\"sendMessage\"\r\n >\r\n \r\n </banta-comment-field>\r\n\r\n <banta-comment-sort\r\n [(sort)]=\"sortOrder\"></banta-comment-sort>\r\n\r\n <banta-comment-view\r\n [class.faded]=\"selectedMessage\"\r\n [source]=\"source\"\r\n [fixedHeight]=\"fixedHeight\"\r\n [maxMessages]=\"maxMessages\"\r\n [maxVisibleMessages]=\"maxVisibleMessages\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n (userSelected)=\"selectMessageUser($event)\"\r\n (sortOrderChanged)=\"sortOrder = $event\"\r\n (selected)=\"selectMessage($event)\"\r\n (liked)=\"likeMessage(source, $event)\"\r\n (unliked)=\"unlikeMessage(source, $event)\"\r\n (messageEdited)=\"editMessage(source, $event.message, $event.newMessage)\"\r\n (reported)=\"reportMessage($event)\"\r\n (usernameSelected)=\"selectUsername($event)\"\r\n (avatarSelected)=\"selectAvatar($event)\"\r\n (shared)=\"shareMessage($event)\"\r\n (deleted)=\"deleteMessage($event)\"\r\n ></banta-comment-view>\r\n </div>\r\n</ng-container>",
9288
+ styles: [":host{display:flex;flex-direction:column}@-webkit-keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}@keyframes select-comment{0%{transform:scale(1.15)}to{transform:scale(1)}}.focused{-webkit-animation-name:select-comment;animation-name:select-comment;-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.focused .replies{margin-top:1em;margin-left:4em}banta-comment-view{opacity:1;transition:opacity .4s ease-in-out}banta-comment-view.faded{opacity:.25}.loading{display:block;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;margin:0 auto;min-height:16em}.main.hidden{display:none}@media (max-width:500px){.focused .replies{margin-left:0}}.loading-screen{text-align:center;opacity:0;transition:opacity .25s ease-in-out}.loading-screen.visible{opacity:1}.loading-screen h1{font-weight:100}.loading-screen mat-spinner{margin:5em auto}.loading-screen .loading-message{opacity:0;transition:opacity .25s ease-in-out;width:500px;max-width:100%;margin:0 auto}.loading-screen .loading-message.visible{opacity:1}"]
8907
9289
  },] }
8908
9290
  ];
8909
9291
  BantaCommentsComponent.ctorParameters = function () { return [
8910
- { type: BantaService },
8911
- { type: ChatBackendService },
9292
+ { type: ChatBackendBase },
8912
9293
  { type: core.ElementRef },
8913
9294
  { type: router.ActivatedRoute }
8914
9295
  ]; };
8915
9296
  BantaCommentsComponent.propDecorators = {
8916
- hashtags: [{ type: core.Input }],
8917
- participants: [{ type: core.Input }],
8918
- source: [{ type: core.Input }],
8919
- fixedHeight: [{ type: core.Input }],
8920
- maxMessages: [{ type: core.Input }],
8921
- maxVisibleMessages: [{ type: core.Input }],
8922
- genericAvatarUrl: [{ type: core.Input }],
8923
- shouldInterceptMessageSend: [{ type: core.Input }],
8924
- topicID: [{ type: core.Input }],
9297
+ loadingMessages: [{ type: core.Input }],
8925
9298
  signInLabel: [{ type: core.Input }],
8926
9299
  sendLabel: [{ type: core.Input }],
8927
9300
  replyLabel: [{ type: core.Input }],
@@ -8929,6 +9302,15 @@
8929
9302
  permissionDeniedLabel: [{ type: core.Input }],
8930
9303
  postCommentLabel: [{ type: core.Input }],
8931
9304
  postReplyLabel: [{ type: core.Input }],
9305
+ fixedHeight: [{ type: core.Input }],
9306
+ maxMessages: [{ type: core.Input }],
9307
+ maxVisibleMessages: [{ type: core.Input }],
9308
+ genericAvatarUrl: [{ type: core.Input }],
9309
+ shouldInterceptMessageSend: [{ type: core.Input }],
9310
+ participants: [{ type: core.Input }],
9311
+ source: [{ type: core.Input }],
9312
+ hashtags: [{ type: core.Input }],
9313
+ topicID: [{ type: core.Input }],
8932
9314
  signInSelected: [{ type: core.Output }],
8933
9315
  editAvatarSelected: [{ type: core.Output }],
8934
9316
  permissionDeniedError: [{ type: core.Output }],
@@ -9010,7 +9392,7 @@
9010
9392
  },] }
9011
9393
  ];
9012
9394
  LiveCommentComponent.ctorParameters = function () { return [
9013
- { type: ChatBackendService }
9395
+ { type: ChatBackendBase }
9014
9396
  ]; };
9015
9397
  LiveCommentComponent.propDecorators = {
9016
9398
  upvoted: [{ type: core.Output }],
@@ -9033,6 +9415,7 @@
9033
9415
  this.permissionDeniedLabel = 'Unavailable';
9034
9416
  this.signInLabel = 'Sign In';
9035
9417
  this.placeholder = '';
9418
+ this.textChanged = new rxjs.Subject();
9036
9419
  this.participants = [];
9037
9420
  this._permissionDeniedError = new rxjs.Subject();
9038
9421
  this.autocompleteVisible = false;
@@ -9050,8 +9433,8 @@
9050
9433
  var root = document.body.querySelector('[ng-version]') || document.body;
9051
9434
  root.appendChild(this.autocompleteEl.nativeElement);
9052
9435
  };
9053
- CommentFieldComponent.prototype.showPermissionDenied = function () {
9054
- this._permissionDeniedError.next();
9436
+ CommentFieldComponent.prototype.sendPermissionDenied = function (message) {
9437
+ this._permissionDeniedError.next(message);
9055
9438
  };
9056
9439
  CommentFieldComponent.prototype.showAutoComplete = function (options) {
9057
9440
  this.autoCompleteSelected = 0;
@@ -9075,17 +9458,19 @@
9075
9458
  CommentFieldComponent.prototype.indicateError = function (message) {
9076
9459
  var _this = this;
9077
9460
  this.sendError = new Error(message);
9078
- setTimeout(function () {
9461
+ this.expandError = false;
9462
+ clearTimeout(this.errorTimeout);
9463
+ this.errorTimeout = setTimeout(function () {
9079
9464
  _this.expandError = true;
9080
- setTimeout(function () {
9465
+ _this.errorTimeout = setTimeout(function () {
9081
9466
  _this.expandError = false;
9082
9467
  }, 5 * 1000);
9083
- });
9468
+ }, 100);
9084
9469
  };
9085
9470
  CommentFieldComponent.prototype.autocomplete = function (replacement) {
9086
9471
  return __awaiter(this, void 0, void 0, function () {
9087
9472
  var el;
9088
- return __generator(this, function (_b) {
9473
+ return __generator(this, function (_a) {
9089
9474
  el = this.textareaEl.nativeElement;
9090
9475
  this.text = this.text.slice(0, el.selectionStart - this.completionPrefix.length) + replacement + this.text.slice(el.selectionStart);
9091
9476
  return [2 /*return*/];
@@ -9095,7 +9480,7 @@
9095
9480
  CommentFieldComponent.prototype.insert = function (str) {
9096
9481
  return __awaiter(this, void 0, void 0, function () {
9097
9482
  var el;
9098
- return __generator(this, function (_b) {
9483
+ return __generator(this, function (_a) {
9099
9484
  el = this.textareaEl.nativeElement;
9100
9485
  this.text = this.text.slice(0, el.selectionStart) + str + this.text.slice(el.selectionStart);
9101
9486
  return [2 /*return*/];
@@ -9105,8 +9490,8 @@
9105
9490
  CommentFieldComponent.prototype.onKeyDown = function (event) {
9106
9491
  return __awaiter(this, void 0, void 0, function () {
9107
9492
  var _this = this;
9108
- return __generator(this, function (_b) {
9109
- switch (_b.label) {
9493
+ return __generator(this, function (_a) {
9494
+ switch (_a.label) {
9110
9495
  case 0:
9111
9496
  if (this.autocompleteVisible) {
9112
9497
  if (event.key === 'Escape') {
@@ -9141,7 +9526,7 @@
9141
9526
  if (!(event.key === 'Enter' && event.ctrlKey)) return [3 /*break*/, 2];
9142
9527
  return [4 /*yield*/, this.sendMessage()];
9143
9528
  case 1:
9144
- _b.sent();
9529
+ _a.sent();
9145
9530
  return [2 /*return*/];
9146
9531
  case 2:
9147
9532
  if (this.completionFunc) {
@@ -9224,19 +9609,18 @@
9224
9609
  this.editAvatarSelected.next();
9225
9610
  };
9226
9611
  CommentFieldComponent.prototype.sendMessage = function () {
9227
- var _a;
9228
9612
  return __awaiter(this, void 0, void 0, function () {
9229
- var text, message, intercept, e_1;
9230
- return __generator(this, function (_b) {
9231
- switch (_b.label) {
9613
+ var text, message, e_1;
9614
+ return __generator(this, function (_a) {
9615
+ switch (_a.label) {
9232
9616
  case 0:
9233
9617
  if (!this.source)
9234
9618
  return [2 /*return*/];
9235
9619
  this.sending = true;
9236
9620
  this.sendError = null;
9237
- _b.label = 1;
9621
+ _a.label = 1;
9238
9622
  case 1:
9239
- _b.trys.push([1, , 8, 9]);
9623
+ _a.trys.push([1, , 7, 8]);
9240
9624
  text = (this.text || '').trim();
9241
9625
  if (text === '')
9242
9626
  return [2 /*return*/];
@@ -9244,34 +9628,29 @@
9244
9628
  user: this.user,
9245
9629
  sentAt: Date.now(),
9246
9630
  url: location.href,
9247
- upvotes: 0,
9631
+ likes: 0,
9248
9632
  message: text
9249
9633
  };
9250
- _b.label = 2;
9634
+ _a.label = 2;
9251
9635
  case 2:
9252
- _b.trys.push([2, 6, , 7]);
9253
- return [4 /*yield*/, ((_a = this.shouldInterceptMessageSend) === null || _a === void 0 ? void 0 : _a.call(this, message, this.source))];
9636
+ _a.trys.push([2, 4, , 6]);
9637
+ return [4 /*yield*/, this.submit(message)];
9254
9638
  case 3:
9255
- intercept = _b.sent();
9256
- if (!!intercept) return [3 /*break*/, 5];
9257
- return [4 /*yield*/, this.source.send(message)];
9639
+ _a.sent();
9640
+ this.text = '';
9641
+ return [3 /*break*/, 6];
9258
9642
  case 4:
9259
- _b.sent();
9260
- _b.label = 5;
9643
+ e_1 = _a.sent();
9644
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 1000); })];
9261
9645
  case 5:
9262
- this.text = '';
9263
- return [3 /*break*/, 7];
9264
- case 6:
9265
- e_1 = _b.sent();
9266
- this.indicateError("Could not send: " + e_1.message);
9267
- console.error("Failed to send message: ", message);
9268
- console.error(e_1);
9269
- return [3 /*break*/, 7];
9270
- case 7: return [3 /*break*/, 9];
9271
- case 8:
9646
+ _a.sent();
9647
+ this.indicateError(e_1.message);
9648
+ return [3 /*break*/, 6];
9649
+ case 6: return [3 /*break*/, 8];
9650
+ case 7:
9272
9651
  this.sending = false;
9273
9652
  return [7 /*endfinally*/];
9274
- case 9: return [2 /*return*/];
9653
+ case 8: return [2 /*return*/];
9275
9654
  }
9276
9655
  });
9277
9656
  });
@@ -9281,7 +9660,7 @@
9281
9660
  CommentFieldComponent.decorators = [
9282
9661
  { type: core.Component, args: [{
9283
9662
  selector: 'banta-comment-field',
9284
- template: "<form class=\"new-message\" (submit)=\"sendMessage()\">\r\n <div class=\"avatar-container\">\r\n <a href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"showEditAvatar()\"\r\n [style.background-image]=\"'url(' + user?.avatarUrl + ')'\"\r\n ></a>\r\n </div>\r\n <div class=\"text-container\">\r\n <div class=\"field-container\">\r\n <mat-form-field appearance=\"outline\" floatLabel=\"always\">\r\n <mat-label>{{label}}</mat-label>\r\n <textarea\r\n #textarea\r\n name=\"message\"\r\n [placeholder]=\"placeholder\"\r\n matInput\r\n cdkTextareaAutosize\r\n (keydown)=\"onKeyDown($event)\"\r\n (blur)=\"onBlur()\"\r\n [disabled]=\"sending\"\r\n [(ngModel)]=\"text\"></textarea>\r\n </mat-form-field>\r\n <ng-content></ng-content>\r\n <div #autocompleteContainer class=\"autocomplete-container\">\r\n <div #autocomplete class=\"autocomplete\" [class.visible]=\"autocompleteVisible\">\r\n\r\n <div>\r\n <strong>{{completionPrefix}}</strong>...\r\n </div>\r\n\r\n <a\r\n mat-button\r\n *ngFor=\"let option of autocompleteOptions; index as index\"\r\n (click)=\"activateAutoComplete(option)\"\r\n [class.active]=\"autoCompleteSelected === index\"\r\n >\r\n {{option.label}}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <mat-spinner *ngIf=\"sending\" class=\"icon loading\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n <div *ngIf=\"sendError\" class=\"error-message\" [class.expanded]=\"expandError\">\r\n <mat-icon *ngIf=\"sendError\">error</mat-icon>\r\n {{sendError.message}}\r\n </div>\r\n <emoji-selector-button\r\n class=\"top-right\"\r\n (selected)=\"insertEmoji($event)\"\r\n ></emoji-selector-button>\r\n </div>\r\n <div class=\"actions\">\r\n <ng-container *ngIf=\"!user\">\r\n <button\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n (click)=\"showSignIn()\"\r\n >{{signInLabel}}</button>\r\n </ng-container>\r\n <ng-container *ngIf=\"user\">\r\n <button\r\n *ngIf=\"canComment\"\r\n mat-raised-button\r\n class=\"send\"\r\n color=\"primary\"\r\n [disabled]=\"!text || sending\"\r\n >\r\n <mat-icon *ngIf=\"!sending\">chevron_right</mat-icon>\r\n <mat-spinner *ngIf=\"sending\" class=\"icon\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n <span class=\"label\">\r\n <ng-container *ngIf=\"!sending\">\r\n {{sendLabel}}\r\n </ng-container>\r\n <ng-container *ngIf=\"sending\">\r\n {{sendingLabel}}\r\n </ng-container>\r\n </span>\r\n\r\n </button>\r\n <button\r\n *ngIf=\"!canComment\"\r\n type=\"button\"\r\n (click)=\"showPermissionDenied()\"\r\n mat-raised-button\r\n color=\"primary\"\r\n >{{permissionDeniedLabel}}</button>\r\n </ng-container>\r\n </div>\r\n</form>\r\n",
9663
+ template: "<form class=\"new-message\" (submit)=\"sendMessage()\">\r\n <div class=\"avatar-container\">\r\n <a href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"showEditAvatar()\"\r\n [style.background-image]=\"'url(' + user?.avatarUrl + ')'\"\r\n ></a>\r\n </div>\r\n <div class=\"text-container\">\r\n <div class=\"field-container\">\r\n <mat-form-field appearance=\"outline\" floatLabel=\"always\">\r\n <mat-label>{{label}}</mat-label>\r\n <textarea\r\n #textarea\r\n name=\"message\"\r\n [placeholder]=\"placeholder\"\r\n matInput\r\n cdkTextareaAutosize\r\n (keydown)=\"onKeyDown($event)\"\r\n (blur)=\"onBlur()\"\r\n [disabled]=\"sending\"\r\n [(ngModel)]=\"text\"></textarea>\r\n </mat-form-field>\r\n <ng-content></ng-content>\r\n <div #autocompleteContainer class=\"autocomplete-container\">\r\n <div #autocomplete class=\"autocomplete\" [class.visible]=\"autocompleteVisible\">\r\n\r\n <div>\r\n <strong>{{completionPrefix}}</strong>...\r\n </div>\r\n\r\n <a\r\n mat-button\r\n *ngFor=\"let option of autocompleteOptions; index as index\"\r\n (click)=\"activateAutoComplete(option)\"\r\n [class.active]=\"autoCompleteSelected === index\"\r\n >\r\n {{option.label}}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <mat-spinner *ngIf=\"sending\" class=\"icon loading\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n <div *ngIf=\"sendError\" class=\"error-message\" [class.expanded]=\"expandError\">\r\n <mat-icon *ngIf=\"sendError\">error</mat-icon>\r\n {{sendError.message}}\r\n </div>\r\n <emoji-selector-button\r\n class=\"top-right\"\r\n (selected)=\"insertEmoji($event)\"\r\n ></emoji-selector-button>\r\n </div>\r\n <div class=\"actions\">\r\n <ng-container *ngIf=\"!user\">\r\n <button\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n (click)=\"showSignIn()\"\r\n >{{signInLabel}}</button>\r\n </ng-container>\r\n <ng-container *ngIf=\"user\">\r\n <button\r\n *ngIf=\"canComment\"\r\n mat-raised-button\r\n class=\"send\"\r\n color=\"primary\"\r\n [disabled]=\"!text || sending\"\r\n >\r\n <mat-icon *ngIf=\"!sending\">chevron_right</mat-icon>\r\n <mat-spinner *ngIf=\"sending\" class=\"icon\" diameter=\"18\" strokeWidth=\"2\"></mat-spinner>\r\n <span class=\"label\">\r\n <ng-container *ngIf=\"!sending\">\r\n {{sendLabel}}\r\n </ng-container>\r\n <ng-container *ngIf=\"sending\">\r\n {{sendingLabel}}\r\n </ng-container>\r\n </span>\r\n\r\n </button>\r\n <button\r\n *ngIf=\"!canComment\"\r\n type=\"button\"\r\n (click)=\"sendPermissionDenied('')\"\r\n mat-raised-button\r\n color=\"primary\"\r\n >{{permissionDeniedLabel}}</button>\r\n </ng-container>\r\n </div>\r\n</form>\r\n",
9285
9664
  styles: ["@-webkit-keyframes comment-field-appear{0%{transform:translateY(128px);opacity:0}to{transform:translate(0);opacity:1}}@keyframes comment-field-appear{0%{transform:translateY(128px);opacity:0}to{transform:translate(0);opacity:1}}:host{margin:0 2em 0 0;display:block;-webkit-animation-name:comment-field-appear;animation-name:comment-field-appear;-webkit-animation-duration:.8s;animation-duration:.8s;-webkit-animation-delay:.4s;animation-delay:.4s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.avatar-container{width:calc(48px + 1.75em);display:flex;justify-content:flex-end}.avatar-container .avatar{width:48px;height:48px;background:pink;border-radius:100%;background-size:cover;background-repeat:no-repeat;background-position:50%;margin-top:.75em;margin-right:.75em}form{display:flex;padding:.5em;align-items:center}form .text-container{position:relative;display:flex;flex-grow:1}form .text-container textarea{font-size:14pt;width:100%}form .text-container textarea[disabled]{opacity:.5}form .text-container mat-form-field{margin-bottom:1em}form .text-container emoji-selector-button{bottom:0;right:0;position:absolute}form .text-container .error-message,form .text-container mat-spinner.loading{position:absolute;left:.5em;bottom:.5em}form .text-container .error-message{color:#683333;overflow-x:hidden;max-width:1.5em;white-space:nowrap;transition:max-width 2s ease-in-out}form .text-container .error-message.expanded,form .text-container .error-message:hover{max-width:100%}form .text-container .error-message mat-icon{vertical-align:middle}form input[type=text]{background:#000;color:#fff;border:1px solid #333;width:100%;height:1em}form .actions{margin-left:1em}form button{display:block;margin:0 0 0 auto}form.new-message{display:flex;align-items:flex-start}form.new-message .field-container{flex-grow:1;display:flex;flex-direction:column}form.new-message mat-form-field{width:100%}form.new-message button{margin:1.25em 0 0}button.send{min-width:9em}textarea{max-height:7em}.autocomplete-container{width:calc(100% - 2em);position:relative;pointer-events:none;top:-2em}.autocomplete{visibility:hidden;pointer-events:none;position:absolute;background:#333;padding:.5em;display:flex;flex-direction:column;z-index:100}.autocomplete.visible{visibility:visible;pointer-events:auto}.autocomplete a{width:100%;text-align:left}.autocomplete a.active{background:#555}@media (max-width:500px){:host{margin:0}.avatar-container{width:auto}.avatar-container .avatar{width:32px;height:32px;margin-top:1.5em}button.send{min-width:auto;margin-top:1.5em}button.send .label{display:none}}"]
9286
9665
  },] }
9287
9666
  ];
@@ -9291,19 +9670,23 @@
9291
9670
  canComment: [{ type: core.Input }],
9292
9671
  signInSelected: [{ type: core.Output }],
9293
9672
  editAvatarSelected: [{ type: core.Output }],
9673
+ sendError: [{ type: core.Input }],
9674
+ expandError: [{ type: core.Input }],
9294
9675
  sendLabel: [{ type: core.Input }],
9295
9676
  sendingLabel: [{ type: core.Input }],
9296
9677
  label: [{ type: core.Input }],
9297
9678
  permissionDeniedLabel: [{ type: core.Input }],
9298
9679
  signInLabel: [{ type: core.Input }],
9299
9680
  placeholder: [{ type: core.Input }],
9681
+ textChanged: [{ type: core.Output }],
9300
9682
  shouldInterceptMessageSend: [{ type: core.Input }],
9301
9683
  autocompleteEl: [{ type: core.ViewChild, args: ['autocomplete',] }],
9302
9684
  autocompleteContainerEl: [{ type: core.ViewChild, args: ['autocompleteContainer',] }],
9303
9685
  textareaEl: [{ type: core.ViewChild, args: ['textarea',] }],
9304
9686
  hashtags: [{ type: core.Input }],
9305
9687
  participants: [{ type: core.Input }],
9306
- permissionDeniedError: [{ type: core.Output }]
9688
+ permissionDeniedError: [{ type: core.Output }],
9689
+ submit: [{ type: core.Input }]
9307
9690
  };
9308
9691
 
9309
9692
  var CommentSortComponent = /** @class */ (function () {
@@ -9382,14 +9765,373 @@
9382
9765
  },] }
9383
9766
  ];
9384
9767
 
9768
+ var ChatSource = /** @class */ (function (_super_1) {
9769
+ __extends(ChatSource, _super_1);
9770
+ function ChatSource(backend, identifier, parentIdentifier, sortOrder) {
9771
+ var _this = _super_1.call(this) || this;
9772
+ _this.backend = backend;
9773
+ _this.identifier = identifier;
9774
+ _this.parentIdentifier = parentIdentifier;
9775
+ _this.sortOrder = sortOrder;
9776
+ _this.subscription = new rxjs.Subscription();
9777
+ _this.state = 'connecting';
9778
+ _this.messageMap = new Map();
9779
+ _this._messageReceived = new rxjs.Subject();
9780
+ _this._messageSent = new rxjs.Subject();
9781
+ _this.messages = [];
9782
+ _this.ready = new Promise(function (resolve) { return _this.markReady = resolve; });
9783
+ return _this;
9784
+ }
9785
+ ChatSource.prototype.bind = function (socket) {
9786
+ var _super = Object.create(null, {
9787
+ bind: { get: function () { return _super_1.prototype.bind; } }
9788
+ });
9789
+ return __awaiter(this, void 0, void 0, function () {
9790
+ var _this = this;
9791
+ return __generator(this, function (_b) {
9792
+ switch (_b.label) {
9793
+ case 0:
9794
+ _super.bind.call(this, socket);
9795
+ this.state = 'connected';
9796
+ this.markReady();
9797
+ return [4 /*yield*/, this.subscribeToTopic()];
9798
+ case 1:
9799
+ _b.sent();
9800
+ this.subscription.add(this.backend.userChanged.subscribe(function () { return _this.authenticate(); }));
9801
+ socket.addEventListener('open', function () { return __awaiter(_this, void 0, void 0, function () {
9802
+ return __generator(this, function (_b) {
9803
+ this.state = 'connected';
9804
+ return [2 /*return*/];
9805
+ });
9806
+ }); });
9807
+ socket.addEventListener('lost', function () { return __awaiter(_this, void 0, void 0, function () {
9808
+ return __generator(this, function (_b) {
9809
+ this.state = 'lost';
9810
+ return [2 /*return*/];
9811
+ });
9812
+ }); });
9813
+ socket.addEventListener('restore', function () { return __awaiter(_this, void 0, void 0, function () {
9814
+ return __generator(this, function (_b) {
9815
+ switch (_b.label) {
9816
+ case 0:
9817
+ this.state = 'restored';
9818
+ return [4 /*yield*/, this.authenticate()];
9819
+ case 1:
9820
+ _b.sent();
9821
+ return [4 /*yield*/, this.subscribeToTopic()];
9822
+ case 2:
9823
+ _b.sent();
9824
+ return [2 /*return*/];
9825
+ }
9826
+ });
9827
+ }); });
9828
+ return [2 /*return*/, this];
9829
+ }
9830
+ });
9831
+ });
9832
+ };
9833
+ ChatSource.prototype.getExistingMessages = function () {
9834
+ return __awaiter(this, void 0, void 0, function () {
9835
+ var messages;
9836
+ var _this = this;
9837
+ return __generator(this, function (_b) {
9838
+ switch (_b.label) {
9839
+ case 0: return [4 /*yield*/, this.peer.getExistingMessages()];
9840
+ case 1:
9841
+ messages = _b.sent();
9842
+ messages = messages.map(function (message) {
9843
+ var existingMessage = _this.messageMap.get(message.id);
9844
+ if (existingMessage)
9845
+ message = Object.assign(existingMessage, message);
9846
+ else
9847
+ _this.messageMap.set(message.id, message);
9848
+ return message;
9849
+ });
9850
+ return [2 /*return*/, messages];
9851
+ }
9852
+ });
9853
+ });
9854
+ };
9855
+ ChatSource.prototype.editMessage = function (messageId, text) {
9856
+ return __awaiter(this, void 0, void 0, function () {
9857
+ return __generator(this, function (_b) {
9858
+ this.peer.editMessage(messageId, text);
9859
+ return [2 /*return*/];
9860
+ });
9861
+ });
9862
+ };
9863
+ ChatSource.prototype.subscribeToTopic = function () {
9864
+ return __awaiter(this, void 0, void 0, function () {
9865
+ return __generator(this, function (_b) {
9866
+ switch (_b.label) {
9867
+ case 0: return [4 /*yield*/, this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder)];
9868
+ case 1:
9869
+ _b.sent();
9870
+ return [2 /*return*/];
9871
+ }
9872
+ });
9873
+ });
9874
+ };
9875
+ ChatSource.prototype.authenticate = function () {
9876
+ var _a;
9877
+ return __awaiter(this, void 0, void 0, function () {
9878
+ return __generator(this, function (_b) {
9879
+ switch (_b.label) {
9880
+ case 0: return [4 /*yield*/, this.peer.authenticate((_a = this.backend.user) === null || _a === void 0 ? void 0 : _a.token)];
9881
+ case 1:
9882
+ _b.sent();
9883
+ return [2 /*return*/];
9884
+ }
9885
+ });
9886
+ });
9887
+ };
9888
+ ChatSource.prototype.close = function () {
9889
+ _super_1.prototype.close.call(this);
9890
+ this.subscription.unsubscribe();
9891
+ };
9892
+ ChatSource.prototype.onPermissions = function (permissions) {
9893
+ window.bantaPermissionsDebug = permissions;
9894
+ this.permissions = permissions;
9895
+ };
9896
+ ChatSource.prototype.onChatMessage = function (message) {
9897
+ if (this.messageMap.has(message.id)) {
9898
+ Object.assign(this.messageMap.get(message.id), message);
9899
+ }
9900
+ else if (!message.hidden) {
9901
+ // Only process non-hidden messages through here.
9902
+ // Hidden messages may be sent to us when they become hidden (ie moderation is occurring).
9903
+ // But if we never had the message to begin with, we should discard it.
9904
+ this.messageMap.set(message.id, message);
9905
+ this._messageReceived.next(message);
9906
+ }
9907
+ };
9908
+ Object.defineProperty(ChatSource.prototype, "messageReceived", {
9909
+ get: function () { return this._messageReceived.asObservable(); },
9910
+ enumerable: false,
9911
+ configurable: true
9912
+ });
9913
+ Object.defineProperty(ChatSource.prototype, "messageSent", {
9914
+ get: function () { return this._messageSent.asObservable(); },
9915
+ enumerable: false,
9916
+ configurable: true
9917
+ });
9918
+ ChatSource.prototype.send = function (message) {
9919
+ return __awaiter(this, void 0, void 0, function () {
9920
+ return __generator(this, function (_b) {
9921
+ switch (_b.label) {
9922
+ case 0: return [4 /*yield*/, this.peer.sendMessage(message)];
9923
+ case 1: return [2 /*return*/, _b.sent()];
9924
+ }
9925
+ });
9926
+ });
9927
+ };
9928
+ ChatSource.prototype.loadAfter = function (message, count) {
9929
+ return __awaiter(this, void 0, void 0, function () {
9930
+ return __generator(this, function (_b) {
9931
+ if (!message)
9932
+ return [2 /*return*/];
9933
+ if (!message.pagingCursor)
9934
+ return [2 /*return*/, []];
9935
+ return [2 /*return*/, this.peer.loadAfter(Number(message.pagingCursor), count)];
9936
+ });
9937
+ });
9938
+ };
9939
+ ChatSource.prototype.get = function (id) {
9940
+ return __awaiter(this, void 0, void 0, function () {
9941
+ var message;
9942
+ return __generator(this, function (_b) {
9943
+ switch (_b.label) {
9944
+ case 0:
9945
+ if (this.messageMap.has(id))
9946
+ return [2 /*return*/, this.messageMap.get(id)];
9947
+ return [4 /*yield*/, this.peer.getMessage(id)];
9948
+ case 1:
9949
+ message = _b.sent();
9950
+ this.messageMap.set(id, message);
9951
+ return [2 /*return*/, message];
9952
+ }
9953
+ });
9954
+ });
9955
+ };
9956
+ ChatSource.prototype.getCount = function () {
9957
+ return __awaiter(this, void 0, void 0, function () {
9958
+ return __generator(this, function (_b) {
9959
+ switch (_b.label) {
9960
+ case 0: return [4 /*yield*/, this.peer.getCount()];
9961
+ case 1: return [2 /*return*/, _b.sent()];
9962
+ }
9963
+ });
9964
+ });
9965
+ };
9966
+ ChatSource.prototype.likeMessage = function (messageId) {
9967
+ return __awaiter(this, void 0, void 0, function () {
9968
+ return __generator(this, function (_b) {
9969
+ switch (_b.label) {
9970
+ case 0: return [4 /*yield*/, this.peer.likeMessage(messageId)];
9971
+ case 1: return [2 /*return*/, _b.sent()];
9972
+ }
9973
+ });
9974
+ });
9975
+ };
9976
+ ChatSource.prototype.unlikeMessage = function (messageId) {
9977
+ return __awaiter(this, void 0, void 0, function () {
9978
+ return __generator(this, function (_b) {
9979
+ switch (_b.label) {
9980
+ case 0: return [4 /*yield*/, this.peer.unlikeMessage(messageId)];
9981
+ case 1: return [2 /*return*/, _b.sent()];
9982
+ }
9983
+ });
9984
+ });
9985
+ };
9986
+ ChatSource.prototype.deleteMessage = function (messageId) {
9987
+ return __awaiter(this, void 0, void 0, function () {
9988
+ return __generator(this, function (_b) {
9989
+ switch (_b.label) {
9990
+ case 0: return [4 /*yield*/, this.peer.deleteMessage(messageId)];
9991
+ case 1: return [2 /*return*/, _b.sent()];
9992
+ }
9993
+ });
9994
+ });
9995
+ };
9996
+ return ChatSource;
9997
+ }(common$1.SocketRPC));
9998
+ __decorate([
9999
+ common$1.RpcEvent(),
10000
+ __metadata("design:type", Function),
10001
+ __metadata("design:paramtypes", [Object]),
10002
+ __metadata("design:returntype", void 0)
10003
+ ], ChatSource.prototype, "onPermissions", null);
10004
+ __decorate([
10005
+ common$1.RpcEvent(),
10006
+ __metadata("design:type", Function),
10007
+ __metadata("design:paramtypes", [Object]),
10008
+ __metadata("design:returntype", void 0)
10009
+ ], ChatSource.prototype, "onChatMessage", null);
10010
+
10011
+ var BANTA_SDK_OPTIONS = 'BANTA_SDK_OPTIONS';
10012
+
10013
+ var ChatBackend = /** @class */ (function (_super) {
10014
+ __extends(ChatBackend, _super);
10015
+ function ChatBackend(options) {
10016
+ var _this = _super.call(this) || this;
10017
+ _this.options = options;
10018
+ return _this;
10019
+ }
10020
+ Object.defineProperty(ChatBackend.prototype, "serviceUrl", {
10021
+ get: function () {
10022
+ var _a, _b;
10023
+ return "" + ((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.serviceUrl) !== null && _b !== void 0 ? _b : 'http://localhost:3422');
10024
+ },
10025
+ enumerable: false,
10026
+ configurable: true
10027
+ });
10028
+ ChatBackend.prototype.connectToService = function () {
10029
+ return __awaiter(this, void 0, void 0, function () {
10030
+ var socket;
10031
+ return __generator(this, function (_c) {
10032
+ switch (_c.label) {
10033
+ case 0:
10034
+ socket = new common$1.DurableSocket(this.serviceUrl.replace(/^http/, 'ws') + "/socket");
10035
+ return [4 /*yield*/, new Promise(function (resolve, reject) {
10036
+ socket.onopen = function () {
10037
+ resolve();
10038
+ };
10039
+ socket.onclose = function (e) {
10040
+ if (e.code === 503) {
10041
+ console.error("Failed to connect to chat service!");
10042
+ reject(e);
10043
+ }
10044
+ };
10045
+ })];
10046
+ case 1:
10047
+ _c.sent();
10048
+ socket.onerror = undefined;
10049
+ return [2 /*return*/, socket];
10050
+ }
10051
+ });
10052
+ });
10053
+ };
10054
+ ChatBackend.prototype.getSourceForTopic = function (topicId, options) {
10055
+ return __awaiter(this, void 0, void 0, function () {
10056
+ var _c, _d;
10057
+ return __generator(this, function (_e) {
10058
+ switch (_e.label) {
10059
+ case 0:
10060
+ _d = (_c = new ChatSource(this, topicId, undefined, (options === null || options === void 0 ? void 0 : options.sortOrder) || common$1.CommentsOrder.NEWEST))
10061
+ .bind;
10062
+ return [4 /*yield*/, this.connectToService()];
10063
+ case 1: return [4 /*yield*/, _d.apply(_c, [_e.sent()])];
10064
+ case 2: return [2 /*return*/, _e.sent()];
10065
+ }
10066
+ });
10067
+ });
10068
+ };
10069
+ ChatBackend.prototype.getSourceForThread = function (topicId, messageId, options) {
10070
+ return __awaiter(this, void 0, void 0, function () {
10071
+ var _c, _d;
10072
+ return __generator(this, function (_e) {
10073
+ switch (_e.label) {
10074
+ case 0:
10075
+ _d = (_c = new ChatSource(this, topicId, messageId, (options === null || options === void 0 ? void 0 : options.sortOrder) || common$1.CommentsOrder.NEWEST))
10076
+ .bind;
10077
+ return [4 /*yield*/, this.connectToService()];
10078
+ case 1: return [4 /*yield*/, _d.apply(_c, [_e.sent()])];
10079
+ case 2: return [2 /*return*/, _e.sent()];
10080
+ }
10081
+ });
10082
+ });
10083
+ };
10084
+ ChatBackend.prototype.getSourceCountForTopic = function (topicId) {
10085
+ return __awaiter(this, void 0, void 0, function () {
10086
+ var response, topic;
10087
+ return __generator(this, function (_c) {
10088
+ switch (_c.label) {
10089
+ case 0: return [4 /*yield*/, fetch(this.serviceUrl + "/topics/" + topicId)];
10090
+ case 1:
10091
+ response = _c.sent();
10092
+ if (response.status >= 400)
10093
+ return [2 /*return*/, 0];
10094
+ return [4 /*yield*/, response.json()];
10095
+ case 2:
10096
+ topic = _c.sent();
10097
+ return [2 /*return*/, topic.messageCount || 0];
10098
+ }
10099
+ });
10100
+ });
10101
+ };
10102
+ ChatBackend.prototype.refreshMessage = function (message) {
10103
+ throw new Error("Method not implemented.");
10104
+ };
10105
+ ChatBackend.prototype.getMessage = function (topicId, messageId) {
10106
+ throw new Error("Method not implemented.");
10107
+ };
10108
+ ChatBackend.prototype.getSubMessage = function (topicId, parentMessageId, messageId) {
10109
+ throw new Error("Method not implemented.");
10110
+ };
10111
+ ChatBackend.prototype.watchMessage = function (message, handler) {
10112
+ throw new Error("Method not implemented.");
10113
+ };
10114
+ return ChatBackend;
10115
+ }(ChatBackendBase));
10116
+ ChatBackend.decorators = [
10117
+ { type: core.Injectable }
10118
+ ];
10119
+ ChatBackend.ctorParameters = function () { return [
10120
+ { type: undefined, decorators: [{ type: core.Inject, args: [BANTA_SDK_OPTIONS,] }] }
10121
+ ]; };
10122
+
9385
10123
  var BantaSdkModule = /** @class */ (function () {
9386
10124
  function BantaSdkModule() {
9387
10125
  }
9388
- BantaSdkModule.forRoot = function () {
10126
+ BantaSdkModule.configure = function (options) {
9389
10127
  return {
9390
10128
  ngModule: BantaSdkModule,
9391
10129
  providers: [
9392
- BantaService
10130
+ {
10131
+ provide: BANTA_SDK_OPTIONS,
10132
+ useValue: options || {}
10133
+ },
10134
+ { provide: ChatBackendBase, useClass: ChatBackend }
9393
10135
  ]
9394
10136
  };
9395
10137
  };
@@ -9436,16 +10178,18 @@
9436
10178
  * Generated bundle index. Do not edit.
9437
10179
  */
9438
10180
 
10181
+ exports.BANTA_SDK_OPTIONS = BANTA_SDK_OPTIONS;
9439
10182
  exports.BantaChatComponent = BantaChatComponent;
9440
10183
  exports.BantaCommentsComponent = BantaCommentsComponent;
9441
10184
  exports.BantaCommonModule = BantaCommonModule;
9442
10185
  exports.BantaComponent = BantaComponent;
9443
10186
  exports.BantaLogoComponent = BantaLogoComponent;
9444
10187
  exports.BantaSdkModule = BantaSdkModule;
9445
- exports.BantaService = BantaService;
9446
- exports.ChatBackendService = ChatBackendService;
10188
+ exports.ChatBackend = ChatBackend;
10189
+ exports.ChatBackendBase = ChatBackendBase;
9447
10190
  exports.ChatMessageComponent = ChatMessageComponent;
9448
10191
  exports.ChatModule = ChatModule;
10192
+ exports.ChatSource = ChatSource;
9449
10193
  exports.ChatViewComponent = ChatViewComponent;
9450
10194
  exports.CommentComponent = CommentComponent;
9451
10195
  exports.CommentFieldComponent = CommentFieldComponent;