@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.
- package/banta-sdk.metadata.json +1 -1
- package/bundles/banta-sdk.umd.js +1157 -413
- package/bundles/banta-sdk.umd.js.map +1 -1
- package/bundles/banta-sdk.umd.min.js +1 -1
- package/bundles/banta-sdk.umd.min.js.map +1 -1
- package/esm2015/lib/banta/banta.component.js +9 -15
- package/esm2015/lib/banta-sdk.module.js +10 -4
- package/esm2015/lib/chat/banta-chat/banta-chat.component.js +16 -19
- package/esm2015/lib/chat/chat-message/chat-message.component.js +2 -2
- package/esm2015/lib/chat/chat-view/chat-view.component.js +7 -6
- package/esm2015/lib/chat/live-chat-message.component.js +3 -3
- package/esm2015/lib/chat-backend-base.js +17 -0
- package/esm2015/lib/chat-backend.js +74 -0
- package/esm2015/lib/chat-source-base.js +2 -0
- package/esm2015/lib/chat-source.js +151 -0
- package/esm2015/lib/comments/banta-comments/banta-comments.component.js +344 -174
- package/esm2015/lib/comments/comment/comment.component.js +56 -20
- package/esm2015/lib/comments/comment-field/comment-field.component.js +19 -17
- package/esm2015/lib/comments/comment-view/comment-view.component.js +77 -39
- package/esm2015/lib/comments/live-comment.component.js +3 -3
- package/esm2015/lib/common/index.js +1 -3
- package/esm2015/lib/index.js +6 -1
- package/esm2015/lib/sdk-options.js +2 -0
- package/fesm2015/banta-sdk.js +761 -310
- package/fesm2015/banta-sdk.js.map +1 -1
- package/lib/banta/banta.component.d.ts +7 -8
- package/lib/banta-sdk.module.d.ts +2 -1
- package/lib/chat/banta-chat/banta-chat.component.d.ts +10 -12
- package/lib/chat/chat-view/chat-view.component.d.ts +7 -4
- package/lib/chat/live-chat-message.component.d.ts +2 -2
- package/lib/chat-backend-base.d.ts +22 -0
- package/lib/chat-backend.d.ts +21 -0
- package/lib/chat-source-base.d.ts +31 -0
- package/lib/chat-source.d.ts +38 -0
- package/lib/comments/banta-comments/banta-comments.component.d.ts +68 -61
- package/lib/comments/comment/comment.component.d.ts +25 -6
- package/lib/comments/comment-field/comment-field.component.d.ts +9 -5
- package/lib/comments/comment-view/comment-view.component.d.ts +26 -8
- package/lib/comments/live-comment.component.d.ts +2 -2
- package/lib/common/index.d.ts +0 -2
- package/lib/index.d.ts +5 -0
- package/lib/sdk-options.d.ts +4 -0
- package/package.json +1 -1
- package/esm2015/lib/common/banta.service.js +0 -21
- package/esm2015/lib/common/chat-backend.service.js +0 -7
- package/lib/common/banta.service.d.ts +0 -9
- package/lib/common/chat-backend.service.d.ts +0 -14
package/fesm2015/banta-sdk.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observable, Subject, BehaviorSubject, Subscription } from 'rxjs';
|
|
2
2
|
import { publish } from 'rxjs/operators';
|
|
3
|
-
import {
|
|
3
|
+
import { Component, Input, NgModule, Output, ViewChild, ElementRef, HostBinding, Injectable, Inject } from '@angular/core';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
6
6
|
import { MatIconModule } from '@angular/material/icon';
|
|
@@ -8,9 +8,9 @@ import { MatButtonModule } from '@angular/material/button';
|
|
|
8
8
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
9
9
|
import { MatInputModule } from '@angular/material/input';
|
|
10
10
|
import { FormsModule } from '@angular/forms';
|
|
11
|
-
import { __awaiter } from 'tslib';
|
|
11
|
+
import { __awaiter, __decorate, __metadata } from 'tslib';
|
|
12
12
|
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
|
13
|
-
import { CommentsOrder } from '@banta/common';
|
|
13
|
+
import { CommentsOrder, SocketRPC, RpcEvent, DurableSocket } from '@banta/common';
|
|
14
14
|
import { ActivatedRoute } from '@angular/router';
|
|
15
15
|
import { MatMenuModule } from '@angular/material/menu';
|
|
16
16
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
@@ -31,25 +31,6 @@ function lazyConnection(options) {
|
|
|
31
31
|
return obs.pipe(publish()).refCount();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
class BantaService {
|
|
35
|
-
constructor() {
|
|
36
|
-
this._userChanged = new BehaviorSubject(null);
|
|
37
|
-
}
|
|
38
|
-
get userChanged() {
|
|
39
|
-
return this._userChanged;
|
|
40
|
-
}
|
|
41
|
-
set user(user) {
|
|
42
|
-
this._user = user;
|
|
43
|
-
this._userChanged.next(user);
|
|
44
|
-
}
|
|
45
|
-
get user() {
|
|
46
|
-
return this._user;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
BantaService.decorators = [
|
|
50
|
-
{ type: Injectable }
|
|
51
|
-
];
|
|
52
|
-
|
|
53
34
|
class TimestampComponent {
|
|
54
35
|
constructor() {
|
|
55
36
|
this.relative = '';
|
|
@@ -155,12 +136,6 @@ TimestampComponent.propDecorators = {
|
|
|
155
136
|
value: [{ type: Input }]
|
|
156
137
|
};
|
|
157
138
|
|
|
158
|
-
class ChatBackendService {
|
|
159
|
-
}
|
|
160
|
-
ChatBackendService.decorators = [
|
|
161
|
-
{ type: Injectable }
|
|
162
|
-
];
|
|
163
|
-
|
|
164
139
|
const COMPONENTS = [
|
|
165
140
|
TimestampComponent
|
|
166
141
|
];
|
|
@@ -6956,7 +6931,7 @@ class ChatMessageComponent {
|
|
|
6956
6931
|
ChatMessageComponent.decorators = [
|
|
6957
6932
|
{ type: Component, args: [{
|
|
6958
6933
|
selector: 'banta-chat-message',
|
|
6959
|
-
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.
|
|
6934
|
+
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>",
|
|
6960
6935
|
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}"]
|
|
6961
6936
|
},] }
|
|
6962
6937
|
];
|
|
@@ -6968,6 +6943,22 @@ ChatMessageComponent.propDecorators = {
|
|
|
6968
6943
|
upvoted: [{ type: Output }]
|
|
6969
6944
|
};
|
|
6970
6945
|
|
|
6946
|
+
class ChatBackendBase {
|
|
6947
|
+
constructor() {
|
|
6948
|
+
this._userChanged = new BehaviorSubject(null);
|
|
6949
|
+
}
|
|
6950
|
+
get userChanged() {
|
|
6951
|
+
return this._userChanged;
|
|
6952
|
+
}
|
|
6953
|
+
set user(user) {
|
|
6954
|
+
this._user = user;
|
|
6955
|
+
this._userChanged.next(user);
|
|
6956
|
+
}
|
|
6957
|
+
get user() {
|
|
6958
|
+
return this._user;
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6961
|
+
|
|
6971
6962
|
class LiveChatMessageComponent {
|
|
6972
6963
|
constructor(backend) {
|
|
6973
6964
|
this.backend = backend;
|
|
@@ -7027,7 +7018,7 @@ LiveChatMessageComponent.decorators = [
|
|
|
7027
7018
|
},] }
|
|
7028
7019
|
];
|
|
7029
7020
|
LiveChatMessageComponent.ctorParameters = () => [
|
|
7030
|
-
{ type:
|
|
7021
|
+
{ type: ChatBackendBase }
|
|
7031
7022
|
];
|
|
7032
7023
|
LiveChatMessageComponent.propDecorators = {
|
|
7033
7024
|
upvoted: [{ type: Output }],
|
|
@@ -7037,7 +7028,8 @@ LiveChatMessageComponent.propDecorators = {
|
|
|
7037
7028
|
};
|
|
7038
7029
|
|
|
7039
7030
|
class ChatViewComponent {
|
|
7040
|
-
constructor(elementRef) {
|
|
7031
|
+
constructor(backend, elementRef) {
|
|
7032
|
+
this.backend = backend;
|
|
7041
7033
|
this.elementRef = elementRef;
|
|
7042
7034
|
this._sourceSubs = new Subscription();
|
|
7043
7035
|
this._selected = new Subject();
|
|
@@ -7078,10 +7070,8 @@ class ChatViewComponent {
|
|
|
7078
7070
|
console.dir(this.messages);
|
|
7079
7071
|
this._sourceSubs.add(this._source.messageReceived.subscribe(msg => this.messageReceived(msg)));
|
|
7080
7072
|
this._sourceSubs.add(this._source.messageSent.subscribe(msg => this.messageSent(msg)));
|
|
7081
|
-
|
|
7082
|
-
|
|
7083
|
-
.subscribe(user => this.currentUser = user));
|
|
7084
|
-
}
|
|
7073
|
+
this._sourceSubs.add(this.backend.userChanged
|
|
7074
|
+
.subscribe(user => this.currentUser = user));
|
|
7085
7075
|
}
|
|
7086
7076
|
}
|
|
7087
7077
|
addMessage(message) {
|
|
@@ -7167,6 +7157,7 @@ ChatViewComponent.decorators = [
|
|
|
7167
7157
|
},] }
|
|
7168
7158
|
];
|
|
7169
7159
|
ChatViewComponent.ctorParameters = () => [
|
|
7160
|
+
{ type: ChatBackendBase },
|
|
7170
7161
|
{ type: ElementRef }
|
|
7171
7162
|
];
|
|
7172
7163
|
ChatViewComponent.propDecorators = {
|
|
@@ -7183,10 +7174,8 @@ ChatViewComponent.propDecorators = {
|
|
|
7183
7174
|
* Chat component
|
|
7184
7175
|
*/
|
|
7185
7176
|
class BantaChatComponent {
|
|
7186
|
-
constructor(
|
|
7187
|
-
this.banta = banta;
|
|
7177
|
+
constructor(backend) {
|
|
7188
7178
|
this.backend = backend;
|
|
7189
|
-
this.elementRef = elementRef;
|
|
7190
7179
|
this._subs = new Subscription();
|
|
7191
7180
|
this.user = null;
|
|
7192
7181
|
this.signInLabel = 'Sign In';
|
|
@@ -7202,7 +7191,7 @@ class BantaChatComponent {
|
|
|
7202
7191
|
this.newMessage = {};
|
|
7203
7192
|
}
|
|
7204
7193
|
ngOnInit() {
|
|
7205
|
-
this._subs.add(this.
|
|
7194
|
+
this._subs.add(this.backend.userChanged.subscribe(user => this.user = user));
|
|
7206
7195
|
}
|
|
7207
7196
|
ngOnDestroy() {
|
|
7208
7197
|
this._subs.unsubscribe();
|
|
@@ -7236,8 +7225,8 @@ class BantaChatComponent {
|
|
|
7236
7225
|
showSignIn() {
|
|
7237
7226
|
this._signInSelected.next();
|
|
7238
7227
|
}
|
|
7239
|
-
sendPermissionError() {
|
|
7240
|
-
this._permissionDeniedError.next();
|
|
7228
|
+
sendPermissionError(message) {
|
|
7229
|
+
this._permissionDeniedError.next(message);
|
|
7241
7230
|
}
|
|
7242
7231
|
insertEmoji(emoji) {
|
|
7243
7232
|
let message = this.newMessage.message || '';
|
|
@@ -7275,14 +7264,15 @@ class BantaChatComponent {
|
|
|
7275
7264
|
return this._userSelected;
|
|
7276
7265
|
}
|
|
7277
7266
|
get canChat() {
|
|
7278
|
-
var _a;
|
|
7279
7267
|
if (!this.user)
|
|
7280
7268
|
return false;
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
return
|
|
7269
|
+
// TODO
|
|
7270
|
+
// if (!this.user.permissions)
|
|
7271
|
+
// return true;
|
|
7272
|
+
// if (!this.user.permissions.canChat)
|
|
7273
|
+
// return true;
|
|
7274
|
+
// return this.user.permissions?.canChat(this.source);
|
|
7275
|
+
return true;
|
|
7286
7276
|
}
|
|
7287
7277
|
sendMessage() {
|
|
7288
7278
|
var _a;
|
|
@@ -7296,7 +7286,7 @@ class BantaChatComponent {
|
|
|
7296
7286
|
let message = {
|
|
7297
7287
|
user: null,
|
|
7298
7288
|
sentAt: Date.now(),
|
|
7299
|
-
|
|
7289
|
+
likes: 0,
|
|
7300
7290
|
url: location.href,
|
|
7301
7291
|
message: text
|
|
7302
7292
|
};
|
|
@@ -7321,9 +7311,7 @@ BantaChatComponent.decorators = [
|
|
|
7321
7311
|
},] }
|
|
7322
7312
|
];
|
|
7323
7313
|
BantaChatComponent.ctorParameters = () => [
|
|
7324
|
-
{ type:
|
|
7325
|
-
{ type: ChatBackendService },
|
|
7326
|
-
{ type: ElementRef }
|
|
7314
|
+
{ type: ChatBackendBase }
|
|
7327
7315
|
];
|
|
7328
7316
|
BantaChatComponent.propDecorators = {
|
|
7329
7317
|
shouldInterceptMessageSend: [{ type: Input }],
|
|
@@ -7367,8 +7355,7 @@ ChatModule.decorators = [
|
|
|
7367
7355
|
* Unified chat and comments component
|
|
7368
7356
|
*/
|
|
7369
7357
|
class BantaComponent {
|
|
7370
|
-
constructor(
|
|
7371
|
-
this.banta = banta;
|
|
7358
|
+
constructor(backend, matDialog) {
|
|
7372
7359
|
this.backend = backend;
|
|
7373
7360
|
this.matDialog = matDialog;
|
|
7374
7361
|
this._subs = new Subscription();
|
|
@@ -7386,7 +7373,7 @@ class BantaComponent {
|
|
|
7386
7373
|
this.genericAvatarUrl = 'https://gravatar.com/avatar/915c804e0be607a4ad766ddadea5c48a?s=512&d=https://codepen.io/assets/avatars/user-avatar-512x512-6e240cf350d2f1cc07c2bed234c3a3bb5f1b237023c204c782622e80d6b212ba.png';
|
|
7387
7374
|
}
|
|
7388
7375
|
ngOnInit() {
|
|
7389
|
-
this._subs.add(this.
|
|
7376
|
+
this._subs.add(this.backend.userChanged.subscribe(user => this.currentUser = user));
|
|
7390
7377
|
this._subs.add(this.backend.notificationsChanged.subscribe(notifs => this.notifications = notifs));
|
|
7391
7378
|
this._subs.add(this.backend.newNotification.subscribe(notif => {
|
|
7392
7379
|
this.newNotifications = true;
|
|
@@ -7401,7 +7388,7 @@ class BantaComponent {
|
|
|
7401
7388
|
let message = {
|
|
7402
7389
|
user: null,
|
|
7403
7390
|
sentAt: Date.now(),
|
|
7404
|
-
|
|
7391
|
+
likes: 0,
|
|
7405
7392
|
message: text
|
|
7406
7393
|
};
|
|
7407
7394
|
try {
|
|
@@ -7514,11 +7501,8 @@ class BantaComponent {
|
|
|
7514
7501
|
}
|
|
7515
7502
|
upvoteMessage(message) {
|
|
7516
7503
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
else
|
|
7520
|
-
yield this.backend.upvoteMessage(message.topicId, message.id);
|
|
7521
|
-
//message.upvotes += 1;
|
|
7504
|
+
// TODO
|
|
7505
|
+
//await this.backend.likeMessage(message.id);
|
|
7522
7506
|
});
|
|
7523
7507
|
}
|
|
7524
7508
|
showProfile(user) {
|
|
@@ -7537,13 +7521,12 @@ class BantaComponent {
|
|
|
7537
7521
|
BantaComponent.decorators = [
|
|
7538
7522
|
{ type: Component, args: [{
|
|
7539
7523
|
selector: `banta`,
|
|
7540
|
-
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 \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>",
|
|
7524
|
+
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 \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>",
|
|
7541
7525
|
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}"]
|
|
7542
7526
|
},] }
|
|
7543
7527
|
];
|
|
7544
7528
|
BantaComponent.ctorParameters = () => [
|
|
7545
|
-
{ type:
|
|
7546
|
-
{ type: ChatBackendService },
|
|
7529
|
+
{ type: ChatBackendBase },
|
|
7547
7530
|
{ type: MatDialog }
|
|
7548
7531
|
];
|
|
7549
7532
|
BantaComponent.propDecorators = {
|
|
@@ -7650,7 +7633,8 @@ class CommentComponent {
|
|
|
7650
7633
|
constructor() {
|
|
7651
7634
|
this._reported = new Subject();
|
|
7652
7635
|
this._selected = new Subject();
|
|
7653
|
-
this.
|
|
7636
|
+
this._liked = new Subject();
|
|
7637
|
+
this._unliked = new Subject();
|
|
7654
7638
|
this._shared = new Subject();
|
|
7655
7639
|
this._userSelected = new Subject();
|
|
7656
7640
|
this._avatarSelected = new Subject();
|
|
@@ -7658,7 +7642,12 @@ class CommentComponent {
|
|
|
7658
7642
|
this.isNew = false;
|
|
7659
7643
|
this.visible = false;
|
|
7660
7644
|
this.showReplyAction = true;
|
|
7661
|
-
this.
|
|
7645
|
+
this.mine = false;
|
|
7646
|
+
this.editing = false;
|
|
7647
|
+
this._editStarted = new Subject();
|
|
7648
|
+
this._deleted = new Subject();
|
|
7649
|
+
this._editEnded = new Subject();
|
|
7650
|
+
this._edited = new Subject();
|
|
7662
7651
|
}
|
|
7663
7652
|
ngOnInit() {
|
|
7664
7653
|
let maxTime = 500;
|
|
@@ -7670,6 +7659,10 @@ class CommentComponent {
|
|
|
7670
7659
|
setTimeout(() => this.isNew = false, 1000);
|
|
7671
7660
|
}, randomTime);
|
|
7672
7661
|
}
|
|
7662
|
+
get isHighlighted() {
|
|
7663
|
+
var _a, _b, _c;
|
|
7664
|
+
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;
|
|
7665
|
+
}
|
|
7673
7666
|
get userSelected() {
|
|
7674
7667
|
return this._userSelected.asObservable();
|
|
7675
7668
|
}
|
|
@@ -7682,24 +7675,42 @@ class CommentComponent {
|
|
|
7682
7675
|
get reported() {
|
|
7683
7676
|
return this._reported.asObservable();
|
|
7684
7677
|
}
|
|
7685
|
-
|
|
7686
|
-
|
|
7678
|
+
saveEdit() {
|
|
7679
|
+
this._edited.next(this.editedMessage);
|
|
7687
7680
|
}
|
|
7688
|
-
|
|
7689
|
-
|
|
7681
|
+
endEditing() {
|
|
7682
|
+
this._editEnded.next();
|
|
7690
7683
|
}
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7684
|
+
startEdit() {
|
|
7685
|
+
this._editStarted.next();
|
|
7686
|
+
this.editedMessage = this.message.message;
|
|
7687
|
+
}
|
|
7688
|
+
delete() {
|
|
7689
|
+
this._deleted.next();
|
|
7694
7690
|
}
|
|
7695
|
-
get
|
|
7696
|
-
return this.
|
|
7691
|
+
get liked() {
|
|
7692
|
+
return this._liked.asObservable();
|
|
7697
7693
|
}
|
|
7694
|
+
get unliked() {
|
|
7695
|
+
return this._unliked.asObservable();
|
|
7696
|
+
}
|
|
7697
|
+
get selected() {
|
|
7698
|
+
return this._selected.asObservable();
|
|
7699
|
+
}
|
|
7700
|
+
get edited() { return this._edited.asObservable(); }
|
|
7701
|
+
get deleted() { return this._deleted.asObservable(); }
|
|
7702
|
+
get editStarted() { return this._editStarted.asObservable(); }
|
|
7703
|
+
get editEnded() { return this._editEnded.asObservable(); }
|
|
7704
|
+
get shared() { return this._shared.asObservable(); }
|
|
7705
|
+
get commentId() { var _a; return (_a = this.message) === null || _a === void 0 ? void 0 : _a.id; }
|
|
7698
7706
|
report() {
|
|
7699
7707
|
this._reported.next();
|
|
7700
7708
|
}
|
|
7701
|
-
|
|
7702
|
-
this.
|
|
7709
|
+
like() {
|
|
7710
|
+
this._liked.next();
|
|
7711
|
+
}
|
|
7712
|
+
unlike() {
|
|
7713
|
+
this._unliked.next();
|
|
7703
7714
|
}
|
|
7704
7715
|
share() {
|
|
7705
7716
|
this._shared.next(this.message);
|
|
@@ -7729,12 +7740,13 @@ class CommentComponent {
|
|
|
7729
7740
|
CommentComponent.decorators = [
|
|
7730
7741
|
{ type: Component, args: [{
|
|
7731
7742
|
selector: 'banta-comment',
|
|
7732
|
-
template: "\r\n<mat-menu #pointItemMenu=\"matMenu\">\r\n <button mat-menu-item (click)=\"report()\">Report</button>\r\n <button mat-menu-item>
|
|
7733
|
-
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{
|
|
7743
|
+
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> \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",
|
|
7744
|
+
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}"]
|
|
7734
7745
|
},] }
|
|
7735
7746
|
];
|
|
7736
7747
|
CommentComponent.propDecorators = {
|
|
7737
7748
|
isNew: [{ type: HostBinding, args: ['class.new',] }],
|
|
7749
|
+
isHighlighted: [{ type: HostBinding, args: ['class.highlighted',] }],
|
|
7738
7750
|
visible: [{ type: HostBinding, args: ['class.visible',] }],
|
|
7739
7751
|
message: [{ type: Input }],
|
|
7740
7752
|
showReplyAction: [{ type: Input }],
|
|
@@ -7742,11 +7754,18 @@ CommentComponent.propDecorators = {
|
|
|
7742
7754
|
usernameSelected: [{ type: Output }],
|
|
7743
7755
|
avatarSelected: [{ type: Output }],
|
|
7744
7756
|
reported: [{ type: Output }],
|
|
7745
|
-
|
|
7746
|
-
|
|
7757
|
+
permissions: [{ type: Input }],
|
|
7758
|
+
mine: [{ type: Input }],
|
|
7759
|
+
editing: [{ type: Input }],
|
|
7760
|
+
liked: [{ type: Output }],
|
|
7761
|
+
unliked: [{ type: Output }],
|
|
7747
7762
|
selected: [{ type: Output }],
|
|
7748
|
-
|
|
7749
|
-
|
|
7763
|
+
edited: [{ type: Output }],
|
|
7764
|
+
deleted: [{ type: Output }],
|
|
7765
|
+
editStarted: [{ type: Output }],
|
|
7766
|
+
editEnded: [{ type: Output }],
|
|
7767
|
+
shared: [{ type: Output }],
|
|
7768
|
+
commentId: [{ type: HostBinding, args: ['attr.data-comment-id',] }]
|
|
7750
7769
|
};
|
|
7751
7770
|
|
|
7752
7771
|
class CommentViewComponent {
|
|
@@ -7754,16 +7773,20 @@ class CommentViewComponent {
|
|
|
7754
7773
|
this.backend = backend;
|
|
7755
7774
|
this._sourceSubs = new Subscription();
|
|
7756
7775
|
this._selected = new Subject();
|
|
7757
|
-
this.
|
|
7776
|
+
this._liked = new Subject();
|
|
7777
|
+
this._unliked = new Subject();
|
|
7758
7778
|
this._reported = new Subject();
|
|
7759
7779
|
this._userSelected = new Subject();
|
|
7760
7780
|
this._usernameSelected = new Subject();
|
|
7761
7781
|
this._avatarSelected = new Subject();
|
|
7762
7782
|
this._shared = new Subject();
|
|
7783
|
+
this._deleted = new Subject();
|
|
7784
|
+
this._messageEdited = new Subject();
|
|
7763
7785
|
this.showEmptyState = true;
|
|
7764
7786
|
this.allowReplies = true;
|
|
7765
7787
|
this.menuMessage = null;
|
|
7766
7788
|
this.messages = [];
|
|
7789
|
+
this.customSortEnabled = false;
|
|
7767
7790
|
this.maxMessages = 2000;
|
|
7768
7791
|
this.maxVisibleMessages = 200;
|
|
7769
7792
|
this.newestLast = false;
|
|
@@ -7772,33 +7795,33 @@ class CommentViewComponent {
|
|
|
7772
7795
|
this.hasMore = false;
|
|
7773
7796
|
this.newMessages = [];
|
|
7774
7797
|
this.olderMessages = [];
|
|
7798
|
+
this.sortOrderChanged = new Subject();
|
|
7775
7799
|
}
|
|
7776
7800
|
get selected() {
|
|
7777
7801
|
return this._selected;
|
|
7778
7802
|
}
|
|
7779
|
-
get
|
|
7780
|
-
return this.
|
|
7803
|
+
get messageEdited() {
|
|
7804
|
+
return this._messageEdited.asObservable();
|
|
7781
7805
|
}
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
}
|
|
7785
|
-
get upvoted() {
|
|
7786
|
-
return this._upvoted;
|
|
7787
|
-
}
|
|
7788
|
-
get usernameSelected() {
|
|
7789
|
-
return this._usernameSelected;
|
|
7790
|
-
}
|
|
7791
|
-
get avatarSelected() {
|
|
7792
|
-
return this._avatarSelected;
|
|
7793
|
-
}
|
|
7794
|
-
get shared() {
|
|
7795
|
-
return this._shared;
|
|
7806
|
+
saveEdit(message, newMessage) {
|
|
7807
|
+
this._messageEdited.next({ message, newMessage });
|
|
7796
7808
|
}
|
|
7809
|
+
get userSelected() { return this._userSelected; }
|
|
7810
|
+
get reported() { return this._reported; }
|
|
7811
|
+
get liked() { return this._liked; }
|
|
7812
|
+
get unliked() { return this._unliked; }
|
|
7813
|
+
get usernameSelected() { return this._usernameSelected; }
|
|
7814
|
+
get avatarSelected() { return this._avatarSelected; }
|
|
7815
|
+
get shared() { return this._shared; }
|
|
7816
|
+
get deleted() { return this._deleted; }
|
|
7797
7817
|
get source() {
|
|
7798
7818
|
return this._source;
|
|
7799
7819
|
}
|
|
7800
|
-
|
|
7801
|
-
this.
|
|
7820
|
+
likeMessage(message) {
|
|
7821
|
+
this._liked.next(message);
|
|
7822
|
+
}
|
|
7823
|
+
unlikeMessage(message) {
|
|
7824
|
+
this._unliked.next(message);
|
|
7802
7825
|
}
|
|
7803
7826
|
reportMessage(message) {
|
|
7804
7827
|
this._reported.next(message);
|
|
@@ -7818,31 +7841,51 @@ class CommentViewComponent {
|
|
|
7818
7841
|
sharedMessage(message) {
|
|
7819
7842
|
this._shared.next(message);
|
|
7820
7843
|
}
|
|
7844
|
+
startEditing(message) {
|
|
7845
|
+
this.messages.forEach(m => m.transientState.editing = false);
|
|
7846
|
+
message.transientState.editing = true;
|
|
7847
|
+
}
|
|
7848
|
+
deleteMessage(message) {
|
|
7849
|
+
this._deleted.next(message);
|
|
7850
|
+
}
|
|
7821
7851
|
set source(value) {
|
|
7852
|
+
this.customSortEnabled = (value === null || value === void 0 ? void 0 : value.sortOrder) !== CommentsOrder.NEWEST;
|
|
7853
|
+
this.newMessages = [];
|
|
7854
|
+
this.olderMessages = [];
|
|
7855
|
+
window.bantaSourceDebug = value;
|
|
7822
7856
|
if (this._sourceSubs) {
|
|
7823
7857
|
this._sourceSubs.unsubscribe();
|
|
7824
7858
|
this._sourceSubs = null;
|
|
7825
7859
|
}
|
|
7826
7860
|
this._source = value;
|
|
7827
7861
|
if (value) {
|
|
7828
|
-
console.log(`[banta-comment-view] Subscribing to source...`);
|
|
7829
7862
|
const messages = (value.messages || []).slice();
|
|
7830
7863
|
this.messages = messages;
|
|
7831
7864
|
this.olderMessages = messages.splice(this.maxVisibleMessages, messages.length);
|
|
7832
|
-
this.hasMore = this.olderMessages.length > 0;
|
|
7865
|
+
this.hasMore = true; //this.olderMessages.length > 0;
|
|
7833
7866
|
this._sourceSubs = new Subscription();
|
|
7834
7867
|
this._sourceSubs.add(this._source.messageReceived.subscribe(msg => this.messageReceived(msg)));
|
|
7835
7868
|
this._sourceSubs.add(this._source.messageSent.subscribe(msg => this.messageSent(msg)));
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
}
|
|
7869
|
+
this._sourceSubs.add(this.backend.userChanged.subscribe(user => this.currentUser = user));
|
|
7870
|
+
this.getInitialMessages();
|
|
7839
7871
|
}
|
|
7840
7872
|
}
|
|
7873
|
+
getInitialMessages() {
|
|
7874
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
7875
|
+
let messages = (yield this._source.getExistingMessages());
|
|
7876
|
+
messages.forEach(m => { var _a; return (_a = m.transientState) !== null && _a !== void 0 ? _a : (m.transientState = {}); });
|
|
7877
|
+
this.messages = messages;
|
|
7878
|
+
});
|
|
7879
|
+
}
|
|
7841
7880
|
messageIdentity(index, chatMessage) {
|
|
7842
7881
|
return chatMessage.id;
|
|
7843
7882
|
}
|
|
7844
7883
|
showNew() {
|
|
7845
7884
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7885
|
+
if (this.source && this.source.sortOrder !== CommentsOrder.NEWEST) {
|
|
7886
|
+
this.sortOrderChanged.next(CommentsOrder.NEWEST);
|
|
7887
|
+
return;
|
|
7888
|
+
}
|
|
7846
7889
|
this.isViewingMore = false;
|
|
7847
7890
|
this.messages = this.newMessages.splice(0, this.newMessages.length).concat(this.messages);
|
|
7848
7891
|
let overflow = this.messages.splice(this.maxVisibleMessages, this.messages.length);
|
|
@@ -7858,29 +7901,38 @@ class CommentViewComponent {
|
|
|
7858
7901
|
this.messages = this.messages.concat(this.olderMessages.splice(0, 50));
|
|
7859
7902
|
}
|
|
7860
7903
|
else {
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7904
|
+
this.isLoadingMore = true;
|
|
7905
|
+
let nextPageSize = 20;
|
|
7906
|
+
let lastMessage;
|
|
7907
|
+
lastMessage = this.messages[this.messages.length - 1];
|
|
7908
|
+
if (!lastMessage) {
|
|
7866
7909
|
this.isLoadingMore = false;
|
|
7867
|
-
|
|
7868
|
-
|
|
7910
|
+
this.hasMore = false;
|
|
7911
|
+
return;
|
|
7869
7912
|
}
|
|
7870
|
-
|
|
7913
|
+
let messages = yield this.source.loadAfter(lastMessage, nextPageSize);
|
|
7914
|
+
messages.forEach(m => { var _a; return (_a = m.transientState) !== null && _a !== void 0 ? _a : (m.transientState = {}); });
|
|
7915
|
+
this.messages = this.messages.concat(messages);
|
|
7916
|
+
this.isLoadingMore = false;
|
|
7917
|
+
if (messages.length === 0) {
|
|
7918
|
+
console.log(`Reached the end of the list.`);
|
|
7871
7919
|
this.hasMore = false;
|
|
7872
7920
|
}
|
|
7873
7921
|
}
|
|
7874
7922
|
});
|
|
7875
7923
|
}
|
|
7876
7924
|
addMessage(message) {
|
|
7925
|
+
var _a;
|
|
7926
|
+
if (!message.transientState)
|
|
7927
|
+
(_a = message.transientState) !== null && _a !== void 0 ? _a : (message.transientState = {});
|
|
7877
7928
|
let destination = this.messages;
|
|
7878
7929
|
let bucket = this.olderMessages;
|
|
7879
7930
|
if (this.isViewingMore) {
|
|
7880
7931
|
destination = this.newMessages;
|
|
7881
7932
|
bucket = null;
|
|
7882
7933
|
}
|
|
7883
|
-
|
|
7934
|
+
let newestLast = this.newestLast;
|
|
7935
|
+
if (newestLast) {
|
|
7884
7936
|
destination.push(message);
|
|
7885
7937
|
let overflow = destination.splice(this.maxVisibleMessages, destination.length);
|
|
7886
7938
|
bucket === null || bucket === void 0 ? void 0 : bucket.push(...overflow);
|
|
@@ -7935,41 +7987,65 @@ class CommentViewComponent {
|
|
|
7935
7987
|
CommentViewComponent.decorators = [
|
|
7936
7988
|
{ type: Component, args: [{
|
|
7937
7989
|
selector: 'banta-comment-view',
|
|
7938
|
-
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 [
|
|
7990
|
+
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",
|
|
7939
7991
|
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}}"]
|
|
7940
7992
|
},] }
|
|
7941
7993
|
];
|
|
7942
7994
|
CommentViewComponent.ctorParameters = () => [
|
|
7943
|
-
{ type:
|
|
7995
|
+
{ type: ChatBackendBase }
|
|
7944
7996
|
];
|
|
7945
7997
|
CommentViewComponent.propDecorators = {
|
|
7946
7998
|
showEmptyState: [{ type: Input }],
|
|
7947
7999
|
allowReplies: [{ type: Input }],
|
|
7948
8000
|
fixedHeight: [{ type: Input }, { type: HostBinding, args: ['class.fixed-height',] }],
|
|
7949
8001
|
selected: [{ type: Output }],
|
|
8002
|
+
messageEdited: [{ type: Output }],
|
|
7950
8003
|
userSelected: [{ type: Output }],
|
|
7951
8004
|
reported: [{ type: Output }],
|
|
7952
|
-
|
|
8005
|
+
liked: [{ type: Output }],
|
|
8006
|
+
unliked: [{ type: Output }],
|
|
7953
8007
|
usernameSelected: [{ type: Output }],
|
|
7954
8008
|
avatarSelected: [{ type: Output }],
|
|
7955
8009
|
shared: [{ type: Output }],
|
|
8010
|
+
deleted: [{ type: Output }],
|
|
7956
8011
|
source: [{ type: Input }],
|
|
7957
8012
|
genericAvatarUrl: [{ type: Input }],
|
|
7958
8013
|
messageContainer: [{ type: ViewChild, args: ['messageContainer',] }],
|
|
7959
8014
|
maxMessages: [{ type: Input }],
|
|
7960
8015
|
maxVisibleMessages: [{ type: Input }],
|
|
7961
|
-
newestLast: [{ type: Input }]
|
|
8016
|
+
newestLast: [{ type: Input }],
|
|
8017
|
+
sortOrderChanged: [{ type: Output }]
|
|
7962
8018
|
};
|
|
7963
8019
|
|
|
7964
8020
|
/**
|
|
7965
8021
|
* Comments component
|
|
7966
8022
|
*/
|
|
7967
8023
|
class BantaCommentsComponent {
|
|
7968
|
-
constructor(
|
|
7969
|
-
this.banta = banta;
|
|
8024
|
+
constructor(backend, elementRef, activatedRoute) {
|
|
7970
8025
|
this.backend = backend;
|
|
7971
8026
|
this.elementRef = elementRef;
|
|
7972
8027
|
this.activatedRoute = activatedRoute;
|
|
8028
|
+
// Loading Screen
|
|
8029
|
+
this._loadingMessage = '';
|
|
8030
|
+
this.loadingMessageVisible = false;
|
|
8031
|
+
this.loading = true;
|
|
8032
|
+
this.showLoadingScreen = false;
|
|
8033
|
+
this._loadingMessageIndex = 0;
|
|
8034
|
+
this.loadingMessages = [
|
|
8035
|
+
`Just a second...`,
|
|
8036
|
+
`We're definitely working on it.`,
|
|
8037
|
+
`There's no need to refresh.`,
|
|
8038
|
+
`It's definitely worth the wait!`,
|
|
8039
|
+
`This has never happened before.`,
|
|
8040
|
+
`We'll keep trying, but it's not looking great.
|
|
8041
|
+
Commenting & chat services may be down.
|
|
8042
|
+
If you continue to experience issues, please contact support.
|
|
8043
|
+
`
|
|
8044
|
+
];
|
|
8045
|
+
// Properties
|
|
8046
|
+
this._signInSelected = new Subject();
|
|
8047
|
+
this._permissionDeniedError = new Subject();
|
|
8048
|
+
this._editAvatarSelected = new Subject();
|
|
7973
8049
|
this._upvoted = new Subject();
|
|
7974
8050
|
this._reported = new Subject();
|
|
7975
8051
|
this._selected = new Subject();
|
|
@@ -7979,12 +8055,8 @@ class BantaCommentsComponent {
|
|
|
7979
8055
|
this._avatarSelected = new Subject();
|
|
7980
8056
|
this._subs = new Subscription();
|
|
7981
8057
|
this._sortOrder = CommentsOrder.NEWEST;
|
|
7982
|
-
this.
|
|
7983
|
-
|
|
7984
|
-
{ hashtag: 'timeout', description: 'Cause a slow timeout error' },
|
|
7985
|
-
{ hashtag: 'slow', description: 'Be slow when this message is posted' },
|
|
7986
|
-
];
|
|
7987
|
-
this.participants = [];
|
|
8058
|
+
this.selectedMessageVisible = false;
|
|
8059
|
+
// Inputs
|
|
7988
8060
|
this.signInLabel = 'Sign In';
|
|
7989
8061
|
this.sendLabel = 'Send';
|
|
7990
8062
|
this.replyLabel = 'Reply';
|
|
@@ -7992,174 +8064,289 @@ class BantaCommentsComponent {
|
|
|
7992
8064
|
this.permissionDeniedLabel = 'Send';
|
|
7993
8065
|
this.postCommentLabel = 'Post a comment';
|
|
7994
8066
|
this.postReplyLabel = 'Post a reply';
|
|
7995
|
-
this.
|
|
7996
|
-
this.
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8067
|
+
this.participants = [];
|
|
8068
|
+
this.hashtags = [
|
|
8069
|
+
{ hashtag: 'error', description: 'Cause an error' },
|
|
8070
|
+
{ hashtag: 'timeout', description: 'Cause a slow timeout error' },
|
|
8071
|
+
{ hashtag: 'slow', description: 'Be slow when this message is posted' },
|
|
8072
|
+
];
|
|
8073
|
+
this.sendMessage = (message) => __awaiter(this, void 0, void 0, function* () {
|
|
8074
|
+
var _a;
|
|
8075
|
+
try {
|
|
8076
|
+
const intercept = yield ((_a = this.shouldInterceptMessageSend) === null || _a === void 0 ? void 0 : _a.call(this, message, this.source));
|
|
8077
|
+
if (!intercept) {
|
|
8078
|
+
yield this.source.send(message);
|
|
8079
|
+
}
|
|
8080
|
+
if (this.source.sortOrder !== CommentsOrder.NEWEST) {
|
|
8081
|
+
this.sortOrder = CommentsOrder.NEWEST;
|
|
8082
|
+
}
|
|
8083
|
+
return true;
|
|
8084
|
+
}
|
|
8085
|
+
catch (e) {
|
|
8086
|
+
this.handleBackendException(e, 'Could not send: ');
|
|
8087
|
+
}
|
|
8088
|
+
});
|
|
8089
|
+
this.sendReply = (message) => __awaiter(this, void 0, void 0, function* () {
|
|
8090
|
+
var _b;
|
|
8091
|
+
try {
|
|
8092
|
+
const intercept = yield ((_b = this.shouldInterceptMessageSend) === null || _b === void 0 ? void 0 : _b.call(this, message, this.source));
|
|
8093
|
+
if (!intercept) {
|
|
8094
|
+
yield this.selectedMessageThread.send(message);
|
|
8095
|
+
}
|
|
8096
|
+
return true;
|
|
8097
|
+
}
|
|
8098
|
+
catch (e) {
|
|
8099
|
+
this.handleBackendException(e, 'Could not send reply: ');
|
|
8100
|
+
}
|
|
8101
|
+
});
|
|
8001
8102
|
}
|
|
8002
|
-
|
|
8003
|
-
|
|
8103
|
+
handleBackendExceptionAsAlert(e, prefix = '') {
|
|
8104
|
+
try {
|
|
8105
|
+
this.handleBackendException(e, prefix);
|
|
8106
|
+
}
|
|
8107
|
+
catch (e) {
|
|
8108
|
+
alert(e.message);
|
|
8109
|
+
}
|
|
8004
8110
|
}
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8111
|
+
handleBackendException(e, prefix = '') {
|
|
8112
|
+
let errorMessage = e.message;
|
|
8113
|
+
if (errorMessage.startsWith('permission-denied|')) {
|
|
8114
|
+
errorMessage = errorMessage.replace(/^permission-denied\|/, '');
|
|
8115
|
+
if (errorMessage.startsWith(`app-handle|`)) {
|
|
8116
|
+
// If this is an error during authorizeAction on the backend, pass control to the user-provided
|
|
8117
|
+
// permission-denied handler.
|
|
8118
|
+
this.sendPermissionDenied(errorMessage.replace(/^app-handle\|/, ''));
|
|
8119
|
+
return;
|
|
8120
|
+
}
|
|
8011
8121
|
}
|
|
8122
|
+
throw new Error(`${prefix}${errorMessage}`);
|
|
8012
8123
|
}
|
|
8124
|
+
// Lifecycle Events / Initialization
|
|
8013
8125
|
ngOnInit() {
|
|
8014
|
-
this._subs.add(this.
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
if (comment.length > 0) {
|
|
8025
|
-
// comment.item(0).scroll({behavior: 'smooth'});
|
|
8026
|
-
comment.item(0).scrollIntoView();
|
|
8126
|
+
this._subs.add(this.backend.userChanged.subscribe(user => this.user = user));
|
|
8127
|
+
this.startLoading();
|
|
8128
|
+
if (typeof window !== 'undefined') {
|
|
8129
|
+
let queryString = window.location.search.substring(1);
|
|
8130
|
+
let query = queryString.split('&')
|
|
8131
|
+
.map(s => s.split('='))
|
|
8132
|
+
.reduce((o, [k, v]) => (o[k] = v, o), {});
|
|
8133
|
+
const commentID = query['comment'];
|
|
8134
|
+
if (commentID) {
|
|
8135
|
+
this.sharedCommentID = commentID;
|
|
8027
8136
|
}
|
|
8028
|
-
}
|
|
8029
|
-
}
|
|
8030
|
-
checkForSharedComment() {
|
|
8031
|
-
const commentID = this.activatedRoute.snapshot.queryParamMap.get('comment');
|
|
8032
|
-
if (commentID)
|
|
8033
|
-
this.scrollToComment(commentID);
|
|
8137
|
+
}
|
|
8034
8138
|
}
|
|
8035
8139
|
ngOnDestroy() {
|
|
8036
8140
|
this._subs.unsubscribe();
|
|
8037
8141
|
}
|
|
8038
|
-
get source() {
|
|
8039
|
-
return this._source;
|
|
8040
|
-
}
|
|
8041
|
-
set source(value) {
|
|
8042
|
-
this._source = value;
|
|
8043
|
-
}
|
|
8044
|
-
get topicID() {
|
|
8045
|
-
return this._topicID;
|
|
8046
|
-
}
|
|
8047
|
-
set topicID(value) {
|
|
8048
|
-
if (this._topicID !== value) {
|
|
8049
|
-
this._topicID = value;
|
|
8050
|
-
setTimeout(() => this.setSourceFromTopicID(value));
|
|
8051
|
-
}
|
|
8052
|
-
}
|
|
8053
8142
|
setSourceFromTopicID(topicID) {
|
|
8054
|
-
var _a, _b;
|
|
8055
8143
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8056
|
-
|
|
8057
|
-
|
|
8144
|
+
if (this._source) {
|
|
8145
|
+
this._source.close();
|
|
8146
|
+
this._source = null;
|
|
8147
|
+
}
|
|
8058
8148
|
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
8149
|
+
console.log(`[banta-comments] Subscribing to topic source '${topicID}'`);
|
|
8059
8150
|
this._source = yield this.backend.getSourceForTopic(topicID, { sortOrder: this.sortOrder });
|
|
8060
|
-
|
|
8151
|
+
if (this.sharedCommentID) {
|
|
8152
|
+
this.navigateToSharedComment(this.sharedCommentID);
|
|
8153
|
+
this.sharedCommentID = null;
|
|
8154
|
+
}
|
|
8061
8155
|
this._source.messageReceived.subscribe(m => this.addParticipant(m));
|
|
8062
8156
|
this._source.messageSent.subscribe(m => this.addParticipant(m));
|
|
8063
8157
|
this._source.messages.forEach(m => this.addParticipant(m));
|
|
8064
8158
|
}));
|
|
8065
8159
|
});
|
|
8066
8160
|
}
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
return;
|
|
8070
|
-
let existing = this.participants.find(x => x.id === message.user.id);
|
|
8071
|
-
if (existing)
|
|
8072
|
-
return;
|
|
8073
|
-
this.participants.push(message.user);
|
|
8074
|
-
}
|
|
8075
|
-
showSignIn() {
|
|
8076
|
-
this._signInSelected.next();
|
|
8161
|
+
get loadingMessage() {
|
|
8162
|
+
return this._loadingMessage;
|
|
8077
8163
|
}
|
|
8078
|
-
|
|
8079
|
-
this.
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
setTimeout(() => this.sendError = null);
|
|
8088
|
-
}
|
|
8089
|
-
get signInSelected() {
|
|
8090
|
-
return this._signInSelected;
|
|
8091
|
-
}
|
|
8092
|
-
get editAvatarSelected() {
|
|
8093
|
-
return this._editAvatarSelected;
|
|
8094
|
-
}
|
|
8095
|
-
get permissionDeniedError() {
|
|
8096
|
-
return this._permissionDeniedError;
|
|
8164
|
+
set loadingMessage(value) {
|
|
8165
|
+
this.loadingMessageVisible = false;
|
|
8166
|
+
setTimeout(() => {
|
|
8167
|
+
this._loadingMessage = value;
|
|
8168
|
+
this._loadingMessage = value;
|
|
8169
|
+
setTimeout(() => {
|
|
8170
|
+
this.loadingMessageVisible = true;
|
|
8171
|
+
});
|
|
8172
|
+
}, 500);
|
|
8097
8173
|
}
|
|
8098
|
-
|
|
8099
|
-
this
|
|
8174
|
+
startLoading() {
|
|
8175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8176
|
+
this.loadingStartedAt = this.messageChangedAt = Date.now();
|
|
8177
|
+
if (this.updateLoading())
|
|
8178
|
+
return;
|
|
8179
|
+
yield new Promise(resolve => setTimeout(() => resolve(), 100));
|
|
8180
|
+
if (this.updateLoading())
|
|
8181
|
+
return;
|
|
8182
|
+
yield new Promise(resolve => setTimeout(() => resolve(), 250));
|
|
8183
|
+
if (this.updateLoading())
|
|
8184
|
+
return;
|
|
8185
|
+
yield new Promise(resolve => setTimeout(() => resolve(), 500));
|
|
8186
|
+
if (this.updateLoading())
|
|
8187
|
+
return;
|
|
8188
|
+
console.log(`[Banta] Loading is taking a long time! Showing loading screen.`);
|
|
8189
|
+
this.showLoadingScreen = true;
|
|
8190
|
+
if (typeof window !== 'undefined')
|
|
8191
|
+
this._loadingTimer = setInterval(() => this.updateLoading(), 1000);
|
|
8192
|
+
});
|
|
8100
8193
|
}
|
|
8101
|
-
|
|
8102
|
-
var _a;
|
|
8103
|
-
if (
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8194
|
+
updateLoading() {
|
|
8195
|
+
var _a, _b;
|
|
8196
|
+
if (((_a = this.source) === null || _a === void 0 ? void 0 : _a.state) && ((_b = this.source) === null || _b === void 0 ? void 0 : _b.state) !== 'connecting') {
|
|
8197
|
+
clearInterval(this._loadingTimer);
|
|
8198
|
+
this.loadingMessage = `Here we go!`;
|
|
8199
|
+
setTimeout(() => {
|
|
8200
|
+
this.loading = false;
|
|
8201
|
+
}, 750);
|
|
8108
8202
|
return true;
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
return this._selected.asObservable();
|
|
8203
|
+
}
|
|
8204
|
+
let messageSwitchTime = 5 * 1000;
|
|
8205
|
+
if (this.messageChangedAt + messageSwitchTime < Date.now()) {
|
|
8206
|
+
if (this.loadingMessages[this._loadingMessageIndex]) {
|
|
8207
|
+
this.loadingMessage = this.loadingMessages[this._loadingMessageIndex++];
|
|
8208
|
+
this.messageChangedAt = Date.now();
|
|
8209
|
+
}
|
|
8210
|
+
}
|
|
8211
|
+
return false;
|
|
8119
8212
|
}
|
|
8120
|
-
get
|
|
8121
|
-
|
|
8213
|
+
get source() { return this._source; }
|
|
8214
|
+
set source(value) {
|
|
8215
|
+
this._source = value;
|
|
8216
|
+
if (value && this.sharedCommentID) {
|
|
8217
|
+
this.navigateToSharedComment(this.sharedCommentID);
|
|
8218
|
+
this.sharedCommentID = null;
|
|
8219
|
+
}
|
|
8122
8220
|
}
|
|
8123
|
-
get
|
|
8124
|
-
|
|
8221
|
+
get topicID() { return this._topicID; }
|
|
8222
|
+
set topicID(value) {
|
|
8223
|
+
if (this._topicID !== value) {
|
|
8224
|
+
this._topicID = value;
|
|
8225
|
+
setTimeout(() => this.setSourceFromTopicID(value));
|
|
8226
|
+
}
|
|
8125
8227
|
}
|
|
8126
|
-
|
|
8127
|
-
|
|
8228
|
+
// Outputs
|
|
8229
|
+
get signInSelected() { return this._signInSelected; }
|
|
8230
|
+
get editAvatarSelected() { return this._editAvatarSelected; }
|
|
8231
|
+
get permissionDeniedError() { return this._permissionDeniedError; }
|
|
8232
|
+
get upvoted() { return this._upvoted.asObservable(); }
|
|
8233
|
+
get reported() { return this._reported.asObservable(); }
|
|
8234
|
+
get selected() { return this._selected.asObservable(); }
|
|
8235
|
+
get userSelected() { return this._userSelected.asObservable(); }
|
|
8236
|
+
get usernameSelected() { return this._usernameSelected.asObservable(); }
|
|
8237
|
+
get avatarSelected() { return this._avatarSelected.asObservable(); }
|
|
8238
|
+
get shared() { return this._shared.asObservable(); }
|
|
8239
|
+
get sortOrder() { return this._sortOrder; }
|
|
8240
|
+
set sortOrder(value) {
|
|
8241
|
+
if (this._sortOrder !== value) {
|
|
8242
|
+
this._sortOrder = value;
|
|
8243
|
+
setTimeout(() => {
|
|
8244
|
+
this.setSourceFromTopicID(this.topicID);
|
|
8245
|
+
});
|
|
8246
|
+
}
|
|
8128
8247
|
}
|
|
8129
|
-
|
|
8130
|
-
|
|
8248
|
+
// UI Interactions
|
|
8249
|
+
scrollToComment(commentId) {
|
|
8250
|
+
setTimeout(() => {
|
|
8251
|
+
const comment = document.querySelectorAll(`[data-comment-id="${commentId}"]`);
|
|
8252
|
+
if (comment.length > 0) {
|
|
8253
|
+
// comment.item(0).scroll({behavior: 'smooth'});
|
|
8254
|
+
comment.item(0).scrollIntoView();
|
|
8255
|
+
}
|
|
8256
|
+
}, 1000);
|
|
8131
8257
|
}
|
|
8132
|
-
|
|
8258
|
+
navigateToSharedComment(id) {
|
|
8259
|
+
var _a, _b, _c;
|
|
8260
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8261
|
+
let source = this.source;
|
|
8262
|
+
yield source.ready;
|
|
8263
|
+
console.log(`Navigating to shared comment with ID '${id}'...`);
|
|
8264
|
+
let message;
|
|
8265
|
+
try {
|
|
8266
|
+
message = yield this.source.get(id);
|
|
8267
|
+
}
|
|
8268
|
+
catch (e) {
|
|
8269
|
+
console.error(`Failed to find comment from URL: ${e.message}`);
|
|
8270
|
+
return;
|
|
8271
|
+
}
|
|
8272
|
+
(_a = message.transientState) !== null && _a !== void 0 ? _a : (message.transientState = {});
|
|
8273
|
+
// If there is a parent message, we should instead focus that and let the
|
|
8274
|
+
// scrollToComment and highlight do the work.
|
|
8275
|
+
if (message.parentMessageId) {
|
|
8276
|
+
let parentMessage = yield this.source.get(message.parentMessageId);
|
|
8277
|
+
(_b = parentMessage.transientState) !== null && _b !== void 0 ? _b : (parentMessage.transientState = {});
|
|
8278
|
+
let thread = yield this.selectMessage(parentMessage);
|
|
8279
|
+
// Need to re-retrieve the message within the new chat source to affect its
|
|
8280
|
+
// transient state.
|
|
8281
|
+
yield thread.ready;
|
|
8282
|
+
message = yield thread.get(message.id);
|
|
8283
|
+
(_c = message.transientState) !== null && _c !== void 0 ? _c : (message.transientState = {});
|
|
8284
|
+
message.transientState.highlighted = true;
|
|
8285
|
+
console.dir(message);
|
|
8286
|
+
yield new Promise(resolve => setTimeout(() => resolve(), 500));
|
|
8287
|
+
}
|
|
8288
|
+
else {
|
|
8289
|
+
this.selectMessage(message);
|
|
8290
|
+
}
|
|
8291
|
+
this.scrollToComment(id);
|
|
8292
|
+
});
|
|
8133
8293
|
}
|
|
8134
|
-
|
|
8135
|
-
this.
|
|
8294
|
+
sendPermissionDenied(message) {
|
|
8295
|
+
this._permissionDeniedError.next(message);
|
|
8136
8296
|
}
|
|
8137
|
-
|
|
8297
|
+
scrollToMessage(message) {
|
|
8298
|
+
let el = this.elementRef.nativeElement.querySelector(`[data-comment-id="${message.id}"]`);
|
|
8299
|
+
if (!el)
|
|
8300
|
+
return;
|
|
8301
|
+
el.scrollIntoView({ block: 'center', inline: 'start' });
|
|
8138
8302
|
}
|
|
8139
|
-
|
|
8140
|
-
|
|
8303
|
+
addParticipant(message) {
|
|
8304
|
+
if (!message || !message.user || !message.user.id)
|
|
8305
|
+
return;
|
|
8306
|
+
let existing = this.participants.find(x => x.id === message.user.id);
|
|
8307
|
+
if (existing)
|
|
8308
|
+
return;
|
|
8309
|
+
this.participants.push(message.user);
|
|
8141
8310
|
}
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8311
|
+
// Actions
|
|
8312
|
+
likeMessage(source, message) {
|
|
8313
|
+
var _a;
|
|
8314
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8315
|
+
this._upvoted.next(message);
|
|
8316
|
+
message.transientState.liking = true;
|
|
8317
|
+
if (!((_a = message.userState) === null || _a === void 0 ? void 0 : _a.liked))
|
|
8318
|
+
message.likes = (message.likes || 0) + 1;
|
|
8319
|
+
try {
|
|
8320
|
+
yield source.likeMessage(message.id);
|
|
8321
|
+
}
|
|
8322
|
+
catch (e) {
|
|
8323
|
+
this.handleBackendExceptionAsAlert(e, 'Could not like this message: ');
|
|
8324
|
+
}
|
|
8325
|
+
yield new Promise(resolve => setTimeout(() => resolve(), 250));
|
|
8326
|
+
message.transientState.liking = false;
|
|
8149
8327
|
});
|
|
8150
8328
|
}
|
|
8151
|
-
|
|
8329
|
+
unlikeMessage(source, message) {
|
|
8330
|
+
var _a;
|
|
8152
8331
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8153
8332
|
this._upvoted.next(message);
|
|
8154
|
-
message
|
|
8155
|
-
|
|
8156
|
-
|
|
8333
|
+
message.transientState.liking = true;
|
|
8334
|
+
if ((_a = message.userState) === null || _a === void 0 ? void 0 : _a.liked)
|
|
8335
|
+
message.likes = (message.likes || 0) - 1;
|
|
8336
|
+
try {
|
|
8337
|
+
yield source.unlikeMessage(message.id);
|
|
8338
|
+
}
|
|
8339
|
+
catch (e) {
|
|
8340
|
+
this.handleBackendExceptionAsAlert(e, 'Failed to unlike this message: ');
|
|
8341
|
+
}
|
|
8157
8342
|
yield new Promise(resolve => setTimeout(() => resolve(), 250));
|
|
8158
|
-
message
|
|
8343
|
+
message.transientState.liking = false;
|
|
8159
8344
|
});
|
|
8160
8345
|
}
|
|
8161
8346
|
reportMessage(message) {
|
|
8162
|
-
this
|
|
8347
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8348
|
+
this._reported.next(message);
|
|
8349
|
+
});
|
|
8163
8350
|
}
|
|
8164
8351
|
unselectMessage() {
|
|
8165
8352
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -8179,70 +8366,101 @@ class BantaCommentsComponent {
|
|
|
8179
8366
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8180
8367
|
this._selected.next(message);
|
|
8181
8368
|
this.selectedMessage = message;
|
|
8369
|
+
let selectedMessageThread = yield this.backend.getSourceForThread(this.topicID, message.id);
|
|
8182
8370
|
setTimeout(() => this.selectedMessageVisible = true);
|
|
8183
8371
|
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
8184
|
-
this.selectedMessageThread =
|
|
8372
|
+
this.selectedMessageThread = selectedMessageThread;
|
|
8185
8373
|
}), 250);
|
|
8374
|
+
return selectedMessageThread;
|
|
8375
|
+
});
|
|
8376
|
+
}
|
|
8377
|
+
showSignIn() {
|
|
8378
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8379
|
+
this._signInSelected.next();
|
|
8380
|
+
});
|
|
8381
|
+
}
|
|
8382
|
+
showEditAvatar() {
|
|
8383
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8384
|
+
this._editAvatarSelected.next();
|
|
8186
8385
|
});
|
|
8187
8386
|
}
|
|
8188
8387
|
selectMessageUser(message) {
|
|
8189
|
-
this
|
|
8388
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8389
|
+
this._userSelected.next(message);
|
|
8390
|
+
});
|
|
8190
8391
|
}
|
|
8191
8392
|
selectUsername(user) {
|
|
8192
|
-
this
|
|
8393
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8394
|
+
this._usernameSelected.next(user);
|
|
8395
|
+
});
|
|
8193
8396
|
}
|
|
8194
8397
|
selectAvatar(user) {
|
|
8195
|
-
this
|
|
8398
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8399
|
+
this._avatarSelected.next(user);
|
|
8400
|
+
});
|
|
8196
8401
|
}
|
|
8197
8402
|
shareMessage(message) {
|
|
8198
|
-
this
|
|
8403
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8404
|
+
this._shared.next(message);
|
|
8405
|
+
});
|
|
8199
8406
|
}
|
|
8200
|
-
|
|
8407
|
+
deleteMessage(message) {
|
|
8201
8408
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
sentAt: Date.now(),
|
|
8211
|
-
updatedAt: Date.now()
|
|
8212
|
-
});
|
|
8213
|
-
this.replyMessage = '';
|
|
8409
|
+
if (!confirm("Are you sure you want to delete this comment? You cannot undo this action."))
|
|
8410
|
+
return;
|
|
8411
|
+
try {
|
|
8412
|
+
yield this.source.deleteMessage(message.id);
|
|
8413
|
+
}
|
|
8414
|
+
catch (e) {
|
|
8415
|
+
this.handleBackendExceptionAsAlert(e, `Could not delete message: `);
|
|
8416
|
+
}
|
|
8214
8417
|
});
|
|
8215
8418
|
}
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8419
|
+
editMessage(source, message, newText) {
|
|
8420
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8421
|
+
try {
|
|
8422
|
+
yield source.editMessage(message.id, newText);
|
|
8423
|
+
}
|
|
8424
|
+
catch (e) {
|
|
8425
|
+
this.handleBackendExceptionAsAlert(e, 'Could not edit this message: ');
|
|
8426
|
+
return;
|
|
8427
|
+
}
|
|
8428
|
+
message.message = newText;
|
|
8429
|
+
message.transientState.editing = false;
|
|
8430
|
+
});
|
|
8431
|
+
}
|
|
8432
|
+
startEditing(message) {
|
|
8433
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8434
|
+
this.selectedMessage.transientState.editing = false;
|
|
8435
|
+
message.transientState.editing = true;
|
|
8436
|
+
});
|
|
8437
|
+
}
|
|
8438
|
+
saveEdit(message, text) {
|
|
8439
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8440
|
+
try {
|
|
8441
|
+
yield this.source.editMessage(message.id, text);
|
|
8442
|
+
message.transientState.editing = false;
|
|
8443
|
+
}
|
|
8444
|
+
catch (e) {
|
|
8445
|
+
this.handleBackendExceptionAsAlert(e, `Could not edit message: `);
|
|
8446
|
+
}
|
|
8447
|
+
});
|
|
8221
8448
|
}
|
|
8222
8449
|
}
|
|
8223
8450
|
BantaCommentsComponent.decorators = [
|
|
8224
8451
|
{ type: Component, args: [{
|
|
8225
8452
|
selector: 'banta-comments',
|
|
8226
|
-
template: "\r\n<div class=\"focused\" [class.visible]=\"selectedMessageVisible\" *ngIf=\"selectedMessage\">\r\n\r\n
|
|
8227
|
-
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}}"]
|
|
8453
|
+
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>",
|
|
8454
|
+
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}"]
|
|
8228
8455
|
},] }
|
|
8229
8456
|
];
|
|
8230
8457
|
BantaCommentsComponent.ctorParameters = () => [
|
|
8231
|
-
{ type:
|
|
8232
|
-
{ type: ChatBackendService },
|
|
8458
|
+
{ type: ChatBackendBase },
|
|
8233
8459
|
{ type: ElementRef },
|
|
8234
8460
|
{ type: ActivatedRoute }
|
|
8235
8461
|
];
|
|
8236
8462
|
BantaCommentsComponent.propDecorators = {
|
|
8237
|
-
|
|
8238
|
-
participants: [{ type: Input }],
|
|
8239
|
-
source: [{ type: Input }],
|
|
8240
|
-
fixedHeight: [{ type: Input }],
|
|
8241
|
-
maxMessages: [{ type: Input }],
|
|
8242
|
-
maxVisibleMessages: [{ type: Input }],
|
|
8243
|
-
genericAvatarUrl: [{ type: Input }],
|
|
8244
|
-
shouldInterceptMessageSend: [{ type: Input }],
|
|
8245
|
-
topicID: [{ type: Input }],
|
|
8463
|
+
loadingMessages: [{ type: Input }],
|
|
8246
8464
|
signInLabel: [{ type: Input }],
|
|
8247
8465
|
sendLabel: [{ type: Input }],
|
|
8248
8466
|
replyLabel: [{ type: Input }],
|
|
@@ -8250,6 +8468,15 @@ BantaCommentsComponent.propDecorators = {
|
|
|
8250
8468
|
permissionDeniedLabel: [{ type: Input }],
|
|
8251
8469
|
postCommentLabel: [{ type: Input }],
|
|
8252
8470
|
postReplyLabel: [{ type: Input }],
|
|
8471
|
+
fixedHeight: [{ type: Input }],
|
|
8472
|
+
maxMessages: [{ type: Input }],
|
|
8473
|
+
maxVisibleMessages: [{ type: Input }],
|
|
8474
|
+
genericAvatarUrl: [{ type: Input }],
|
|
8475
|
+
shouldInterceptMessageSend: [{ type: Input }],
|
|
8476
|
+
participants: [{ type: Input }],
|
|
8477
|
+
source: [{ type: Input }],
|
|
8478
|
+
hashtags: [{ type: Input }],
|
|
8479
|
+
topicID: [{ type: Input }],
|
|
8253
8480
|
signInSelected: [{ type: Output }],
|
|
8254
8481
|
editAvatarSelected: [{ type: Output }],
|
|
8255
8482
|
permissionDeniedError: [{ type: Output }],
|
|
@@ -8321,7 +8548,7 @@ LiveCommentComponent.decorators = [
|
|
|
8321
8548
|
},] }
|
|
8322
8549
|
];
|
|
8323
8550
|
LiveCommentComponent.ctorParameters = () => [
|
|
8324
|
-
{ type:
|
|
8551
|
+
{ type: ChatBackendBase }
|
|
8325
8552
|
];
|
|
8326
8553
|
LiveCommentComponent.propDecorators = {
|
|
8327
8554
|
upvoted: [{ type: Output }],
|
|
@@ -8344,6 +8571,7 @@ class CommentFieldComponent {
|
|
|
8344
8571
|
this.permissionDeniedLabel = 'Unavailable';
|
|
8345
8572
|
this.signInLabel = 'Sign In';
|
|
8346
8573
|
this.placeholder = '';
|
|
8574
|
+
this.textChanged = new Subject();
|
|
8347
8575
|
this.participants = [];
|
|
8348
8576
|
this._permissionDeniedError = new Subject();
|
|
8349
8577
|
this.autocompleteVisible = false;
|
|
@@ -8357,8 +8585,8 @@ class CommentFieldComponent {
|
|
|
8357
8585
|
let root = document.body.querySelector('[ng-version]') || document.body;
|
|
8358
8586
|
root.appendChild(this.autocompleteEl.nativeElement);
|
|
8359
8587
|
}
|
|
8360
|
-
|
|
8361
|
-
this._permissionDeniedError.next();
|
|
8588
|
+
sendPermissionDenied(message) {
|
|
8589
|
+
this._permissionDeniedError.next(message);
|
|
8362
8590
|
}
|
|
8363
8591
|
showAutoComplete(options) {
|
|
8364
8592
|
this.autoCompleteSelected = 0;
|
|
@@ -8381,12 +8609,14 @@ class CommentFieldComponent {
|
|
|
8381
8609
|
}
|
|
8382
8610
|
indicateError(message) {
|
|
8383
8611
|
this.sendError = new Error(message);
|
|
8384
|
-
|
|
8612
|
+
this.expandError = false;
|
|
8613
|
+
clearTimeout(this.errorTimeout);
|
|
8614
|
+
this.errorTimeout = setTimeout(() => {
|
|
8385
8615
|
this.expandError = true;
|
|
8386
|
-
setTimeout(() => {
|
|
8616
|
+
this.errorTimeout = setTimeout(() => {
|
|
8387
8617
|
this.expandError = false;
|
|
8388
8618
|
}, 5 * 1000);
|
|
8389
|
-
});
|
|
8619
|
+
}, 100);
|
|
8390
8620
|
}
|
|
8391
8621
|
autocomplete(replacement) {
|
|
8392
8622
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -8512,7 +8742,6 @@ class CommentFieldComponent {
|
|
|
8512
8742
|
this.editAvatarSelected.next();
|
|
8513
8743
|
}
|
|
8514
8744
|
sendMessage() {
|
|
8515
|
-
var _a;
|
|
8516
8745
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8517
8746
|
if (!this.source)
|
|
8518
8747
|
return;
|
|
@@ -8526,20 +8755,16 @@ class CommentFieldComponent {
|
|
|
8526
8755
|
user: this.user,
|
|
8527
8756
|
sentAt: Date.now(),
|
|
8528
8757
|
url: location.href,
|
|
8529
|
-
|
|
8758
|
+
likes: 0,
|
|
8530
8759
|
message: text
|
|
8531
8760
|
};
|
|
8532
8761
|
try {
|
|
8533
|
-
|
|
8534
|
-
if (!intercept) {
|
|
8535
|
-
yield this.source.send(message);
|
|
8536
|
-
}
|
|
8762
|
+
yield this.submit(message);
|
|
8537
8763
|
this.text = '';
|
|
8538
8764
|
}
|
|
8539
8765
|
catch (e) {
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
console.error(e);
|
|
8766
|
+
yield new Promise(resolve => setTimeout(() => resolve(), 1000));
|
|
8767
|
+
this.indicateError(e.message);
|
|
8543
8768
|
}
|
|
8544
8769
|
}
|
|
8545
8770
|
finally {
|
|
@@ -8551,7 +8776,7 @@ class CommentFieldComponent {
|
|
|
8551
8776
|
CommentFieldComponent.decorators = [
|
|
8552
8777
|
{ type: Component, args: [{
|
|
8553
8778
|
selector: 'banta-comment-field',
|
|
8554
|
-
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)=\"
|
|
8779
|
+
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",
|
|
8555
8780
|
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}}"]
|
|
8556
8781
|
},] }
|
|
8557
8782
|
];
|
|
@@ -8561,19 +8786,23 @@ CommentFieldComponent.propDecorators = {
|
|
|
8561
8786
|
canComment: [{ type: Input }],
|
|
8562
8787
|
signInSelected: [{ type: Output }],
|
|
8563
8788
|
editAvatarSelected: [{ type: Output }],
|
|
8789
|
+
sendError: [{ type: Input }],
|
|
8790
|
+
expandError: [{ type: Input }],
|
|
8564
8791
|
sendLabel: [{ type: Input }],
|
|
8565
8792
|
sendingLabel: [{ type: Input }],
|
|
8566
8793
|
label: [{ type: Input }],
|
|
8567
8794
|
permissionDeniedLabel: [{ type: Input }],
|
|
8568
8795
|
signInLabel: [{ type: Input }],
|
|
8569
8796
|
placeholder: [{ type: Input }],
|
|
8797
|
+
textChanged: [{ type: Output }],
|
|
8570
8798
|
shouldInterceptMessageSend: [{ type: Input }],
|
|
8571
8799
|
autocompleteEl: [{ type: ViewChild, args: ['autocomplete',] }],
|
|
8572
8800
|
autocompleteContainerEl: [{ type: ViewChild, args: ['autocompleteContainer',] }],
|
|
8573
8801
|
textareaEl: [{ type: ViewChild, args: ['textarea',] }],
|
|
8574
8802
|
hashtags: [{ type: Input }],
|
|
8575
8803
|
participants: [{ type: Input }],
|
|
8576
|
-
permissionDeniedError: [{ type: Output }]
|
|
8804
|
+
permissionDeniedError: [{ type: Output }],
|
|
8805
|
+
submit: [{ type: Input }]
|
|
8577
8806
|
};
|
|
8578
8807
|
|
|
8579
8808
|
class CommentSortComponent {
|
|
@@ -8639,12 +8868,234 @@ CommentsModule.decorators = [
|
|
|
8639
8868
|
},] }
|
|
8640
8869
|
];
|
|
8641
8870
|
|
|
8871
|
+
class ChatSource extends SocketRPC {
|
|
8872
|
+
constructor(backend, identifier, parentIdentifier, sortOrder) {
|
|
8873
|
+
super();
|
|
8874
|
+
this.backend = backend;
|
|
8875
|
+
this.identifier = identifier;
|
|
8876
|
+
this.parentIdentifier = parentIdentifier;
|
|
8877
|
+
this.sortOrder = sortOrder;
|
|
8878
|
+
this.subscription = new Subscription();
|
|
8879
|
+
this.state = 'connecting';
|
|
8880
|
+
this.messageMap = new Map();
|
|
8881
|
+
this._messageReceived = new Subject();
|
|
8882
|
+
this._messageSent = new Subject();
|
|
8883
|
+
this.messages = [];
|
|
8884
|
+
this.ready = new Promise(resolve => this.markReady = resolve);
|
|
8885
|
+
}
|
|
8886
|
+
bind(socket) {
|
|
8887
|
+
const _super = Object.create(null, {
|
|
8888
|
+
bind: { get: () => super.bind }
|
|
8889
|
+
});
|
|
8890
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8891
|
+
_super.bind.call(this, socket);
|
|
8892
|
+
this.state = 'connected';
|
|
8893
|
+
this.markReady();
|
|
8894
|
+
yield this.subscribeToTopic();
|
|
8895
|
+
this.subscription.add(this.backend.userChanged.subscribe(() => this.authenticate()));
|
|
8896
|
+
socket.addEventListener('open', () => __awaiter(this, void 0, void 0, function* () {
|
|
8897
|
+
this.state = 'connected';
|
|
8898
|
+
}));
|
|
8899
|
+
socket.addEventListener('lost', () => __awaiter(this, void 0, void 0, function* () {
|
|
8900
|
+
this.state = 'lost';
|
|
8901
|
+
}));
|
|
8902
|
+
socket.addEventListener('restore', () => __awaiter(this, void 0, void 0, function* () {
|
|
8903
|
+
this.state = 'restored';
|
|
8904
|
+
yield this.authenticate();
|
|
8905
|
+
yield this.subscribeToTopic();
|
|
8906
|
+
}));
|
|
8907
|
+
return this;
|
|
8908
|
+
});
|
|
8909
|
+
}
|
|
8910
|
+
getExistingMessages() {
|
|
8911
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8912
|
+
let messages = yield this.peer.getExistingMessages();
|
|
8913
|
+
messages = messages.map(message => {
|
|
8914
|
+
let existingMessage = this.messageMap.get(message.id);
|
|
8915
|
+
if (existingMessage)
|
|
8916
|
+
message = Object.assign(existingMessage, message);
|
|
8917
|
+
else
|
|
8918
|
+
this.messageMap.set(message.id, message);
|
|
8919
|
+
return message;
|
|
8920
|
+
});
|
|
8921
|
+
return messages;
|
|
8922
|
+
});
|
|
8923
|
+
}
|
|
8924
|
+
editMessage(messageId, text) {
|
|
8925
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8926
|
+
this.peer.editMessage(messageId, text);
|
|
8927
|
+
});
|
|
8928
|
+
}
|
|
8929
|
+
subscribeToTopic() {
|
|
8930
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8931
|
+
yield this.peer.subscribe(this.identifier, this.parentIdentifier, this.sortOrder);
|
|
8932
|
+
});
|
|
8933
|
+
}
|
|
8934
|
+
authenticate() {
|
|
8935
|
+
var _a;
|
|
8936
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8937
|
+
yield this.peer.authenticate((_a = this.backend.user) === null || _a === void 0 ? void 0 : _a.token);
|
|
8938
|
+
});
|
|
8939
|
+
}
|
|
8940
|
+
close() {
|
|
8941
|
+
super.close();
|
|
8942
|
+
this.subscription.unsubscribe();
|
|
8943
|
+
}
|
|
8944
|
+
onPermissions(permissions) {
|
|
8945
|
+
window.bantaPermissionsDebug = permissions;
|
|
8946
|
+
this.permissions = permissions;
|
|
8947
|
+
}
|
|
8948
|
+
onChatMessage(message) {
|
|
8949
|
+
if (this.messageMap.has(message.id)) {
|
|
8950
|
+
Object.assign(this.messageMap.get(message.id), message);
|
|
8951
|
+
}
|
|
8952
|
+
else if (!message.hidden) {
|
|
8953
|
+
// Only process non-hidden messages through here.
|
|
8954
|
+
// Hidden messages may be sent to us when they become hidden (ie moderation is occurring).
|
|
8955
|
+
// But if we never had the message to begin with, we should discard it.
|
|
8956
|
+
this.messageMap.set(message.id, message);
|
|
8957
|
+
this._messageReceived.next(message);
|
|
8958
|
+
}
|
|
8959
|
+
}
|
|
8960
|
+
get messageReceived() { return this._messageReceived.asObservable(); }
|
|
8961
|
+
get messageSent() { return this._messageSent.asObservable(); }
|
|
8962
|
+
send(message) {
|
|
8963
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8964
|
+
return yield this.peer.sendMessage(message);
|
|
8965
|
+
});
|
|
8966
|
+
}
|
|
8967
|
+
loadAfter(message, count) {
|
|
8968
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8969
|
+
if (!message)
|
|
8970
|
+
return;
|
|
8971
|
+
if (!message.pagingCursor)
|
|
8972
|
+
return [];
|
|
8973
|
+
return this.peer.loadAfter(Number(message.pagingCursor), count);
|
|
8974
|
+
});
|
|
8975
|
+
}
|
|
8976
|
+
get(id) {
|
|
8977
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8978
|
+
if (this.messageMap.has(id))
|
|
8979
|
+
return this.messageMap.get(id);
|
|
8980
|
+
let message = yield this.peer.getMessage(id);
|
|
8981
|
+
this.messageMap.set(id, message);
|
|
8982
|
+
return message;
|
|
8983
|
+
});
|
|
8984
|
+
}
|
|
8985
|
+
getCount() {
|
|
8986
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8987
|
+
return yield this.peer.getCount();
|
|
8988
|
+
});
|
|
8989
|
+
}
|
|
8990
|
+
likeMessage(messageId) {
|
|
8991
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8992
|
+
return yield this.peer.likeMessage(messageId);
|
|
8993
|
+
});
|
|
8994
|
+
}
|
|
8995
|
+
unlikeMessage(messageId) {
|
|
8996
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8997
|
+
return yield this.peer.unlikeMessage(messageId);
|
|
8998
|
+
});
|
|
8999
|
+
}
|
|
9000
|
+
deleteMessage(messageId) {
|
|
9001
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9002
|
+
return yield this.peer.deleteMessage(messageId);
|
|
9003
|
+
});
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
9006
|
+
__decorate([
|
|
9007
|
+
RpcEvent(),
|
|
9008
|
+
__metadata("design:type", Function),
|
|
9009
|
+
__metadata("design:paramtypes", [Object]),
|
|
9010
|
+
__metadata("design:returntype", void 0)
|
|
9011
|
+
], ChatSource.prototype, "onPermissions", null);
|
|
9012
|
+
__decorate([
|
|
9013
|
+
RpcEvent(),
|
|
9014
|
+
__metadata("design:type", Function),
|
|
9015
|
+
__metadata("design:paramtypes", [Object]),
|
|
9016
|
+
__metadata("design:returntype", void 0)
|
|
9017
|
+
], ChatSource.prototype, "onChatMessage", null);
|
|
9018
|
+
|
|
9019
|
+
const BANTA_SDK_OPTIONS = 'BANTA_SDK_OPTIONS';
|
|
9020
|
+
|
|
9021
|
+
class ChatBackend extends ChatBackendBase {
|
|
9022
|
+
constructor(options) {
|
|
9023
|
+
super();
|
|
9024
|
+
this.options = options;
|
|
9025
|
+
}
|
|
9026
|
+
get serviceUrl() {
|
|
9027
|
+
var _a, _b;
|
|
9028
|
+
return `${(_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.serviceUrl) !== null && _b !== void 0 ? _b : 'http://localhost:3422'}`;
|
|
9029
|
+
}
|
|
9030
|
+
connectToService() {
|
|
9031
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9032
|
+
let socket = new DurableSocket(`${this.serviceUrl.replace(/^http/, 'ws')}/socket`);
|
|
9033
|
+
yield new Promise((resolve, reject) => {
|
|
9034
|
+
socket.onopen = () => {
|
|
9035
|
+
resolve();
|
|
9036
|
+
};
|
|
9037
|
+
socket.onclose = e => {
|
|
9038
|
+
if (e.code === 503) {
|
|
9039
|
+
console.error(`Failed to connect to chat service!`);
|
|
9040
|
+
reject(e);
|
|
9041
|
+
}
|
|
9042
|
+
};
|
|
9043
|
+
});
|
|
9044
|
+
socket.onerror = undefined;
|
|
9045
|
+
return socket;
|
|
9046
|
+
});
|
|
9047
|
+
}
|
|
9048
|
+
getSourceForTopic(topicId, options) {
|
|
9049
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9050
|
+
return yield new ChatSource(this, topicId, undefined, (options === null || options === void 0 ? void 0 : options.sortOrder) || CommentsOrder.NEWEST)
|
|
9051
|
+
.bind(yield this.connectToService());
|
|
9052
|
+
});
|
|
9053
|
+
}
|
|
9054
|
+
getSourceForThread(topicId, messageId, options) {
|
|
9055
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9056
|
+
return yield new ChatSource(this, topicId, messageId, (options === null || options === void 0 ? void 0 : options.sortOrder) || CommentsOrder.NEWEST)
|
|
9057
|
+
.bind(yield this.connectToService());
|
|
9058
|
+
});
|
|
9059
|
+
}
|
|
9060
|
+
getSourceCountForTopic(topicId) {
|
|
9061
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
9062
|
+
let response = yield fetch(`${this.serviceUrl}/topics/${topicId}`);
|
|
9063
|
+
if (response.status >= 400)
|
|
9064
|
+
return 0;
|
|
9065
|
+
let topic = yield response.json();
|
|
9066
|
+
return topic.messageCount || 0;
|
|
9067
|
+
});
|
|
9068
|
+
}
|
|
9069
|
+
refreshMessage(message) {
|
|
9070
|
+
throw new Error("Method not implemented.");
|
|
9071
|
+
}
|
|
9072
|
+
getMessage(topicId, messageId) {
|
|
9073
|
+
throw new Error("Method not implemented.");
|
|
9074
|
+
}
|
|
9075
|
+
getSubMessage(topicId, parentMessageId, messageId) {
|
|
9076
|
+
throw new Error("Method not implemented.");
|
|
9077
|
+
}
|
|
9078
|
+
watchMessage(message, handler) {
|
|
9079
|
+
throw new Error("Method not implemented.");
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
ChatBackend.decorators = [
|
|
9083
|
+
{ type: Injectable }
|
|
9084
|
+
];
|
|
9085
|
+
ChatBackend.ctorParameters = () => [
|
|
9086
|
+
{ type: undefined, decorators: [{ type: Inject, args: [BANTA_SDK_OPTIONS,] }] }
|
|
9087
|
+
];
|
|
9088
|
+
|
|
8642
9089
|
class BantaSdkModule {
|
|
8643
|
-
static
|
|
9090
|
+
static configure(options) {
|
|
8644
9091
|
return {
|
|
8645
9092
|
ngModule: BantaSdkModule,
|
|
8646
9093
|
providers: [
|
|
8647
|
-
|
|
9094
|
+
{
|
|
9095
|
+
provide: BANTA_SDK_OPTIONS,
|
|
9096
|
+
useValue: options || {}
|
|
9097
|
+
},
|
|
9098
|
+
{ provide: ChatBackendBase, useClass: ChatBackend }
|
|
8648
9099
|
]
|
|
8649
9100
|
};
|
|
8650
9101
|
}
|
|
@@ -8690,5 +9141,5 @@ BantaSdkModule.decorators = [
|
|
|
8690
9141
|
* Generated bundle index. Do not edit.
|
|
8691
9142
|
*/
|
|
8692
9143
|
|
|
8693
|
-
export { BantaChatComponent, BantaCommentsComponent, BantaCommonModule, BantaComponent, BantaLogoComponent, BantaSdkModule,
|
|
9144
|
+
export { BANTA_SDK_OPTIONS, BantaChatComponent, BantaCommentsComponent, BantaCommonModule, BantaComponent, BantaLogoComponent, BantaSdkModule, ChatBackend, ChatBackendBase, ChatMessageComponent, ChatModule, ChatSource, ChatViewComponent, CommentComponent, CommentFieldComponent, CommentSortComponent, CommentViewComponent, CommentsModule, EMOJIS, EmojiModule, EmojiSelectorButtonComponent, EmojiSelectorPanelComponent, LiveChatMessageComponent, LiveCommentComponent, LiveMessageComponent, TimestampComponent, lazyConnection };
|
|
8694
9145
|
//# sourceMappingURL=banta-sdk.js.map
|