Skip to content
Snippets Groups Projects
Commit ba9ed6ed authored by Klaus-Dieter Quibeldey-Cirkel's avatar Klaus-Dieter Quibeldey-Cirkel
Browse files

Merge branch '702-new-filter-and-sorting-presets' into 'staging'

Resolve "New filter and sorting presets"

Closes #702

See merge request arsnova/frag.jetzt!702
parents bffe8cf2 b9c81020
Branches
Tags
No related merge requests found
......@@ -3,6 +3,15 @@
[ngClass]="{'search-container' : !scroll, 'search-container-fixed' : scroll}"
(window:scroll)="checkScroll()"
fxLayoutAlign="center">
<button id="filter-close-button"
mat-icon-button
class="searchBarButton"
*ngIf="filter.filterType"
(click)="applyFilterByKey(null)"
aria-labelledby="close_filter">
<mat-icon class="searchBarIcon red">close</mat-icon>
</button>
<mat-label *ngIf="deviceType === 'desktop'"
fxLayoutAlign="center center">
<mat-icon class="search-icon">search</mat-icon>
......@@ -13,14 +22,14 @@
[ngClass]="{'desktop-input': deviceType === 'desktop',
'mobile-input': deviceType === 'mobile' && !search, 'mobile-input-2': deviceType === 'mobile' && search }"
(input)="searchComments()"
[(ngModel)]="searchInput"
[(ngModel)]="filter.currentSearch"
[placeholder]="searchPlaceholder"
aria-labelledby="search-box-input-description"/>
<button id="search_close-button"
mat-icon-button
class="searchBarButton close red"
*ngIf="searchInput !== '' || search"
(click)="hideCommentsList=false; searchInput = ''; search = false; searchPlaceholder = '';">
*ngIf="filter.currentSearch || search"
(click)="abortSearch()">
<mat-icon>close</mat-icon>
</button>
......@@ -55,7 +64,7 @@
aria-labelledby="time_settings"
*ngIf="!searchBox.value && comments && comments.length > 0 && !search"
[matMenuTriggerFor]="timeMenu"
[ngClass]="{'active-filter': period !== 'time-all'}"
[ngClass]="{'active-filter': filter.period !== 'time-all'}"
matTooltip="{{ 'comment-list.select-time' | translate }}">
<mat-icon class="searchBarIcon">history</mat-icon>
</button>
......@@ -73,25 +82,25 @@
xPosition="before">
<button mat-menu-item
matTooltip="{{ 'comment-list.time' | translate }}"
(click)="sortComments(time)"
(click)="applySortingByKey('time')"
aria-labelledby="access_time">
<mat-icon [ngClass]="{time: 'unread-icon'}[currentSort]">update</mat-icon>
<span>{{ 'comment-list.sort-list-time' | translate }}</span>
<mat-icon [ngClass]="{time: 'timesort'}[filter.sortType]">update</mat-icon>
<span [ngClass]="{time: 'timesort'}[filter.sortType]">{{ 'comment-list.sort-list-time' | translate }}</span>
</button>
<button mat-menu-item
matTooltip="{{ 'comment-list.vote-asc' | translate }}"
(click)="sortComments(votedesc)"
(click)="applySortingByKey('votedesc')"
aria-labelledby="keyboard_arrow_up">
<mat-icon [ngClass]="{votedesc: 'up'}[currentSort]">thumb_up</mat-icon>
<mat-icon [ngClass]="{votedesc: 'up'}[filter.sortType]">thumb_up</mat-icon>
<span>{{ 'comment-list.sort-vote-asc' | translate }}</span>
</button>
<button mat-menu-item
matTooltip="{{ 'comment-list.vote-desc' | translate }}"
(click)="sortComments(voteasc)"
(click)="applySortingByKey('voteasc')"
aria-labelledby="keyboard_arrow_down">
<mat-icon [ngClass]="{voteasc: 'down'}[currentSort]">thumb_down</mat-icon>
<mat-icon [ngClass]="{voteasc: 'down'}[filter.sortType]">thumb_down</mat-icon>
<span>{{ 'comment-list.sort-vote-desc' | translate }}</span>
</button>
</mat-menu>
......@@ -99,7 +108,7 @@
<mat-menu #timeMenu="matMenu" xPosition="before">
<div *ngFor="let periodItem of periodsList">
<button mat-menu-item (click)="setTimePeriod(periodItem)" class="period"
[ngClass]="{'selected': periodItem === period}"
[ngClass]="{'selected': periodItem === filter.period}"
aria-labelledby="{{periodItem}}">
<span>{{ ('comment-list.select-' + periodItem) | translate }}</span>
</button>
......@@ -122,7 +131,10 @@
[userRole]="user.role"
[parseVote]="getVote(current)"
[moderator]="true"
(clickedUserNumber)="clickedUserNumber($event)">
[commentsWrittenByUser]="commentsWrittenByUsers.get(current.creatorId).size"
(clickedOnTag)="applyFilterByKey('tag', $event)"
(clickedUserNumber)="applyFilterByKey('userNumber', $event)"
(clickedOnKeyword)="applyFilterByKey('keyword', $event)">
</app-comment>
</div>
<ars-mat-paginator
......@@ -136,7 +148,7 @@
<ars-row [height]="64">
</ars-row>
<div *ngIf="comments && (commentsFilteredByTime.length < 1 && period === 'time-all' || comments.length === 0) && !isLoading"
<div *ngIf="comments && (commentsFilteredByTime.length < 1 && filter.period === 'time-all' || comments.length === 0) && !isLoading"
fxLayout="row"
fxLayoutAlign="center center"
class="no-comments">
......@@ -145,7 +157,7 @@
</div>
<div *ngIf="(filteredComments && filteredComments.length === 0 && hideCommentsList)
|| (comments && commentsFilteredByTime.length === 0 && period !== 'time-all') && !isLoading && comments.length > 0"
|| (comments && commentsFilteredByTime.length === 0 && filter.period !== 'time-all') && !isLoading && comments.length > 0"
fxLayout="row"
fxLayoutAlign="center center"
class="no-comments">
......
......@@ -101,6 +101,10 @@ h3 {
margin: 10px;
}
.timesort {
color: var(--primary);
}
.up {
color: var(--green);
}
......
......@@ -21,8 +21,16 @@ import { DeleteCommentsComponent } from '../../creator/_dialogs/delete-comments/
import { Export } from '../../../models/export';
import { NotificationService } from '../../../services/util/notification.service';
import { BonusTokenService } from '../../../services/http/bonus-token.service';
import { CommentFilter, Period } from '../../../utils/filter-options';
import { Period } from '../../../utils/filter-options';
import { PageEvent } from '@angular/material/paginator';
import {
CommentListFilter,
FilterType,
FilterTypeKey,
SortType,
SortTypeKey
} from '../../shared/comment-list/comment-list.filter';
import { ModeratorService } from '../../../services/http/moderator.service';
@Component({
......@@ -47,7 +55,6 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
voteasc = 'voteasc';
votedesc = 'votedesc';
time = 'time';
currentSort: string;
read = 'read';
unread = 'unread';
favorite = 'favorite';
......@@ -56,21 +63,19 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
ack = 'ack';
bookmark = 'bookmark';
userNumber = 'userNumber';
currentFilter = '';
commentVoteMap = new Map<string, Vote>();
scroll = false;
scrollExtended = false;
searchInput = '';
search = false;
searchPlaceholder = '';
periodsList = Object.values(Period);
period: Period = Period.twoWeeks;
fromNow: number;
headerInterface = null;
pageIndex = 0;
pageSize = 10;
pageSizeOptions = [5, 10, 25];
showFirstLastButtons = true;
commentsWrittenByUsers: Map<string, Set<string>> = new Map<string, Set<string>>();
filter: CommentListFilter;
constructor(
private commentService: CommentService,
......@@ -83,12 +88,14 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
private router: Router,
private notificationService: NotificationService,
private translationService: TranslateService,
private bonusTokenService: BonusTokenService
private bonusTokenService: BonusTokenService,
private moderationService: ModeratorService
) {
langService.langEmitter.subscribe(lang => translateService.use(lang));
this.filter = CommentListFilter.loadCurrentFilter();
}
handlePageEvent(e:PageEvent){
handlePageEvent(e: PageEvent) {
this.pageIndex = e.pageIndex;
this.pageSize = e.pageSize;
}
......@@ -113,25 +120,25 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
}
dialogRef.componentInstance.tags = tags;
dialogRef.afterClosed()
.subscribe(result => {
if (!result || result === 'abort') {
return;
} else {
updRoom.tags = result;
this.roomService.updateRoom(updRoom)
.subscribe((room) => {
this.room = room;
this.translateService.get('room-page.changes-successful').subscribe(msg => {
this.notificationService.show(msg);
});
},
error => {
this.translateService.get('room-page.changes-gone-wrong').subscribe(msg => {
this.notificationService.show(msg);
});
});
}
});
.subscribe(result => {
if (!result || result === 'abort') {
return;
} else {
updRoom.tags = result;
this.roomService.updateRoom(updRoom)
.subscribe((room) => {
this.room = room;
this.translateService.get('room-page.changes-successful').subscribe(msg => {
this.notificationService.show(msg);
});
},
error => {
this.translateService.get('room-page.changes-gone-wrong').subscribe(msg => {
this.notificationService.show(msg);
});
});
}
});
});
nav('deleteQuestions', () => {
const dialogRef = this.dialog.open(DeleteCommentsComponent, {
......@@ -139,14 +146,14 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
});
dialogRef.componentInstance.roomId = this.roomId;
dialogRef.afterClosed()
.subscribe(result => {
if (result === 'delete') {
this.translationService.get('room-page.comments-deleted').subscribe(msg => {
this.notificationService.show(msg);
});
this.commentService.deleteCommentsByRoomId(this.roomId).subscribe();
}
});
.subscribe(result => {
if (result === 'delete') {
this.translationService.get('room-page.comments-deleted').subscribe(msg => {
this.notificationService.show(msg);
});
this.commentService.deleteCommentsByRoomId(this.roomId).subscribe();
}
});
});
nav('exportQuestions', () => {
const exp: Export = new Export(
......@@ -166,6 +173,7 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
}
ngOnDestroy() {
this.filter.save();
if (this.headerInterface) {
this.headerInterface.unsubscribe();
}
......@@ -175,8 +183,12 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
this.initNavigation();
this.roomId = localStorage.getItem(`roomId`);
const userId = this.user.id;
this.filter.updateUserId(userId);
this.userRole = this.user.role;
this.roomService.getRoom(this.roomId).subscribe(room => this.room = room);
this.roomService.getRoom(this.roomId).subscribe(room => {
this.room = room;
this.filter.updateRoom(room);
});
this.hideCommentsList = false;
this.wsCommentService.getModeratorCommentStream(this.roomId).subscribe((message: Message) => {
this.parseIncomingModeratorMessage(message);
......@@ -187,30 +199,23 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
this.translateService.use(localStorage.getItem('currentLang'));
this.deviceType = localStorage.getItem('deviceType');
this.isSafari = localStorage.getItem('isSafari');
this.currentSort = this.votedesc;
this.commentService.getRejectedComments(this.roomId)
.subscribe(comments => {
this.comments = comments;
this.setTimePeriod(this.period);
this.isLoading = false;
this.filter.sortType = SortType.votedesc;
this.moderationService.get(this.roomId)
.subscribe((mods) => {
this.filter.updateModerators(mods.map(mod => mod.accountId));
this.commentService.getRejectedComments(this.roomId)
.subscribe(comments => {
this.comments = comments;
this.setTimePeriod(this.filter.period);
this.isLoading = false;
});
});
this.translateService.get('comment-list.search').subscribe(msg => {
this.searchPlaceholder = msg;
});
}
private getCurrentFilter() {
const filter = new CommentFilter();
filter.filterSelected = this.currentFilter;
filter.periodSet = this.period;
if (filter.periodSet === Period.fromNow) {
filter.timeStampNow = new Date().getTime();
}
CommentFilter.currentFilter = filter;
}
checkScroll(): void {
const currentScroll = document.documentElement.scrollTop;
this.scroll = currentScroll >= 65;
......@@ -223,18 +228,14 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
searchComments(): void {
this.search = true;
if (this.searchInput && this.searchInput.length > 1) {
if (this.filter.currentSearch) {
this.hideCommentsList = true;
this.filteredComments = this.comments
.filter(c => this.checkIfIncludesKeyWord(c.body, this.searchInput)
|| (!!c.answer ? this.checkIfIncludesKeyWord(c.answer, this.searchInput) : false));
this.filteredComments = this.filter.filterCommentsBySearch(this.comments);
} else if (!this.filter.filterType) {
this.hideCommentsList = false;
}
}
checkIfIncludesKeyWord(body: string, keyword: string) {
return body.toLowerCase().includes(keyword.toLowerCase());
}
activateSearch() {
this.translateService.get('comment-list.search').subscribe(msg => {
this.searchPlaceholder = msg;
......@@ -243,6 +244,13 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
this.searchField.nativeElement.focus();
}
abortSearch() {
this.hideCommentsList = false;
this.filter.currentSearch = '';
this.search = false;
this.refreshFiltering();
}
getComments(): void {
this.isLoading = false;
let commentThreshold = -10;
......@@ -254,7 +262,7 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
this.comments = this.comments.filter(x => x.score >= commentThreshold);
}
}
this.setTimePeriod(this.period);
this.setTimePeriod(this.filter.period);
}
getVote(comment: Comment): Vote {
......@@ -278,7 +286,7 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
this.comments = this.comments.filter(function (el) {
return el.id !== payload.id;
});
this.setTimePeriod(this.period);
this.setTimePeriod(this.filter.period);
}
switch (key) {
case this.read:
......@@ -318,7 +326,7 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
}
break;
}
this.setTimePeriod(this.period);
this.setTimePeriod(this.filter.period);
if (this.hideCommentsList) {
this.searchComments();
}
......@@ -336,70 +344,49 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
c.timestamp = payload.timestamp;
c.creatorId = payload.creatorId;
c.keywordsFromQuestioner = payload.keywordsFromQuestioner ?
JSON.parse(payload.keywordsFromQuestioner as unknown as string) : null;
JSON.parse(payload.keywordsFromQuestioner as unknown as string) : null;
c.userNumber = this.commentService.hashCode(c.creatorId);
this.comments = this.comments.concat(c);
break;
}
this.setTimePeriod(this.period);
this.setTimePeriod(this.filter.period);
if (this.hideCommentsList) {
this.searchComments();
}
}
filterComments(type: string, compare?: any): void {
this.currentFilter = type;
if (type === '') {
this.filteredComments = this.commentsFilteredByTime;
refreshFiltering(): void {
this.commentsWrittenByUsers.clear();
for (const comment of this.comments) {
let set = this.commentsWrittenByUsers.get(comment.creatorId);
if (!set) {
set = new Set<string>();
this.commentsWrittenByUsers.set(comment.creatorId, set);
}
set.add(comment.id);
}
this.isLoading = false;
if (this.search) {
this.filteredComments = this.filter.filterCommentsBySearch(this.comments);
return;
}
this.filteredComments = this.commentsFilteredByTime.filter(c => {
switch (type) {
case this.correct:
return c.correct === CorrectWrong.CORRECT ? 1 : 0;
case this.wrong:
return c.correct === CorrectWrong.WRONG ? 1 : 0;
case this.favorite:
return c.favorite;
case this.bookmark:
return c.bookmark;
case this.read:
return c.read;
case this.unread:
return !c.read;
case this.userNumber:
return c.userNumber === compare;
}
});
this.hideCommentsList = true;
this.sortComments(this.currentSort);
}
clickedUserNumber(usrNumber: number): void {
this.filterComments(this.userNumber, usrNumber);
this.commentsFilteredByTime = this.filter.filterCommentsByTime(this.comments);
this.hideCommentsList = !!this.filter.filterType;
this.filteredComments = this.hideCommentsList ?
this.filter.filterCommentsByType(this.commentsFilteredByTime) : this.commentsFilteredByTime;
this.filter.sortCommentsBySortType(this.filteredComments);
}
sort(array: any[], type: string): void {
array.sort((a, b) => {
if (type === this.voteasc) {
return (a.score > b.score) ? 1 : (b.score > a.score) ? -1 : 0;
} else if (type === this.votedesc) {
return (b.score > a.score) ? 1 : (a.score > b.score) ? -1 : 0;
}
const dateA = new Date(a.timestamp), dateB = new Date(b.timestamp);
if (type === this.time) {
return (+dateB > +dateA) ? 1 : (+dateA > +dateB) ? -1 : 0;
}
});
applyFilterByKey(type: FilterTypeKey, compare?: any): void {
this.pageIndex = 0;
this.filter.filterType = FilterType[type];
this.filter.filterCompare = compare;
this.refreshFiltering();
}
sortComments(type: string): void {
if (this.hideCommentsList === true) {
this.sort(this.filteredComments, type);
} else {
this.sort(this.commentsFilteredByTime, type);
}
this.currentSort = type;
applySortingByKey(type: SortTypeKey) {
this.filter.sortType = SortType[type];
this.refreshFiltering();
}
switchToCommentList(): void {
......@@ -411,44 +398,12 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
}
this.router.navigate([`/${role}/room/${this.room.shortId}/comments`]);
}
setTimePeriod(period?: Period) {
if (period) {
this.period = period;
this.fromNow = null;
this.filter.period = period;
this.filter.fromNow = null;
}
const currentTime = new Date();
const hourInSeconds = 3600000;
let periodInSeconds;
if (this.period !== Period.all) {
switch (this.period) {
case Period.fromNow:
if (!this.fromNow) {
this.fromNow = new Date().getTime();
}
break;
case Period.oneHour:
periodInSeconds = hourInSeconds;
break;
case Period.threeHours:
periodInSeconds = hourInSeconds * 2;
break;
case Period.oneDay:
periodInSeconds = hourInSeconds * 24;
break;
case Period.oneWeek:
periodInSeconds = hourInSeconds * 168;
break;
case Period.twoWeeks:
periodInSeconds = hourInSeconds * 336;
break;
}
this.commentsFilteredByTime = this.comments
.filter(c => new Date(c.timestamp).getTime() >=
(this.period === Period.fromNow ? this.fromNow : (currentTime.getTime() - periodInSeconds)));
} else {
this.commentsFilteredByTime = this.comments;
}
this.filterComments(this.currentFilter);
this.refreshFiltering();
}
}
......@@ -7,8 +7,8 @@
<button id="filter-close-button"
mat-icon-button
class="searchBarButton"
*ngIf="currentFilter !== ''"
(click)="filterComments('');"
*ngIf="filter.filterType"
(click)="applyFilterByKey(null)"
aria-labelledby="close_filter">
<mat-icon class="searchBarIcon red">close</mat-icon>
</button>
......@@ -22,14 +22,14 @@
[ngClass]="{'desktop-input': deviceType === 'desktop',
'mobile-input': deviceType === 'mobile' && !search, 'mobile-input-2': deviceType === 'mobile' && search }"
(input)="searchComments()"
[(ngModel)]="searchInput"
[(ngModel)]="filter.currentSearch"
[placeholder]="searchPlaceholder"
aria-labelledby="search-box-input-description">
<button id="search_close-button"
mat-icon-button
class="searchBarButton close red"
*ngIf="searchInput !== '' || search"
(click)="hideCommentsList=false; searchInput = ''; search = false;"
*ngIf="filter.currentSearch || search"
(click)="abortSearch()"
aria-labelledby="close_search">
<mat-icon>close</mat-icon>
</button>
......@@ -49,7 +49,7 @@
<button mat-icon-button
class="searchBarButton"
(click)="activateSearch(); filterComments('')"
(click)="activateSearch(); applyFilterByKey(null)"
*ngIf="deviceType === 'mobile' && !search && comments && comments.length > 0">
<mat-icon class="searchBarIcon">search</mat-icon>
</button>
......@@ -81,7 +81,7 @@
aria-labelledby="time_settings"
*ngIf="!searchBox.value && comments && comments.length > 0 && !search"
[matMenuTriggerFor]="timeMenu"
[ngClass]="{'active-filter': period !== 'time-all'}"
[ngClass]="{'active-filter': filter.period !== 'time-all'}"
matTooltip="{{ 'comment-list.select-time' | translate }}">
<mat-icon class="searchBarIcon">history</mat-icon>
</button>
......@@ -91,7 +91,7 @@
aria-labelledby="pause"
class="freezeButton"
*ngIf="!searchBox.value && !search && !freeze && comments.length > 2"
(click)="pauseCommentStream()"
(click)="activateCommentStream(true)"
matTooltip="{{ 'comment-list.pause-comments' | translate }}">
<mat-icon class="freezeIcon">pause</mat-icon>
</button>
......@@ -101,7 +101,7 @@
aria-labelledby="play"
class="freezeButton"
*ngIf="!searchBox.value && !search && freeze"
(click)="playCommentStream()"
(click)="activateCommentStream(false)"
matTooltip="{{ 'comment-list.play-comments' | translate }}">
<mat-icon class="playIcon">play_arrow</mat-icon>
</button>
......@@ -114,7 +114,7 @@
<button mat-menu-item
(click)="setTimePeriod(periodItem)"
class="period"
[ngClass]="{'selected': periodItem === period}"
[ngClass]="{'selected': periodItem === filter.period}"
aria-labelledby="{{periodItem}}">
<span>{{ ('comment-list.select-' + periodItem) | translate }}</span>
</button>
......@@ -125,24 +125,24 @@
xPosition="before">
<button mat-menu-item
(click)="sortComments(time)"
(click)="applySortingByKey('time')"
aria-labelledby="access_time">
<mat-icon [ngClass]="{time: 'timesort'}[currentSort]">update</mat-icon>
<span [ngClass]="{time: 'timesort'}[currentSort]">{{ 'comment-list.sort-list-time' | translate }}</span>
<mat-icon [ngClass]="{time: 'timesort'}[filter.sortType]">update</mat-icon>
<span [ngClass]="{time: 'timesort'}[filter.sortType]">{{ 'comment-list.sort-list-time' | translate }}</span>
</button>
<button mat-menu-item
(click)="sortComments(votedesc)"
(click)="applySortingByKey('votedesc')"
aria-labelledby="keyboard_arrow_up">
<mat-icon [ngClass]="{votedesc: 'up'}[currentSort]">thumb_up</mat-icon>
<span [ngClass]="{votedesc: 'up'}[currentSort]">{{ 'comment-list.sort-vote-asc' | translate }}</span>
<mat-icon [ngClass]="{votedesc: 'up'}[filter.sortType]">thumb_up</mat-icon>
<span [ngClass]="{votedesc: 'up'}[filter.sortType]">{{ 'comment-list.sort-vote-asc' | translate }}</span>
</button>
<button mat-menu-item
(click)="sortComments(voteasc)"
(click)="applySortingByKey('voteasc')"
aria-labelledby="keyboard_arrow_down">
<mat-icon [ngClass]="{voteasc: 'down'}[currentSort]">thumb_down</mat-icon>
<span [ngClass]="{voteasc: 'down'}[currentSort]">{{ 'comment-list.sort-vote-desc' | translate }}</span>
<mat-icon [ngClass]="{voteasc: 'down'}[filter.sortType]">thumb_down</mat-icon>
<span [ngClass]="{voteasc: 'down'}[filter.sortType]">{{ 'comment-list.sort-vote-desc' | translate }}</span>
</button>
</mat-menu>
......@@ -152,53 +152,53 @@
<div>
<button mat-menu-item
(click)="filterComments(favorite)"
(click)="applyFilterByKey('favorite')"
aria-labelledby="grade">
<mat-icon class="star"
[ngClass]="{favorite: 'favorite-icon'}[currentFilter]">grade
[ngClass]="{favorite: 'favorite-icon'}[filter.filterType]">grade
</mat-icon>
<span
[ngClass]="{favorite: 'favorite-icon'}[currentFilter]">{{ 'comment-list.filter-favorite' | translate }}</span>
[ngClass]="{favorite: 'favorite-icon'}[filter.filterType]">{{ 'comment-list.filter-favorite' | translate }}</span>
</button>
<button mat-menu-item
(click)="filterComments(bookmark)"
(click)="applyFilterByKey('bookmark')"
aria-labelledby="bookmark">
<mat-icon class="bookmark"
[ngClass]="{bookmark: 'bookmark-icon'}[currentFilter]">bookmark
[ngClass]="{bookmark: 'bookmark-icon'}[filter.filterType]">bookmark
</mat-icon>
<span
[ngClass]="{bookmark: 'bookmark-icon'}[currentFilter]">{{ 'comment-list.filter-bookmark' | translate }}</span>
[ngClass]="{bookmark: 'bookmark-icon'}[filter.filterType]">{{ 'comment-list.filter-bookmark' | translate }}</span>
</button>
<button mat-menu-item
(focus)="hideCommentsList=true"
(click)="filterComments(answer)"
(click)="applyFilterByKey('answer')"
aria-labelledby="comment">
<mat-icon class="answer"
[ngClass]="{answer: 'answered-icon'}[currentFilter]">comment
[ngClass]="{answer: 'answered-icon'}[filter.filterType]">comment
</mat-icon>
<span
[ngClass]="{answer: 'answered-icon'}[currentFilter]">{{ 'comment-list.filter-answered' | translate }}</span>
[ngClass]="{answer: 'answered-icon'}[filter.filterType]">{{ 'comment-list.filter-answered' | translate }}</span>
</button>
<button mat-menu-item
(focus)="hideCommentsList=true"
(click)="filterComments(unanswered)"
(click)="applyFilterByKey('unanswered')"
aria-labelledby="comment">
<mat-icon class="unanswered"
[ngClass]="{unanswered: 'unanswered-icon'}[currentFilter]">comment
[ngClass]="{unanswered: 'unanswered-icon'}[filter.filterType]">comment
</mat-icon>
<span
[ngClass]="{unanswered: 'unanswered-icon'}[currentFilter]">{{ 'comment-list.filter-unanswered' | translate }}</span>
[ngClass]="{unanswered: 'unanswered-icon'}[filter.filterType]">{{ 'comment-list.filter-unanswered' | translate }}</span>
</button>
<button mat-menu-item
(focus)="hideCommentsList=true"
(click)="filterComments(owner)"
(click)="applyFilterByKey('owner')"
aria-labelledby="comment">
<mat-icon [ngClass]="{owner: 'owner-icon'}[currentFilter]">person_pin_circle</mat-icon>
<span [ngClass]="{owner: 'owner-icon'}[currentFilter]">{{ 'comment-list.filter-owner' | translate }}</span>
<mat-icon [ngClass]="{owner: 'owner-icon'}[filter.filterType]">person_pin_circle</mat-icon>
<span [ngClass]="{owner: 'owner-icon'}[filter.filterType]">{{ 'comment-list.filter-owner' | translate }}</span>
</button>
<!--
......@@ -227,7 +227,7 @@
<button mat-menu-item
(focus)="hideCommentsList=false"
(click)="sortComments(currentSort); filterComments('')"
(click)="applyFilterByKey(null)"
aria-labelledby="close">
<mat-icon>close</mat-icon>
<span>{{ 'comment-list.filter-reset' | translate }}</span>
......@@ -275,9 +275,9 @@
[user]="user"
[disabled]="!commentsEnabled"
[commentsWrittenByUser]="commentsWrittenByUsers.get(current.creatorId).size"
(clickedOnTag)="clickedOnTag($event)"
(clickedUserNumber)="clickedUserNumber($event)"
(clickedOnKeyword)="clickedOnKeyword($event)"
(clickedOnTag)="applyFilterByKey('tag', $event)"
(clickedUserNumber)="applyFilterByKey('userNumber', $event)"
(clickedOnKeyword)="applyFilterByKey('keyword', $event)"
(votedComment)="votedComment($event)">
</app-comment>
<ars-mat-paginator
......@@ -297,7 +297,7 @@
<!-- No Questions Present -->
<div
*ngIf="comments && (commentsFilteredByTime.length < 1 && period === 'time-all' || comments.length === 0) && !isLoading"
*ngIf="comments && (commentsFilteredByTime.length < 1 && filter.period === 'time-all' || comments.length === 0) && !isLoading"
fxLayout="row"
fxLayoutAlign="center center"
class="no-comments">
......@@ -305,7 +305,7 @@
</div>
<div *ngIf="(filteredComments && filteredComments.length === 0 && hideCommentsList)
|| (comments && commentsFilteredByTime.length === 0 && period !== 'time-all') && !isLoading && comments.length > 0"
|| (comments && commentsFilteredByTime.length === 0 && filter.period !== 'time-all') && !isLoading && comments.length > 0"
fxLayout="row"
fxLayoutAlign="center center"
class="no-comments">
......
import { Period } from '../../../utils/filter-options';
import { Comment } from '../../../models/comment';
import { CorrectWrong } from '../../../models/correct-wrong.enum';
import { Room } from '../../../models/room';
export enum FilterType {
voteasc = 'voteasc',
votedesc = 'votedesc',
time = 'time',
read = 'read',
unread = 'unread',
favorite = 'favorite',
correct = 'correct',
wrong = 'wrong',
ack = 'ack',
bookmark = 'bookmark',
moderator = 'moderator',
lecturer = 'lecturer',
tag = 'tag',
userNumber = 'userNumber',
keyword = 'keyword',
answer = 'answer',
unanswered = 'unanswered',
owner = 'owner',
}
export type FilterTypeKey = keyof typeof FilterType;
export enum SortType {
voteasc = 'voteasc',
votedesc = 'votedesc',
time = 'time'
}
export type SortTypeKey = keyof typeof SortType;
const DEFAULT_PERIOD = Period.all;
const DEFAULT_SORT = SortType.time;
export class CommentListFilter {
//own properties
period: Period;
fromNow: number;
filterType: FilterType;
filterCompare: any;
sortType: SortType;
currentSearch: string;
//dependencies to other values
private userId: string;
private moderatorIds: Set<string>;
private threshold: number;
private ownerId: string;
private lastRoomId: string;
constructor(obj) {
if (!obj) {
this.resetToDefault();
return;
}
this.period = obj.period;
this.fromNow = obj.fromNow;
this.filterType = obj.filterType;
this.filterCompare = obj.filterCompare;
this.sortType = obj.sortType;
this.currentSearch = obj.currentSearch;
this.lastRoomId = obj.lastRoomId;
}
static loadCurrentFilter(): CommentListFilter {
return new CommentListFilter(JSON.parse(localStorage.getItem('currentFilter')));
}
resetToDefault() {
this.period = DEFAULT_PERIOD;
this.fromNow = null;
this.filterType = null;
this.filterCompare = null;
this.sortType = DEFAULT_SORT;
this.currentSearch = '';
}
updateRoom(room: Room) {
if (room.id !== this.lastRoomId) {
this.resetToDefault();
}
this.ownerId = room.ownerId;
this.lastRoomId = room.id;
this.threshold = room.threshold;
}
updateUserId(userId: string) {
this.userId = userId;
}
updateModerators(moderators: string[]) {
this.moderatorIds = new Set<string>([...moderators]);
}
save() {
const ownerId = this.ownerId;
const threshold = this.threshold;
const userId = this.userId;
const moderatorIds = this.moderatorIds;
this.ownerId = this.threshold = this.userId = this.moderatorIds = undefined;
localStorage.setItem('currentFilter', JSON.stringify(this));
this.ownerId = ownerId;
this.threshold = threshold;
this.userId = userId;
this.moderatorIds = moderatorIds;
}
get thresholdEnabled() {
return !!this.threshold;
}
filterCommentsBySearch(comments: Comment[]): Comment[] {
const search = this.currentSearch.toLowerCase();
return comments
.filter(c =>
c.body.toLowerCase().includes(search) ||
c.answer?.toLowerCase().includes(search) ||
c.keywordsFromSpacy?.some(e => e.text.toLowerCase().includes(search)) ||
c.keywordsFromQuestioner?.some(e => e.text.toLowerCase().includes(search)) ||
c.questionerName?.toLowerCase().includes(search)
);
}
filterCommentsByTime(comments: Comment[], moderation = false): Comment[] {
const thresholdComments =
this.thresholdEnabled && !moderation ? comments.filter(comment => comment.score >= this.threshold) : comments;
if (this.period === null || this.period === undefined) {
this.period = DEFAULT_PERIOD;
}
if (this.period === Period.all) {
return thresholdComments;
}
const currentTime = new Date().getTime();
let periodInSeconds;
const hourInSeconds = 3_600_000;
switch (this.period) {
case Period.fromNow:
if (!this.fromNow) {
this.fromNow = currentTime;
}
break;
case Period.oneHour:
periodInSeconds = hourInSeconds;
break;
case Period.threeHours:
periodInSeconds = hourInSeconds * 3;
break;
case Period.oneDay:
periodInSeconds = hourInSeconds * 24;
break;
case Period.oneWeek:
periodInSeconds = hourInSeconds * 168;
break;
case Period.twoWeeks:
periodInSeconds = hourInSeconds * 336;
break;
default:
throw new Error('Time period is invalid.');
}
const filterTime = (this.period === Period.fromNow ? this.fromNow : currentTime - periodInSeconds);
return thresholdComments.filter(c => new Date(c.timestamp).getTime() >= filterTime);
}
filterCommentsByType(comment: Comment[]): Comment[] {
let filterFunc: (c: Comment) => boolean;
switch (this.filterType) {
case FilterType.correct:
filterFunc = (c) => c.correct === CorrectWrong.CORRECT;
break;
case FilterType.wrong:
filterFunc = (c) => c.correct === CorrectWrong.WRONG;
break;
case FilterType.favorite:
filterFunc = (c) => c.favorite;
break;
case FilterType.bookmark:
filterFunc = (c) => c.bookmark;
break;
case FilterType.read:
filterFunc = (c) => c.read;
break;
case FilterType.unread:
filterFunc = (c) => !c.read;
break;
case FilterType.tag:
filterFunc = (c) => c.tag === this.filterCompare;
break;
case FilterType.userNumber:
filterFunc = (c) => c.userNumber === this.filterCompare;
break;
case FilterType.keyword:
filterFunc = (c) => !!(c.keywordsFromQuestioner?.find(k => k.text === this.filterCompare) ||
c.keywordsFromSpacy?.find(k => k.text === this.filterCompare));
break;
case FilterType.answer:
filterFunc = (c) => !!c.answer;
break;
case FilterType.unanswered:
filterFunc = (c) => !c.answer;
break;
case FilterType.owner:
filterFunc = (c) => c.creatorId === this.userId;
break;
case FilterType.moderator:
filterFunc = (c) => this.moderatorIds.has(c.creatorId);
break;
case FilterType.lecturer:
filterFunc = (c) => c.creatorId === this.ownerId;
break;
default:
return comment;
}
return comment.filter(filterFunc);
}
sortCommentsBySortType(comments: Comment[]): Comment[] {
let sortFunc: (a: Comment, b: Comment) => number;
switch (this.sortType) {
case SortType.voteasc:
sortFunc = (a, b) => a.score - b.score;
break;
case SortType.votedesc:
sortFunc = (a, b) => b.score - a.score;
break;
case SortType.time:
sortFunc = (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
break;
}
if (sortFunc) {
comments.sort(sortFunc);
}
comments.sort((a, b) => this.getCommentRoleValue(b) - this.getCommentRoleValue(a));
return comments;
}
private getCommentRoleValue(comment: Comment): number {
if (comment.creatorId === this.ownerId) {
return 2;
} else if (this.moderatorIds.has(comment.creatorId)) {
return 1;
}
return 0;
}
}
......@@ -13,7 +13,7 @@ import { SpacyKeyword } from '../http/spacy.service';
export interface UpdateInformation {
type: 'CommentCreated' | 'CommentPatched' | 'CommentHighlighted' | 'CommentDeleted';
subtype?: string;
subtype?: (keyof Comment);
comment: Comment;
finished?: boolean;
updates?: (keyof Comment)[];
......@@ -100,7 +100,7 @@ export class RoomDataService {
private _currentSubscriptions: RoomDataUpdateSubscription[] = [];
private _currentComments: Comment[] = null;
private _commentUpdates: BehaviorSubject<Comment[]> = new BehaviorSubject<Comment[]>(null);
private _currentRoomComments: BehaviorSubject<Comment[]> = new BehaviorSubject<Comment[]>(null);
private _fastCommentAccess: FastRoomAccessObject = null;
private _wsCommentServiceSubscription: Subscription = null;
private _currentRoomId: string = null;
......@@ -140,10 +140,10 @@ export class RoomDataService {
getRoomData(roomId: string, freezed: boolean = false): Observable<Comment[]> {
const tempSubject = new BehaviorSubject<Comment[]>(null);
if (this._currentRoomId !== roomId) {
this._commentUpdates.next(null);
this._currentRoomComments.next(null);
}
let subscription: Subscription = null;
subscription = this._commentUpdates.subscribe(comments => {
subscription = this._currentRoomComments.subscribe(comments => {
if (comments === null) {
return;
}
......@@ -287,7 +287,7 @@ export class RoomDataService {
private triggerUpdate(type: UpdateType, additionalInformation: UpdateInformation) {
if (type === UpdateType.force) {
this._commentUpdates.next(this._currentComments);
this._currentRoomComments.next(this._currentComments);
} else if (type === UpdateType.commentStream) {
for (const subscription of this._currentSubscriptions) {
subscription.onUpdate(additionalInformation);
......
......@@ -91,7 +91,6 @@ export class CreateCommentKeywords {
const wordCount = text.trim().split(' ').length;
const hasConfidence = selectedLanguage === 'auto' ? result.language.detectedLanguage.confidence >= 0.5 : true;
const errorQuotient = (result.matches.length * 100) / wordCount;
console.log(errorQuotient);
if (!hasConfidence ||
errorQuotient > ERROR_QUOTIENT_USE_DEEPL ||
(!useDeepl && errorQuotient > ERROR_QUOTIENT_WELL_SPELLED)) {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment