@banta/sdk 3.0.1 → 3.1.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/bundles/banta-sdk.umd.js +501 -46
- 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 +2 -4
- package/esm2015/lib/chat/banta-chat/banta-chat.component.js +5 -1
- package/esm2015/lib/comments/banta-comments/banta-comments.component.js +106 -22
- package/esm2015/lib/comments/comment/comment.component.js +24 -5
- package/esm2015/lib/comments/comment-field/comment-field.component.js +235 -0
- package/esm2015/lib/comments/comment-view/comment-view.component.js +4 -4
- package/esm2015/lib/comments/comments.module.js +4 -2
- package/esm2015/lib/comments/index.js +2 -1
- package/esm2015/lib/emoji/emoji-selector-button.component.js +22 -8
- package/esm2015/lib/emoji/emoji-selector-panel/emoji-selector-panel.component.js +31 -3
- package/esm2015/lib/emoji/emoji.module.js +8 -2
- package/fesm2015/banta-sdk.js +426 -44
- package/fesm2015/banta-sdk.js.map +1 -1
- package/lib/comments/banta-comments/banta-comments.component.d.ts +25 -4
- package/lib/comments/comment/comment.component.d.ts +4 -0
- package/lib/comments/comment-field/comment-field.component.d.ts +51 -0
- package/lib/comments/comment-view/comment-view.component.d.ts +1 -1
- package/lib/comments/index.d.ts +1 -0
- package/lib/emoji/emoji-selector-button.component.d.ts +4 -0
- package/lib/emoji/emoji-selector-panel/emoji-selector-panel.component.d.ts +8 -0
- package/package.json +1 -1
package/fesm2015/banta-sdk.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Observable, Subject, BehaviorSubject } from 'rxjs';
|
|
2
2
|
import { publish } from 'rxjs/operators';
|
|
3
|
-
import { Injectable, Component, Input, NgModule, Output,
|
|
3
|
+
import { Injectable, Component, Input, NgModule, Output, ViewChild, ElementRef, HostBinding } 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';
|
|
7
7
|
import { MatButtonModule } from '@angular/material/button';
|
|
8
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
9
|
+
import { MatInputModule } from '@angular/material/input';
|
|
10
|
+
import { FormsModule } from '@angular/forms';
|
|
8
11
|
import { __awaiter } from 'tslib';
|
|
9
12
|
import { SubSink } from 'subsink';
|
|
10
13
|
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
|
11
|
-
import { FormsModule } from '@angular/forms';
|
|
12
14
|
import { MatMenuModule } from '@angular/material/menu';
|
|
13
15
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
14
|
-
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
15
|
-
import { MatInputModule } from '@angular/material/input';
|
|
16
16
|
import { TextFieldModule } from '@angular/cdk/text-field';
|
|
17
17
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
18
18
|
|
|
@@ -6686,14 +6686,42 @@ class EmojiSelectorPanelComponent {
|
|
|
6686
6686
|
constructor(sanitizer) {
|
|
6687
6687
|
this.sanitizer = sanitizer;
|
|
6688
6688
|
this.activeCategory = 'people';
|
|
6689
|
+
this.searchResults = [];
|
|
6690
|
+
this.searchVisible = false;
|
|
6689
6691
|
this.selected = new Subject();
|
|
6690
6692
|
}
|
|
6693
|
+
get searchQuery() {
|
|
6694
|
+
return this._searchQuery;
|
|
6695
|
+
}
|
|
6696
|
+
set searchQuery(value) {
|
|
6697
|
+
this._searchQuery = value;
|
|
6698
|
+
setTimeout(() => {
|
|
6699
|
+
this.searchResults = Object.keys(EMOJIS).filter(k => k.includes(value)).map(k => EMOJIS[k]);
|
|
6700
|
+
this.searchResults.splice(50, this.searchResults.length);
|
|
6701
|
+
console.log(`looking for '${value}' => ${this.searchResults.length} results`);
|
|
6702
|
+
});
|
|
6703
|
+
}
|
|
6704
|
+
humanize(str) {
|
|
6705
|
+
return str.replace(/(^| )[a-z]/g, k => k.toUpperCase()).replace(/_/g, ' ');
|
|
6706
|
+
}
|
|
6691
6707
|
select(char) {
|
|
6692
6708
|
this.selected.next(char);
|
|
6693
6709
|
}
|
|
6694
6710
|
pairs(object) {
|
|
6695
6711
|
return Object.keys(object).map(key => [key, object[key]]);
|
|
6696
6712
|
}
|
|
6713
|
+
hideSearch() {
|
|
6714
|
+
// because of the "outside click detection"
|
|
6715
|
+
setTimeout(() => {
|
|
6716
|
+
this.searchVisible = false;
|
|
6717
|
+
});
|
|
6718
|
+
}
|
|
6719
|
+
showSearch() {
|
|
6720
|
+
// because of the "outside click detection"
|
|
6721
|
+
setTimeout(() => {
|
|
6722
|
+
this.searchVisible = true;
|
|
6723
|
+
});
|
|
6724
|
+
}
|
|
6697
6725
|
ngOnInit() {
|
|
6698
6726
|
let cats = {};
|
|
6699
6727
|
let categoryIcons = {
|
|
@@ -6725,8 +6753,8 @@ class EmojiSelectorPanelComponent {
|
|
|
6725
6753
|
EmojiSelectorPanelComponent.decorators = [
|
|
6726
6754
|
{ type: Component, args: [{
|
|
6727
6755
|
selector: 'emoji-selector-panel',
|
|
6728
|
-
template: "<div class=\"categories\">\r\n\t<a title=\"
|
|
6729
|
-
styles: [":host{background:#111;border:1px solid #333;border-radius:5px;display:flex;flex-direction:column;padding:.5em;width:calc(
|
|
6756
|
+
template: "<ng-container *ngIf=\"searchVisible\">\r\n\t<div class=\"search-box\" *ngIf=\"searchVisible\">\r\n\t\t<a mat-icon-button href=\"javascript:;\" (click)=\"hideSearch()\">\r\n\t\t\t<mat-icon>arrow_back</mat-icon>\r\n\t\t</a>\r\n\t\t<mat-form-field appearance=\"outline\" floatLabel=\"always\">\r\n\t\t\t<mat-label>Search for emoji</mat-label>\r\n\t\t\t<input name=\"search\" type=\"text\" matInput placeholder=\"Start typing\" [(ngModel)]=\"searchQuery\" />\r\n\t\t</mat-form-field>\r\n\t</div>\r\n\t<div class=\"emoji-list\">\r\n\t\t<a href=\"javascript:;\" (click)=\"select(emoji.char)\" \r\n\t\t\t*ngFor=\"let emoji of searchResults\" [innerHtml]=\"emoji.html || ''\">\r\n\t\t</a>\r\n\t</div>\r\n</ng-container>\r\n<ng-container *ngIf=\"!searchVisible\">\r\n\t<div class=\"categories\">\r\n\t\t<ng-container *ngIf=\"!searchVisible\">\r\n\t\t\t<a [title]=\"humanize(category.name)\" [class.active]=\"activeCategory === category.name\" mat-icon-button *ngFor=\"let category of categories\" (click)=\"activeCategory = category.name\">\r\n\t\t\t\t<mat-icon>{{category.icon}}</mat-icon>\r\n\t\t\t</a>\r\n\r\n\t\t\t<a title=\"Search\" [class.active] mat-icon-button (click)=\"showSearch()\">\r\n\t\t\t\t<mat-icon>search</mat-icon>\r\n\t\t\t</a>\r\n\t\t</ng-container>\r\n\t</div>\r\n\t<div *ngFor=\"let category of categories\">\r\n\t\t<div class=\"emoji-list\" *ngIf=\"activeCategory && activeCategory == category.name\">\r\n\t\t\t<a href=\"javascript:;\" (click)=\"select(emoji.char)\" \r\n\t\t\t\t*ngFor=\"let emoji of category.emojis\" [innerHtml]=\"emoji.html || ''\">\r\n\t\t\t</a>\r\n\t\t</div>\r\n\t</div>\r\n</ng-container>",
|
|
6757
|
+
styles: [":host{background:#111;border:1px solid #333;border-radius:5px;color:#fff;display:flex;flex-direction:column;padding:.5em;width:calc(288px + 9em)}.categories a{opacity:.25;transition:opacity .4s ease-in-out}.categories a:hover{opacity:.5}.categories a.active{opacity:1}.emoji-list{flex-grow:1;height:20em;overflow-y:auto}.emoji-list a{background-color:#111;display:inline-block;margin:4px;padding:2px}.emoji-list a ::ng-deep .emoji{height:32px;width:32px}.emoji-list a:hover{background-color:#333}.search-box{align-items:baseline;display:flex}.search-box mat-form-field{flex-grow:1}"]
|
|
6730
6758
|
},] }
|
|
6731
6759
|
];
|
|
6732
6760
|
EmojiSelectorPanelComponent.ctorParameters = () => [
|
|
@@ -6746,6 +6774,11 @@ class EmojiSelectorButtonComponent {
|
|
|
6746
6774
|
}
|
|
6747
6775
|
ngOnDestroy() {
|
|
6748
6776
|
this.removeListener();
|
|
6777
|
+
this.panelElement.nativeElement.remove();
|
|
6778
|
+
}
|
|
6779
|
+
ngAfterViewInit() {
|
|
6780
|
+
let root = document.body.querySelector('[ng-version]') || document.body;
|
|
6781
|
+
root.appendChild(this.panelElement.nativeElement);
|
|
6749
6782
|
}
|
|
6750
6783
|
removeListener() {
|
|
6751
6784
|
document.removeEventListener('click', this.clickListener);
|
|
@@ -6756,6 +6789,12 @@ class EmojiSelectorButtonComponent {
|
|
|
6756
6789
|
return;
|
|
6757
6790
|
}
|
|
6758
6791
|
this.showEmojiPanel = true;
|
|
6792
|
+
let pos = this.buttonElement.nativeElement.getBoundingClientRect();
|
|
6793
|
+
let size = this.panelElement.nativeElement.getBoundingClientRect();
|
|
6794
|
+
Object.assign(this.panelElement.nativeElement.style, {
|
|
6795
|
+
top: `${pos.top + pos.height}px`,
|
|
6796
|
+
right: `${Math.max(0, window.innerWidth - pos.left - pos.width)}px`
|
|
6797
|
+
});
|
|
6759
6798
|
setTimeout(() => {
|
|
6760
6799
|
this.clickListener = (ev) => {
|
|
6761
6800
|
let parent = ev.target;
|
|
@@ -6781,10 +6820,11 @@ EmojiSelectorButtonComponent.decorators = [
|
|
|
6781
6820
|
{ type: Component, args: [{
|
|
6782
6821
|
selector: 'emoji-selector-button',
|
|
6783
6822
|
template: `
|
|
6784
|
-
<button mat-icon-button (click)="show()">
|
|
6823
|
+
<button #button type="button" mat-icon-button (click)="show()">
|
|
6785
6824
|
<mat-icon>emoji_emotions</mat-icon>
|
|
6786
6825
|
</button>
|
|
6787
6826
|
<emoji-selector-panel
|
|
6827
|
+
#panel
|
|
6788
6828
|
(selected)="insert($event)"
|
|
6789
6829
|
[class.visible]="showEmojiPanel"
|
|
6790
6830
|
></emoji-selector-panel>
|
|
@@ -6797,8 +6837,8 @@ EmojiSelectorButtonComponent.decorators = [
|
|
|
6797
6837
|
|
|
6798
6838
|
emoji-selector-panel {
|
|
6799
6839
|
position: absolute;
|
|
6800
|
-
bottom: 2.5em;
|
|
6801
|
-
right: 0;
|
|
6840
|
+
/* bottom: 2.5em;
|
|
6841
|
+
right: 0; */
|
|
6802
6842
|
opacity: 0;
|
|
6803
6843
|
pointer-events: none;
|
|
6804
6844
|
z-index: 10;
|
|
@@ -6813,7 +6853,7 @@ EmojiSelectorButtonComponent.decorators = [
|
|
|
6813
6853
|
color: #666
|
|
6814
6854
|
}
|
|
6815
6855
|
|
|
6816
|
-
:host.bottom-left emoji-selector-panel {
|
|
6856
|
+
/* :host.bottom-left emoji-selector-panel {
|
|
6817
6857
|
right: auto;
|
|
6818
6858
|
left: 0;
|
|
6819
6859
|
}
|
|
@@ -6828,12 +6868,14 @@ EmojiSelectorButtonComponent.decorators = [
|
|
|
6828
6868
|
bottom: auto;
|
|
6829
6869
|
left: 0;
|
|
6830
6870
|
right: auto;
|
|
6831
|
-
}
|
|
6871
|
+
} */
|
|
6832
6872
|
`]
|
|
6833
6873
|
},] }
|
|
6834
6874
|
];
|
|
6835
6875
|
EmojiSelectorButtonComponent.propDecorators = {
|
|
6836
|
-
selected: [{ type: Output }]
|
|
6876
|
+
selected: [{ type: Output }],
|
|
6877
|
+
panelElement: [{ type: ViewChild, args: ['panel', { read: ElementRef },] }],
|
|
6878
|
+
buttonElement: [{ type: ViewChild, args: ['button', { read: ElementRef },] }]
|
|
6837
6879
|
};
|
|
6838
6880
|
|
|
6839
6881
|
const COMPONENTS$1 = [
|
|
@@ -6847,8 +6889,11 @@ EmojiModule.decorators = [
|
|
|
6847
6889
|
declarations: COMPONENTS$1,
|
|
6848
6890
|
imports: [
|
|
6849
6891
|
CommonModule,
|
|
6892
|
+
FormsModule,
|
|
6850
6893
|
MatIconModule,
|
|
6851
|
-
MatButtonModule
|
|
6894
|
+
MatButtonModule,
|
|
6895
|
+
MatFormFieldModule,
|
|
6896
|
+
MatInputModule
|
|
6852
6897
|
],
|
|
6853
6898
|
exports: COMPONENTS$1
|
|
6854
6899
|
},] }
|
|
@@ -7217,8 +7262,12 @@ class BantaChatComponent {
|
|
|
7217
7262
|
}
|
|
7218
7263
|
get canChat() {
|
|
7219
7264
|
var _a;
|
|
7265
|
+
if (!this.user)
|
|
7266
|
+
return false;
|
|
7220
7267
|
if (!this.user.permissions)
|
|
7221
7268
|
return true;
|
|
7269
|
+
if (!this.user.permissions.canChat)
|
|
7270
|
+
return true;
|
|
7222
7271
|
return (_a = this.user.permissions) === null || _a === void 0 ? void 0 : _a.canChat(this.source);
|
|
7223
7272
|
}
|
|
7224
7273
|
sendMessage() {
|
|
@@ -7315,8 +7364,6 @@ class BantaComponent {
|
|
|
7315
7364
|
this.pointSubChat = null;
|
|
7316
7365
|
this.newPointSubMessage = {};
|
|
7317
7366
|
this.genericAvatarUrl = 'https://gravatar.com/avatar/915c804e0be607a4ad766ddadea5c48a?s=512&d=https://codepen.io/assets/avatars/user-avatar-512x512-6e240cf350d2f1cc07c2bed234c3a3bb5f1b237023c204c782622e80d6b212ba.png';
|
|
7318
|
-
// this.firehoseSource = new MockFirehoseSource();
|
|
7319
|
-
// this.pointSource = new MockPointSource();
|
|
7320
7367
|
}
|
|
7321
7368
|
ngOnInit() {
|
|
7322
7369
|
this._subs.add(this.banta.userChanged.subscribe(user => this.currentUser = user), this.backend.notificationsChanged.subscribe(notifs => this.notifications = notifs), this.backend.newNotification.subscribe(notif => {
|
|
@@ -7468,7 +7515,7 @@ class BantaComponent {
|
|
|
7468
7515
|
BantaComponent.decorators = [
|
|
7469
7516
|
{ type: Component, args: [{
|
|
7470
7517
|
selector: `banta`,
|
|
7471
|
-
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\r\n <pre>{{profileUser | json}}</pre>\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>",
|
|
7518
|
+
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>",
|
|
7472
7519
|
styles: [":host{display:flex;flex-direction:row;height:40em;padding:.5em;position:relative}.counted-action{align-items:center;display:flex}.count-indicator{border:1px solid #333;border-radius:3px;font-size:9pt;padding:0 3px}header{margin-bottom:1em;position:relative}header div{align-items:center;display:flex;height:30px}header button{color:#666}header label{color:#333;font-size:12pt;font-weight:100;letter-spacing:2px;margin:0 auto 0 0;overflow-x:hidden;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;z-index:1}header:after,header label{display:block;position:relative}header:after{border:1px solid #ccc;content:\"\";height:0;width:100%;z-index:0}.points{display:flex;flex-direction:column;max-width:50em}:host.point-focus .points{max-width:50em;width:66%}: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{flex-shrink:0;font-size:12pt;margin-left:.5em;max-width:30em;position:relative;transition:width .2s ease-in,max-width .2s ease-in;width:33%}.points .points-section{opacity:1;z-index:2}.points .point-focus,.points .points-section{display:flex;flex-direction:column;flex-grow:1;transition:opacity .2s ease-in}.points .point-focus{bottom:0;left:0;opacity:0;padding:.5em;position:absolute;right:0;top:1.75em;width:100%}.firehose{display:flex;flex-direction:column;flex-grow:1;font-size:10pt}form{align-items:center;display:flex;padding:.5em 0}form textarea{font-size:14pt;min-height:6em}form input[type=text],form textarea{background:#000;border:1px solid #333;color:#fff;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{display:flex;flex-direction:column;min-width:0;overflow-x:hidden;transition:width .4s ease-out,min-width .4s ease-out;width:0}.aux.open{min-width:18em;width:30em}.aux .aux-contents{align-items:center;display:flex;flex-direction:column;flex-grow:1;justify-content:center;max-width:100%;min-width:10em;width:30em}.notifications .notification{border-bottom:1px solid #333;padding:1em}.notifications .notification banta-timestamp{color:#999;display:block;font-size:9pt;text-align:right}.message.reply{padding:1em}.tabs{display:none}@media (max-width:1015px){:host{flex-direction:column}.tabs{-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background:rgba(0,0,0,.5);display:flex;left:0;position:absolute;right:0;top:0;width:100%;z-index:10}.points{margin-left:0;max-width:100%;width:100%}header{display:none}.aux,:host.point-focus .points{max-width:100%;width:100%}.aux{min-width:0}.aux,.firehose,.points{background:#000;bottom:0;left:0;position:absolute;right:0;top:2em;z-index:0}.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}"]
|
|
7473
7520
|
},] }
|
|
7474
7521
|
];
|
|
@@ -7583,8 +7630,20 @@ class CommentComponent {
|
|
|
7583
7630
|
this._selected = new Subject();
|
|
7584
7631
|
this._upvoted = new Subject();
|
|
7585
7632
|
this._userSelected = new Subject();
|
|
7633
|
+
this.isNew = false;
|
|
7634
|
+
this.visible = false;
|
|
7586
7635
|
this.showReplyAction = true;
|
|
7587
7636
|
}
|
|
7637
|
+
ngOnInit() {
|
|
7638
|
+
let maxTime = 500;
|
|
7639
|
+
let minTime = 0;
|
|
7640
|
+
let randomTime = minTime + Math.random() * (maxTime - minTime);
|
|
7641
|
+
setTimeout(() => {
|
|
7642
|
+
this.isNew = true;
|
|
7643
|
+
this.visible = true;
|
|
7644
|
+
setTimeout(() => this.isNew = false, 1000);
|
|
7645
|
+
}, randomTime);
|
|
7646
|
+
}
|
|
7588
7647
|
get userSelected() {
|
|
7589
7648
|
return this._userSelected;
|
|
7590
7649
|
}
|
|
@@ -7597,6 +7656,10 @@ class CommentComponent {
|
|
|
7597
7656
|
get selected() {
|
|
7598
7657
|
return this._selected;
|
|
7599
7658
|
}
|
|
7659
|
+
get commentId() {
|
|
7660
|
+
var _a;
|
|
7661
|
+
return (_a = this.message) === null || _a === void 0 ? void 0 : _a.id;
|
|
7662
|
+
}
|
|
7600
7663
|
report() {
|
|
7601
7664
|
this._reported.next();
|
|
7602
7665
|
}
|
|
@@ -7620,17 +7683,20 @@ class CommentComponent {
|
|
|
7620
7683
|
CommentComponent.decorators = [
|
|
7621
7684
|
{ type: Component, args: [{
|
|
7622
7685
|
selector: 'banta-comment',
|
|
7623
|
-
template: "\r\n<mat-menu #pointItemMenu=\"matMenu\">\r\n <button mat-menu-item (click)=\"report()\">Report</button>\r\n <button mat-menu-item>Help</button>\r\n</mat-menu>\r\n\r\n<div class=\"message-content\">\r\n <div class=\"user\">\r\n <
|
|
7624
|
-
styles: [":host{display:flex;flex-direction:column;padding:.5em;position:relative}:host:hover{background:#eee}:host .message-content .content{margin-left:4em;margin-right:3em}:host.abbreviated .message-content .content{max-height:8.5em;overflow-y:hidden;text-overflow:ellipsis}:host .actions{align-items:center;display:flex;margin-left:4em;padding-right:10px}:host .actions button{color:#666}:host .actions banta-timestamp{color:#666;font-size:10pt}.user{align-items:center;display:flex;margin:1em 0 0;position:relative}.user
|
|
7686
|
+
template: "\r\n<mat-menu #pointItemMenu=\"matMenu\">\r\n <button mat-menu-item (click)=\"report()\">Report</button>\r\n <button mat-menu-item>Help</button>\r\n</mat-menu>\r\n\r\n<div class=\"message-content\">\r\n <div class=\"user\">\r\n <a \r\n href=\"javascript:;\"\r\n class=\"avatar\"\r\n (click)=\"selectUser()\"\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)=\"selectUser()\">@{{message.user.username}}</a>\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 class=\"content\">\r\n {{message.message}}\r\n </div>\r\n \r\n <div class=\"actions\">\r\n <banta-timestamp [value]=\"message.sentAt\"></banta-timestamp>\r\n <div class=\"spacer\"></div>\r\n <div class=\"counted-action\" *ngIf=\"showReplyAction\">\r\n <div class=\"count-indicator\">\r\n {{message.submessages?.length || 0}}\r\n </div>\r\n <button mat-icon-button matTooltip=\"Comment\" matTooltipPosition=\"below\" (click)=\"select()\">\r\n <mat-icon [inline]=\"true\">comment</mat-icon>\r\n </button>\r\n </div>\r\n <div class=\"counted-action\">\r\n <div class=\"count-indicator\"> \r\n {{message.upvotes}}\r\n </div>\r\n <button mat-icon-button matTooltip=\"Upvote\" matTooltipPosition=\"below\" (click)=\"upvote()\">\r\n <mat-icon [inline]=\"true\">thumb_up</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n</div>\r\n",
|
|
7687
|
+
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;padding:.5em;position:relative;visibility:hidden}:host.new{-webkit-animation-duration:.4s;-webkit-animation-fill-mode:both;-webkit-animation-name:comment-appear;animation-duration:.4s;animation-fill-mode:both;animation-name:comment-appear}:host.new,:host.visible{visibility:visible}:host:hover{background:#eee}:host .message-content .content{margin-left:4em;margin-right:3em}:host.abbreviated .message-content .content{max-height:8.5em;overflow-y:hidden;text-overflow:ellipsis}:host .actions{align-items:center;display:flex;margin-left:4em;padding-right:10px}:host .actions button{color:#666}:host .actions banta-timestamp{color:#666;font-size:10pt}.user{align-items:center;display:flex;margin:1em 0 0;position:relative}.user .display-name,.user .username{color:#000;display:block;flex-grow:0;flex-shrink:1;font-size:10pt;margin:0 auto 0 0;max-width:100%;overflow:hidden;padding:0 0 0 1em;position:relative;text-overflow:ellipsis;white-space:nowrap;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;z-index:1}.user .display-name.username,.user .username.username{color:#666;flex-grow:1;flex-shrink:0}.avatar{background-color:#333;background-position:50%;background-size:cover;border-radius:100%;flex-grow:0;flex-shrink:0;height:3em;width:3em}.counted-action{align-items:center;display:flex}.count-indicator{border:1px solid #ccc;border-radius:3px;color:#666;font-size:9pt;padding:0 3px}: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}"]
|
|
7625
7688
|
},] }
|
|
7626
7689
|
];
|
|
7627
7690
|
CommentComponent.propDecorators = {
|
|
7691
|
+
isNew: [{ type: HostBinding, args: ['class.new',] }],
|
|
7692
|
+
visible: [{ type: HostBinding, args: ['class.visible',] }],
|
|
7628
7693
|
message: [{ type: Input }],
|
|
7629
7694
|
showReplyAction: [{ type: Input }],
|
|
7630
7695
|
userSelected: [{ type: Output }],
|
|
7631
7696
|
reported: [{ type: Output }],
|
|
7632
7697
|
upvoted: [{ type: Output }],
|
|
7633
|
-
selected: [{ type: Output }]
|
|
7698
|
+
selected: [{ type: Output }],
|
|
7699
|
+
commentId: [{ type: HostBinding, args: ['attr.data-comment-id',] }]
|
|
7634
7700
|
};
|
|
7635
7701
|
|
|
7636
7702
|
class CommentViewComponent {
|
|
@@ -7703,7 +7769,7 @@ class CommentViewComponent {
|
|
|
7703
7769
|
}
|
|
7704
7770
|
}
|
|
7705
7771
|
}
|
|
7706
|
-
messageIdentity(chatMessage) {
|
|
7772
|
+
messageIdentity(index, chatMessage) {
|
|
7707
7773
|
return chatMessage.id;
|
|
7708
7774
|
}
|
|
7709
7775
|
showNew() {
|
|
@@ -7800,8 +7866,8 @@ class CommentViewComponent {
|
|
|
7800
7866
|
CommentViewComponent.decorators = [
|
|
7801
7867
|
{ type: Component, args: [{
|
|
7802
7868
|
selector: 'banta-comment-view',
|
|
7803
|
-
template: "<div class=\"message-container\">\r\n <ng-content select=\"[data-before]\"></ng-content>\r\n \r\n <a mat-button class=\"nav\"
|
|
7804
|
-
styles: [":host{display:flex;flex-direction:column;flex-grow:1;opacity:1;transition:opacity .2s ease-in}.message-container{background:#fff;color:#
|
|
7869
|
+
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 Show \r\n <ng-container *ngIf=\"newMessages.length === 1\">\r\n {{newMessages.length}} new message\r\n </ng-container>\r\n <ng-container *ngIf=\"newMessages.length > 1\">\r\n {{newMessages.length}} new messages\r\n </ng-container>\r\n <ng-container *ngIf=\"newMessages.length === 0\">\r\n new messages\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 <banta-comment \r\n *ngFor=\"let message of messages; trackBy: messageIdentity\"\r\n class=\"abbreviated\"\r\n [message]=\"message\"\r\n (click)=\"isViewingMore = true\"\r\n [showReplyAction]=\"allowReplies\"\r\n (userSelected)=\"selectMessageUser(message)\"\r\n (upvoted)=\"upvoteMessage(message)\"\r\n (reported)=\"reportMessage(message)\"\r\n (selected)=\"selectMessage(message)\"\r\n ></banta-comment>\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>",
|
|
7870
|
+
styles: [":host{display:flex;flex-direction:column;flex-grow:1;opacity:1;transition:opacity .2s ease-in}.message-container{background:#fff;color:#111;flex-grow:1;opacity:1;overflow-x:hidden;padding:.5em 1em .5em .5em;position:relative;transition:opacity .5s ease-in-out}.message-container.no-scroll{height:auto;overflow-y:visible}.message-container.faded{opacity:.25}.message-container .overlay{bottom:0;left:0;position:absolute;right:0;top:0;z-index:10}:host.fixed-height .message-container{overflow-y:auto}:host-context(.mat-dark-theme) .message-container{background:#111;color:#fff}.empty-state{color:#666;margin:3em;text-align:center}:host-context(.mat-dark-theme) .empty-state{color:#666}a.nav{opacity:0;pointer-events:none;text-align:center;text-transform:uppercase;transition:opacity .4s ease-in-out;width:100%}a.nav.visible{opacity:1;pointer-events:auto}.loading-more{margin:0 auto;padding:2em;text-align:center;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}"]
|
|
7805
7871
|
},] }
|
|
7806
7872
|
];
|
|
7807
7873
|
CommentViewComponent.ctorParameters = () => [
|
|
@@ -7827,19 +7893,34 @@ CommentViewComponent.propDecorators = {
|
|
|
7827
7893
|
* Comments component
|
|
7828
7894
|
*/
|
|
7829
7895
|
class BantaCommentsComponent {
|
|
7830
|
-
constructor(banta, backend) {
|
|
7896
|
+
constructor(banta, backend, elementRef) {
|
|
7831
7897
|
this.banta = banta;
|
|
7832
7898
|
this.backend = backend;
|
|
7899
|
+
this.elementRef = elementRef;
|
|
7833
7900
|
this._upvoted = new Subject();
|
|
7834
7901
|
this._reported = new Subject();
|
|
7835
7902
|
this._selected = new Subject();
|
|
7836
7903
|
this._userSelected = new Subject();
|
|
7837
7904
|
this._subs = new SubSink();
|
|
7905
|
+
this.hashtags = [
|
|
7906
|
+
{ hashtag: 'error', description: 'Cause an error' },
|
|
7907
|
+
{ hashtag: 'timeout', description: 'Cause a slow timeout error' },
|
|
7908
|
+
{ hashtag: 'slow', description: 'Be slow when this message is posted' },
|
|
7909
|
+
];
|
|
7910
|
+
this.participants = [];
|
|
7838
7911
|
this.signInLabel = 'Sign In';
|
|
7839
7912
|
this.sendLabel = 'Send';
|
|
7913
|
+
this.replyLabel = 'Reply';
|
|
7914
|
+
this.sendingLabel = 'Sending';
|
|
7840
7915
|
this.permissionDeniedLabel = 'Send';
|
|
7916
|
+
this.postCommentLabel = 'Post a comment';
|
|
7917
|
+
this.postReplyLabel = 'Post a reply';
|
|
7841
7918
|
this._signInSelected = new Subject();
|
|
7842
7919
|
this._permissionDeniedError = new Subject();
|
|
7920
|
+
this._editAvatarSelected = new Subject();
|
|
7921
|
+
this.sending = false;
|
|
7922
|
+
this.expandError = false;
|
|
7923
|
+
this.selectedMessageVisible = false;
|
|
7843
7924
|
}
|
|
7844
7925
|
ngOnInit() {
|
|
7845
7926
|
this._subs.add(this.banta.userChanged.subscribe(user => this.user = user));
|
|
@@ -7865,14 +7946,39 @@ class BantaCommentsComponent {
|
|
|
7865
7946
|
this._source.close();
|
|
7866
7947
|
this._source = null;
|
|
7867
7948
|
this._source = yield this.backend.getSourceForTopic(topicID);
|
|
7949
|
+
this._source.messageReceived.subscribe(m => this.addParticipant(m));
|
|
7950
|
+
this._source.messageSent.subscribe(m => this.addParticipant(m));
|
|
7951
|
+
this._source.messages.forEach(m => this.addParticipant(m));
|
|
7868
7952
|
});
|
|
7869
7953
|
}
|
|
7954
|
+
addParticipant(message) {
|
|
7955
|
+
if (!message || !message.user || !message.user.id)
|
|
7956
|
+
return;
|
|
7957
|
+
let existing = this.participants.find(x => x.id === message.user.id);
|
|
7958
|
+
if (existing)
|
|
7959
|
+
return;
|
|
7960
|
+
this.participants.push(message.user);
|
|
7961
|
+
}
|
|
7870
7962
|
showSignIn() {
|
|
7871
7963
|
this._signInSelected.next();
|
|
7872
7964
|
}
|
|
7965
|
+
showEditAvatar() {
|
|
7966
|
+
this._editAvatarSelected.next();
|
|
7967
|
+
}
|
|
7968
|
+
get newMessageText() {
|
|
7969
|
+
return this._newMessageText;
|
|
7970
|
+
}
|
|
7971
|
+
set newMessageText(value) {
|
|
7972
|
+
this._newMessageText = value;
|
|
7973
|
+
if (this._newMessageText === '' && this.sendError)
|
|
7974
|
+
setTimeout(() => this.sendError = null);
|
|
7975
|
+
}
|
|
7873
7976
|
get signInSelected() {
|
|
7874
7977
|
return this._signInSelected;
|
|
7875
7978
|
}
|
|
7979
|
+
get editAvatarSelected() {
|
|
7980
|
+
return this._editAvatarSelected;
|
|
7981
|
+
}
|
|
7876
7982
|
get permissionDeniedError() {
|
|
7877
7983
|
return this._permissionDeniedError;
|
|
7878
7984
|
}
|
|
@@ -7881,8 +7987,12 @@ class BantaCommentsComponent {
|
|
|
7881
7987
|
}
|
|
7882
7988
|
get canComment() {
|
|
7883
7989
|
var _a;
|
|
7990
|
+
if (!this.user)
|
|
7991
|
+
return false;
|
|
7884
7992
|
if (!this.user.permissions)
|
|
7885
7993
|
return true;
|
|
7994
|
+
if (!this.user.permissions.canComment)
|
|
7995
|
+
return true;
|
|
7886
7996
|
return (_a = this.user.permissions) === null || _a === void 0 ? void 0 : _a.canComment(this.source);
|
|
7887
7997
|
}
|
|
7888
7998
|
get upvoted() {
|
|
@@ -7907,37 +8017,58 @@ class BantaCommentsComponent {
|
|
|
7907
8017
|
insertReplyEmoji(text) {
|
|
7908
8018
|
this.replyMessage += text;
|
|
7909
8019
|
}
|
|
8020
|
+
indicateError(message) {
|
|
8021
|
+
this.sendError = new Error(message);
|
|
8022
|
+
setTimeout(() => {
|
|
8023
|
+
this.expandError = true;
|
|
8024
|
+
setTimeout(() => {
|
|
8025
|
+
this.expandError = false;
|
|
8026
|
+
}, 5 * 1000);
|
|
8027
|
+
});
|
|
8028
|
+
}
|
|
7910
8029
|
sendMessage() {
|
|
7911
8030
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7912
8031
|
if (!this.source)
|
|
7913
8032
|
return;
|
|
7914
|
-
|
|
7915
|
-
this.
|
|
7916
|
-
if (text === '')
|
|
7917
|
-
return;
|
|
7918
|
-
let message = {
|
|
7919
|
-
user: this.user,
|
|
7920
|
-
sentAt: Date.now(),
|
|
7921
|
-
upvotes: 0,
|
|
7922
|
-
message: text
|
|
7923
|
-
};
|
|
8033
|
+
this.sending = true;
|
|
8034
|
+
this.sendError = null;
|
|
7924
8035
|
try {
|
|
7925
|
-
|
|
8036
|
+
let text = (this.newMessageText || '').trim();
|
|
8037
|
+
if (text === '')
|
|
8038
|
+
return;
|
|
8039
|
+
let message = {
|
|
8040
|
+
user: this.user,
|
|
8041
|
+
sentAt: Date.now(),
|
|
8042
|
+
upvotes: 0,
|
|
8043
|
+
message: text
|
|
8044
|
+
};
|
|
8045
|
+
try {
|
|
8046
|
+
yield this.source.send(message);
|
|
8047
|
+
this.newMessageText = '';
|
|
8048
|
+
}
|
|
8049
|
+
catch (e) {
|
|
8050
|
+
this.indicateError(`Could not send: ${e.message}`);
|
|
8051
|
+
console.error(`Failed to send message: `, message);
|
|
8052
|
+
console.error(e);
|
|
8053
|
+
}
|
|
7926
8054
|
}
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
console.error(e);
|
|
8055
|
+
finally {
|
|
8056
|
+
this.sending = false;
|
|
7930
8057
|
}
|
|
7931
8058
|
});
|
|
7932
8059
|
}
|
|
7933
8060
|
upvoteMessage(message) {
|
|
7934
|
-
this
|
|
8061
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8062
|
+
this._upvoted.next(message);
|
|
8063
|
+
yield this.backend.upvoteMessage(message.topicId, message.parentMessageId ? message.parentMessageId : message.id, message.parentMessageId ? message.id : undefined);
|
|
8064
|
+
});
|
|
7935
8065
|
}
|
|
7936
8066
|
reportMessage(message) {
|
|
7937
8067
|
this._reported.next(message);
|
|
7938
8068
|
}
|
|
7939
8069
|
unselectMessage() {
|
|
7940
8070
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8071
|
+
let message = this.selectedMessage;
|
|
7941
8072
|
this._selected.next(null);
|
|
7942
8073
|
this.selectedMessage = null;
|
|
7943
8074
|
if (this.selectedMessageThread) {
|
|
@@ -7945,13 +8076,18 @@ class BantaCommentsComponent {
|
|
|
7945
8076
|
this.selectedMessageThread.close();
|
|
7946
8077
|
this.selectedMessageThread = null;
|
|
7947
8078
|
}
|
|
8079
|
+
if (message)
|
|
8080
|
+
setTimeout(() => this.scrollToMessage(message));
|
|
7948
8081
|
});
|
|
7949
8082
|
}
|
|
7950
8083
|
selectMessage(message) {
|
|
7951
8084
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7952
8085
|
this._selected.next(message);
|
|
7953
8086
|
this.selectedMessage = message;
|
|
7954
|
-
this.
|
|
8087
|
+
setTimeout(() => this.selectedMessageVisible = true);
|
|
8088
|
+
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
8089
|
+
this.selectedMessageThread = yield this.backend.getSourceForThread(this.topicID, message.id);
|
|
8090
|
+
}), 250);
|
|
7955
8091
|
});
|
|
7956
8092
|
}
|
|
7957
8093
|
selectMessageUser(message) {
|
|
@@ -7972,19 +8108,28 @@ class BantaCommentsComponent {
|
|
|
7972
8108
|
this.replyMessage = '';
|
|
7973
8109
|
});
|
|
7974
8110
|
}
|
|
8111
|
+
scrollToMessage(message) {
|
|
8112
|
+
let el = this.elementRef.nativeElement.querySelector(`[data-comment-id="${message.id}"]`);
|
|
8113
|
+
if (!el)
|
|
8114
|
+
return;
|
|
8115
|
+
el.scrollIntoView({ block: 'center', inline: 'start' });
|
|
8116
|
+
}
|
|
7975
8117
|
}
|
|
7976
8118
|
BantaCommentsComponent.decorators = [
|
|
7977
8119
|
{ type: Component, args: [{
|
|
7978
8120
|
selector: 'banta-comments',
|
|
7979
|
-
template: "\r\n<div class=\"focused\" *ngIf=\"selectedMessage\">\r\n\r\n <div>\r\n <a mat-button href=\"javascript:;\" (click)=\"unselectMessage()\">\r\n <mat-icon>arrow_back</mat-icon>\r\n Latest Comments\r\n </a>\r\n </div>\r\n\r\n <banta-comment \r\n [message]=\"selectedMessage\"\r\n ></banta-comment>\r\n\r\n <div class=\"replies\">\r\n
|
|
7980
|
-
styles: [":host{flex-direction:column}
|
|
8121
|
+
template: "\r\n<div class=\"focused\" [class.visible]=\"selectedMessageVisible\" *ngIf=\"selectedMessage\">\r\n\r\n <div>\r\n <a mat-button href=\"javascript:;\" (click)=\"unselectMessage()\">\r\n <mat-icon>arrow_back</mat-icon>\r\n Latest Comments\r\n </a>\r\n </div>\r\n\r\n <banta-comment \r\n [message]=\"selectedMessage\"\r\n ></banta-comment>\r\n\r\n <div class=\"replies\">\r\n\r\n <ng-container *ngIf=\"!selectedMessageThread\">\r\n <div class=\"loading\">\r\n <mat-spinner></mat-spinner>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"selectedMessageThread\">\r\n <banta-comment-view \r\n [source]=\"selectedMessageThread\"\r\n [allowReplies]=\"false\"\r\n [fixedHeight]=\"false\"\r\n [showEmptyState]=\"false\"\r\n [newestLast]=\"true\"\r\n ></banta-comment-view>\r\n\r\n <banta-comment-field\r\n [sendLabel]=\"replyLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n (signInSelected)=\"showSignIn()\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n [source]=\"selectedMessageThread\"\r\n [canComment]=\"canComment\"\r\n [signInLabel]=\"signInLabel\"\r\n [user]=\"user\"\r\n [label]=\"postReplyLabel\"\r\n ></banta-comment-field>\r\n </ng-container>\r\n </div>\r\n</div>\r\n\r\n<div class=\"main\" [class.hidden]=\"selectedMessage\">\r\n <banta-comment-field\r\n [source]=\"source\"\r\n [user]=\"user\"\r\n [sendLabel]=\"sendLabel\"\r\n [sendingLabel]=\"sendingLabel\"\r\n [signInLabel]=\"signInLabel\"\r\n [canComment]=\"canComment\"\r\n [hashtags]=\"hashtags\"\r\n [participants]=\"participants\"\r\n [label]=\"postCommentLabel\"\r\n (editAvatarSelected)=\"showEditAvatar()\"\r\n (signInSelected)=\"showSignIn()\"\r\n ></banta-comment-field>\r\n\r\n <banta-comment-view \r\n [class.faded]=\"selectedMessage\"\r\n [source]=\"source\"\r\n [fixedHeight]=\"fixedHeight\"\r\n [maxMessages]=\"maxMessages\"\r\n [maxVisibleMessages]=\"maxVisibleMessages\"\r\n [genericAvatarUrl]=\"genericAvatarUrl\"\r\n (userSelected)=\"selectMessageUser($event)\"\r\n (selected)=\"selectMessage($event)\"\r\n (upvoted)=\"upvoteMessage($event)\"\r\n (reported)=\"reportMessage($event)\"\r\n ></banta-comment-view>\r\n</div>",
|
|
8122
|
+
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-duration:.4s;-webkit-animation-fill-mode:both;-webkit-animation-name:select-comment;animation-duration:.4s;animation-fill-mode:both;animation-name:select-comment}.focused .replies{margin-left:4em;margin-top:1em}banta-comment-view{opacity:1;transition:opacity .4s ease-in-out}banta-comment-view.faded{opacity:.25}.loading{display:block;margin:0 auto;min-height:16em;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}.main.hidden{display:none}"]
|
|
7981
8123
|
},] }
|
|
7982
8124
|
];
|
|
7983
8125
|
BantaCommentsComponent.ctorParameters = () => [
|
|
7984
8126
|
{ type: BantaService },
|
|
7985
|
-
{ type: ChatBackendService }
|
|
8127
|
+
{ type: ChatBackendService },
|
|
8128
|
+
{ type: ElementRef }
|
|
7986
8129
|
];
|
|
7987
8130
|
BantaCommentsComponent.propDecorators = {
|
|
8131
|
+
hashtags: [{ type: Input }],
|
|
8132
|
+
participants: [{ type: Input }],
|
|
7988
8133
|
source: [{ type: Input }],
|
|
7989
8134
|
fixedHeight: [{ type: Input }],
|
|
7990
8135
|
maxMessages: [{ type: Input }],
|
|
@@ -7993,8 +8138,13 @@ BantaCommentsComponent.propDecorators = {
|
|
|
7993
8138
|
topicID: [{ type: Input }],
|
|
7994
8139
|
signInLabel: [{ type: Input }],
|
|
7995
8140
|
sendLabel: [{ type: Input }],
|
|
8141
|
+
replyLabel: [{ type: Input }],
|
|
8142
|
+
sendingLabel: [{ type: Input }],
|
|
7996
8143
|
permissionDeniedLabel: [{ type: Input }],
|
|
8144
|
+
postCommentLabel: [{ type: Input }],
|
|
8145
|
+
postReplyLabel: [{ type: Input }],
|
|
7997
8146
|
signInSelected: [{ type: Output }],
|
|
8147
|
+
editAvatarSelected: [{ type: Output }],
|
|
7998
8148
|
permissionDeniedError: [{ type: Output }],
|
|
7999
8149
|
upvoted: [{ type: Output }],
|
|
8000
8150
|
reported: [{ type: Output }],
|
|
@@ -8070,11 +8220,243 @@ LiveCommentComponent.propDecorators = {
|
|
|
8070
8220
|
message: [{ type: Input }]
|
|
8071
8221
|
};
|
|
8072
8222
|
|
|
8223
|
+
class CommentFieldComponent {
|
|
8224
|
+
constructor() {
|
|
8225
|
+
this.canComment = true;
|
|
8226
|
+
this.signInSelected = new Subject();
|
|
8227
|
+
this.editAvatarSelected = new Subject();
|
|
8228
|
+
this.sending = false;
|
|
8229
|
+
this.expandError = false;
|
|
8230
|
+
this.text = '';
|
|
8231
|
+
this.sendLabel = 'Send';
|
|
8232
|
+
this.sendingLabel = 'Sending';
|
|
8233
|
+
this.label = 'Post a comment';
|
|
8234
|
+
this.permissionDeniedLabel = 'Unavailable';
|
|
8235
|
+
this.signInLabel = 'Sign In';
|
|
8236
|
+
this.participants = [];
|
|
8237
|
+
this.autocompleteVisible = false;
|
|
8238
|
+
this.autocompleteOptions = [];
|
|
8239
|
+
this.autoCompleteSelected = 0;
|
|
8240
|
+
}
|
|
8241
|
+
ngAfterViewInit() {
|
|
8242
|
+
let root = document.body.querySelector('[ng-version]') || document.body;
|
|
8243
|
+
root.appendChild(this.autocompleteEl.nativeElement);
|
|
8244
|
+
}
|
|
8245
|
+
showAutoComplete(options) {
|
|
8246
|
+
this.autoCompleteSelected = 0;
|
|
8247
|
+
this.autocompleteOptions = options;
|
|
8248
|
+
let pos = this.autocompleteContainerEl.nativeElement.getBoundingClientRect();
|
|
8249
|
+
let size = this.autocompleteEl.nativeElement.getBoundingClientRect();
|
|
8250
|
+
this.autocompleteEl.nativeElement.style.left = `${pos.left}px`;
|
|
8251
|
+
this.autocompleteEl.nativeElement.style.top = `${pos.top}px`;
|
|
8252
|
+
this.autocompleteEl.nativeElement.style.width = `${pos.width}px`;
|
|
8253
|
+
this.autocompleteVisible = true;
|
|
8254
|
+
}
|
|
8255
|
+
activateAutoComplete(option) {
|
|
8256
|
+
option.action();
|
|
8257
|
+
this.dismissAutoComplete();
|
|
8258
|
+
}
|
|
8259
|
+
dismissAutoComplete() {
|
|
8260
|
+
this.autocompleteVisible = false;
|
|
8261
|
+
this.completionFunc = null;
|
|
8262
|
+
this.completionPrefix = '';
|
|
8263
|
+
}
|
|
8264
|
+
indicateError(message) {
|
|
8265
|
+
this.sendError = new Error(message);
|
|
8266
|
+
setTimeout(() => {
|
|
8267
|
+
this.expandError = true;
|
|
8268
|
+
setTimeout(() => {
|
|
8269
|
+
this.expandError = false;
|
|
8270
|
+
}, 5 * 1000);
|
|
8271
|
+
});
|
|
8272
|
+
}
|
|
8273
|
+
autocomplete(replacement) {
|
|
8274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8275
|
+
let el = this.textareaEl.nativeElement;
|
|
8276
|
+
this.text = this.text.slice(0, el.selectionStart - this.completionPrefix.length) + replacement + this.text.slice(el.selectionStart);
|
|
8277
|
+
});
|
|
8278
|
+
}
|
|
8279
|
+
insert(str) {
|
|
8280
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8281
|
+
let el = this.textareaEl.nativeElement;
|
|
8282
|
+
this.text = this.text.slice(0, el.selectionStart) + str + this.text.slice(el.selectionStart);
|
|
8283
|
+
});
|
|
8284
|
+
}
|
|
8285
|
+
onKeyDown(event) {
|
|
8286
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8287
|
+
console.log(event.key);
|
|
8288
|
+
if (this.autocompleteVisible) {
|
|
8289
|
+
if (event.key === 'Escape') {
|
|
8290
|
+
this.dismissAutoComplete();
|
|
8291
|
+
return;
|
|
8292
|
+
}
|
|
8293
|
+
if (event.key === 'Shift') {
|
|
8294
|
+
return;
|
|
8295
|
+
}
|
|
8296
|
+
if (event.key === 'Enter') {
|
|
8297
|
+
this.activateAutoComplete(this.autocompleteOptions[this.autoCompleteSelected]);
|
|
8298
|
+
event.stopPropagation();
|
|
8299
|
+
event.preventDefault();
|
|
8300
|
+
return;
|
|
8301
|
+
}
|
|
8302
|
+
if (event.key === 'ArrowUp') {
|
|
8303
|
+
if (this.autoCompleteSelected === 0)
|
|
8304
|
+
this.autoCompleteSelected = this.autocompleteOptions.length - 1;
|
|
8305
|
+
else
|
|
8306
|
+
this.autoCompleteSelected = this.autoCompleteSelected - 1;
|
|
8307
|
+
event.stopPropagation();
|
|
8308
|
+
event.preventDefault();
|
|
8309
|
+
return;
|
|
8310
|
+
}
|
|
8311
|
+
else if (event.key === 'ArrowDown') {
|
|
8312
|
+
this.autoCompleteSelected = (this.autoCompleteSelected + 1) % this.autocompleteOptions.length;
|
|
8313
|
+
event.stopPropagation();
|
|
8314
|
+
event.preventDefault();
|
|
8315
|
+
return;
|
|
8316
|
+
}
|
|
8317
|
+
}
|
|
8318
|
+
if (event.key === 'Enter' && event.ctrlKey) {
|
|
8319
|
+
yield this.sendMessage();
|
|
8320
|
+
return;
|
|
8321
|
+
}
|
|
8322
|
+
if (this.completionFunc) {
|
|
8323
|
+
if (event.key === 'Backspace') {
|
|
8324
|
+
this.completionPrefix = this.completionPrefix.slice(0, this.completionPrefix.length - 1);
|
|
8325
|
+
if (this.completionPrefix === '') {
|
|
8326
|
+
this.dismissAutoComplete();
|
|
8327
|
+
return;
|
|
8328
|
+
}
|
|
8329
|
+
}
|
|
8330
|
+
else if (event.key === ' ' || event.key.length > 1) {
|
|
8331
|
+
this.dismissAutoComplete();
|
|
8332
|
+
return;
|
|
8333
|
+
}
|
|
8334
|
+
else {
|
|
8335
|
+
this.completionPrefix += event.key;
|
|
8336
|
+
}
|
|
8337
|
+
this.showAutoComplete(this.completionFunc(this.completionPrefix));
|
|
8338
|
+
}
|
|
8339
|
+
else {
|
|
8340
|
+
if (event.key === ':') {
|
|
8341
|
+
this.startAutoComplete(event, prefix => {
|
|
8342
|
+
prefix = prefix.slice(1);
|
|
8343
|
+
// makes :-), :-( etc work (as they are ":)" etc in the db)
|
|
8344
|
+
if (prefix.startsWith('-'))
|
|
8345
|
+
prefix = prefix.slice(1);
|
|
8346
|
+
return Object.keys(EMOJIS)
|
|
8347
|
+
.filter(k => k.includes(prefix) || EMOJIS[k].keywords.some(kw => kw.includes(prefix)))
|
|
8348
|
+
.map(k => ({
|
|
8349
|
+
label: `${EMOJIS[k].char} ${k}`,
|
|
8350
|
+
action: () => this.autocomplete(EMOJIS[k].char)
|
|
8351
|
+
}))
|
|
8352
|
+
.slice(0, 5);
|
|
8353
|
+
});
|
|
8354
|
+
}
|
|
8355
|
+
else if (event.key === '@') {
|
|
8356
|
+
this.startAutoComplete(event, prefix => {
|
|
8357
|
+
prefix = prefix.slice(1);
|
|
8358
|
+
return this.participants.filter(x => x.username.includes(prefix))
|
|
8359
|
+
.map(p => ({
|
|
8360
|
+
label: `@${p.username} -- ${p.displayName}`,
|
|
8361
|
+
action: () => this.autocomplete(`@${p.username}`)
|
|
8362
|
+
}));
|
|
8363
|
+
});
|
|
8364
|
+
}
|
|
8365
|
+
else if (event.key === '#') {
|
|
8366
|
+
this.startAutoComplete(event, prefix => {
|
|
8367
|
+
prefix = prefix.slice(1);
|
|
8368
|
+
return this.hashtags
|
|
8369
|
+
.filter(ht => ht.hashtag.includes(prefix))
|
|
8370
|
+
.map(ht => ({
|
|
8371
|
+
label: `#${ht.hashtag}${ht.description ? ` -- ${ht.description}` : ``}`,
|
|
8372
|
+
action: () => this.autocomplete(`#${ht.hashtag}`)
|
|
8373
|
+
}))
|
|
8374
|
+
.slice(0, 5);
|
|
8375
|
+
});
|
|
8376
|
+
}
|
|
8377
|
+
}
|
|
8378
|
+
});
|
|
8379
|
+
}
|
|
8380
|
+
startAutoComplete(event, completionFunc) {
|
|
8381
|
+
this.completionPrefix = event.key;
|
|
8382
|
+
this.completionFunc = completionFunc;
|
|
8383
|
+
this.showAutoComplete(this.completionFunc(this.completionPrefix));
|
|
8384
|
+
}
|
|
8385
|
+
onBlur() {
|
|
8386
|
+
setTimeout(() => this.dismissAutoComplete(), 250);
|
|
8387
|
+
}
|
|
8388
|
+
insertEmoji(text) {
|
|
8389
|
+
this.text += text;
|
|
8390
|
+
}
|
|
8391
|
+
showSignIn() {
|
|
8392
|
+
this.signInSelected.next();
|
|
8393
|
+
}
|
|
8394
|
+
showEditAvatar() {
|
|
8395
|
+
this.editAvatarSelected.next();
|
|
8396
|
+
}
|
|
8397
|
+
sendMessage() {
|
|
8398
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8399
|
+
if (!this.source)
|
|
8400
|
+
return;
|
|
8401
|
+
this.sending = true;
|
|
8402
|
+
this.sendError = null;
|
|
8403
|
+
try {
|
|
8404
|
+
let text = (this.text || '').trim();
|
|
8405
|
+
if (text === '')
|
|
8406
|
+
return;
|
|
8407
|
+
let message = {
|
|
8408
|
+
user: this.user,
|
|
8409
|
+
sentAt: Date.now(),
|
|
8410
|
+
upvotes: 0,
|
|
8411
|
+
message: text
|
|
8412
|
+
};
|
|
8413
|
+
try {
|
|
8414
|
+
yield this.source.send(message);
|
|
8415
|
+
this.text = '';
|
|
8416
|
+
}
|
|
8417
|
+
catch (e) {
|
|
8418
|
+
this.indicateError(`Could not send: ${e.message}`);
|
|
8419
|
+
console.error(`Failed to send message: `, message);
|
|
8420
|
+
console.error(e);
|
|
8421
|
+
}
|
|
8422
|
+
}
|
|
8423
|
+
finally {
|
|
8424
|
+
this.sending = false;
|
|
8425
|
+
}
|
|
8426
|
+
});
|
|
8427
|
+
}
|
|
8428
|
+
}
|
|
8429
|
+
CommentFieldComponent.decorators = [
|
|
8430
|
+
{ type: Component, args: [{
|
|
8431
|
+
selector: 'banta-comment-field',
|
|
8432
|
+
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=\"Type your comment\"\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 <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 <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\r\n </button>\r\n <button \r\n *ngIf=\"!canComment\"\r\n type=\"button\"\r\n (click)=\"showPermissionDenied()\"\r\n mat-raised-button \r\n color=\"primary\"\r\n >{{permissionDeniedLabel}}</button>\r\n </ng-container>\r\n </div>\r\n</form>\r\n",
|
|
8433
|
+
styles: ["@-webkit-keyframes comment-field-appear{0%{opacity:0;transform:translateY(128px)}to{opacity:1;transform:translate(0)}}@keyframes comment-field-appear{0%{opacity:0;transform:translateY(128px)}to{opacity:1;transform:translate(0)}}:host{-webkit-animation-delay:.4s;-webkit-animation-duration:.8s;-webkit-animation-fill-mode:both;-webkit-animation-name:comment-field-appear;animation-delay:.4s;animation-duration:.8s;animation-fill-mode:both;animation-name:comment-field-appear;display:block;margin:0 2em 0 0}.avatar-container{display:flex;justify-content:flex-end;width:calc(48px + 1.75em)}.avatar-container .avatar{background:pink;background-position:50%;background-repeat:no-repeat;background-size:cover;border-radius:100%;height:48px;margin-right:.75em;margin-top:.75em;width:48px}form{align-items:center;display:flex;padding:.5em 0}form .text-container{display:flex;flex-grow:1;position:relative}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;position:absolute;right:0}form .text-container .error-message,form .text-container mat-spinner.loading{bottom:.5em;left:.5em;position:absolute}form .text-container .error-message{color:#683333;max-width:1.5em;overflow-x:hidden;transition:max-width 2s ease-in-out;white-space:nowrap}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;border:1px solid #333;color:#fff;height:1em;width:100%}form .actions{margin-left:1em}form button{display:block;margin:0 0 0 auto}form.new-message{align-items:flex-start;display:flex}form.new-message .field-container{display:flex;flex-direction:column;flex-grow:1}form.new-message mat-form-field{width:100%}form.new-message button{margin:1.25em 0 0 1em}button.send{min-width:9em}textarea{max-height:7em}.autocomplete-container{pointer-events:none;position:relative;top:-2em;width:calc(100% - 2em)}.autocomplete{background:#333;display:flex;flex-direction:column;padding:.5em;pointer-events:none;position:absolute;visibility:hidden;z-index:100}.autocomplete.visible{pointer-events:auto;visibility:visible}.autocomplete a{text-align:left;width:100%}.autocomplete a.active{background:#555}"]
|
|
8434
|
+
},] }
|
|
8435
|
+
];
|
|
8436
|
+
CommentFieldComponent.propDecorators = {
|
|
8437
|
+
source: [{ type: Input }],
|
|
8438
|
+
user: [{ type: Input }],
|
|
8439
|
+
canComment: [{ type: Input }],
|
|
8440
|
+
signInSelected: [{ type: Output }],
|
|
8441
|
+
editAvatarSelected: [{ type: Output }],
|
|
8442
|
+
sendLabel: [{ type: Input }],
|
|
8443
|
+
sendingLabel: [{ type: Input }],
|
|
8444
|
+
label: [{ type: Input }],
|
|
8445
|
+
permissionDeniedLabel: [{ type: Input }],
|
|
8446
|
+
signInLabel: [{ type: Input }],
|
|
8447
|
+
autocompleteEl: [{ type: ViewChild, args: ['autocomplete',] }],
|
|
8448
|
+
autocompleteContainerEl: [{ type: ViewChild, args: ['autocompleteContainer',] }],
|
|
8449
|
+
textareaEl: [{ type: ViewChild, args: ['textarea',] }],
|
|
8450
|
+
hashtags: [{ type: Input }],
|
|
8451
|
+
participants: [{ type: Input }]
|
|
8452
|
+
};
|
|
8453
|
+
|
|
8073
8454
|
const COMPONENTS$3 = [
|
|
8074
8455
|
CommentComponent,
|
|
8075
8456
|
CommentViewComponent,
|
|
8076
8457
|
BantaCommentsComponent,
|
|
8077
|
-
LiveCommentComponent
|
|
8458
|
+
LiveCommentComponent,
|
|
8459
|
+
CommentFieldComponent
|
|
8078
8460
|
];
|
|
8079
8461
|
class CommentsModule {
|
|
8080
8462
|
}
|
|
@@ -8149,5 +8531,5 @@ BantaSdkModule.decorators = [
|
|
|
8149
8531
|
* Generated bundle index. Do not edit.
|
|
8150
8532
|
*/
|
|
8151
8533
|
|
|
8152
|
-
export { BantaChatComponent, BantaCommentsComponent, BantaCommonModule, BantaComponent, BantaLogoComponent, BantaSdkModule, BantaService, ChatBackendService, ChatMessageComponent, ChatModule, ChatViewComponent, CommentComponent, CommentViewComponent, CommentsModule, EMOJIS, EmojiModule, EmojiSelectorButtonComponent, EmojiSelectorPanelComponent, LiveChatMessageComponent, LiveCommentComponent, LiveMessageComponent, TimestampComponent, lazyConnection };
|
|
8534
|
+
export { BantaChatComponent, BantaCommentsComponent, BantaCommonModule, BantaComponent, BantaLogoComponent, BantaSdkModule, BantaService, ChatBackendService, ChatMessageComponent, ChatModule, ChatViewComponent, CommentComponent, CommentFieldComponent, CommentViewComponent, CommentsModule, EMOJIS, EmojiModule, EmojiSelectorButtonComponent, EmojiSelectorPanelComponent, LiveChatMessageComponent, LiveCommentComponent, LiveMessageComponent, TimestampComponent, lazyConnection };
|
|
8153
8535
|
//# sourceMappingURL=banta-sdk.js.map
|