From 18984746f11a4bfe01642c585373d60be4172dcf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lukas=20Mau=C3=9F?= <lukas.mauss@gmx.de>
Date: Wed, 3 Feb 2021 17:34:27 +0100
Subject: [PATCH] Add filter by time period to comment list

A new button is now displayed in comment list view for creators and participants. A click on the button will open
a menu with which the user can select a time period for the displayed comments. Sorting and filtering actions will
applied to the selected period. Displaying all existing comments is the default setting.
---
 .../comment-list/comment-list.component.html  | 34 +++++++--
 .../comment-list/comment-list.component.scss  |  4 ++
 .../comment-list/comment-list.component.ts    | 70 +++++++++++++++----
 3 files changed, 90 insertions(+), 18 deletions(-)

diff --git a/src/app/components/shared/comment-list/comment-list.component.html b/src/app/components/shared/comment-list/comment-list.component.html
index 113b01146..f0696d1f3 100644
--- a/src/app/components/shared/comment-list/comment-list.component.html
+++ b/src/app/components/shared/comment-list/comment-list.component.html
@@ -36,9 +36,9 @@
 
   <div class="button-bar"
        fxLayoutAlign="center center">
-    <div *ngIf="comments && comments.length > 0">
+    <div *ngIf="comments && commentsFilteredByTime.length > 0">
       <h3 class="counter"
-          *ngIf="!hideCommentsList">{{comments.length}}</h3>
+          *ngIf="!hideCommentsList">{{commentsFilteredByTime.length}}</h3>
       <h3 class="counter-filtered"
           *ngIf="filteredComments && hideCommentsList">{{filteredComments.length}}</h3>
     </div>
@@ -70,6 +70,16 @@
       <mat-icon class="searchBarIcon">filter_list</mat-icon>
     </button>
 
+    <button id="time-button"
+            mat-icon-button
+            class="searchBarButton"
+            aria-labelledby="time_settings"
+            *ngIf="!searchBox.value && comments && comments.length > 0 && !search"
+            [matMenuTriggerFor]="timeMenu"
+            matTooltip="{{ 'comment-list.select-time' | translate }}">
+      <mat-icon class="searchBarIcon">access_time</mat-icon>
+    </button>
+
     <button id="pause-comments"
             mat-fab
             aria-labelledby="pause"
@@ -92,6 +102,16 @@
 
   </div>
 
+  <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}"
+              aria-labelledby="{{periodItem}}">
+        <span>{{ ('comment-list.select-' + periodItem) | translate }}</span>
+      </button>
+    </div>
+  </mat-menu>
+
   <mat-menu #sortMenu="matMenu"
             xPosition="before">
 
@@ -185,7 +205,7 @@
 </button>
 
 <div *ngIf="!isLoading">
-  <app-comment *ngFor="let current of hideCommentsList ? filteredComments : comments"
+  <app-comment *ngFor="let current of hideCommentsList ? filteredComments : commentsFilteredByTime"
                [comment]="current"
                [parseVote]="getVote(current)"
                [userRole]="userRole"
@@ -197,7 +217,7 @@
 
 </div>
 <!-- No Questions Present -->
-<div *ngIf="comments && comments.length < 1 && !isLoading"
+<div *ngIf="comments && commentsFilteredByTime.length < 1 && !isLoading"
      fxLayout="row"
      fxLayoutAlign="center center"
      class="no-comments">
@@ -224,4 +244,10 @@
   <div id="play">{{'comment-list.a11y-play' | translate}}</div>
   <div id="close_search">{{'comment-list.a11y-close_search' | translate}}</div>
   <div id="new-comment">{{ 'comment-page.new-comment' | translate:{comment: newestComment} }}</div>
+  <div id="select-time">{{ 'comment-list.a11y-select-time' | translate }}</div>
+  <div id="select-time-1h">{{ 'comment-list.a11y-select-time-1h' | translate }}</div>
+  <div id="select-time-3h">{{ 'comment-list.a11y-select-time-3h' | translate }}</div>
+  <div id="select-time-1d">{{ 'comment-list.a11y-select-time-1d' | translate }}</div>
+  <div id="select-time-1w">{{ 'comment-list.a11y-select-time-1w' | translate }}</div>
+  <div id="select-time-all">{{ 'comment-list.a11y-select-time-all' | translate }}</div>
 </div>
diff --git a/src/app/components/shared/comment-list/comment-list.component.scss b/src/app/components/shared/comment-list/comment-list.component.scss
index 8e4057570..0e68b8c23 100644
--- a/src/app/components/shared/comment-list/comment-list.component.scss
+++ b/src/app/components/shared/comment-list/comment-list.component.scss
@@ -202,3 +202,7 @@ h3 {
 .visible {
   transform: scale(1.4);
 }
+
+.selected {
+  font-weight: bold;
+}
diff --git a/src/app/components/shared/comment-list/comment-list.component.ts b/src/app/components/shared/comment-list/comment-list.component.ts
index 0dd540b55..1e96bd179 100644
--- a/src/app/components/shared/comment-list/comment-list.component.ts
+++ b/src/app/components/shared/comment-list/comment-list.component.ts
@@ -24,11 +24,20 @@ import { AuthenticationService } from '../../../services/http/authentication.ser
 import { Title } from '@angular/platform-browser';
 import { TitleService } from '../../../services/util/title.service';
 
+export enum Period {
+  ONEHOUR = 'time-1h',
+  THREEHOURS = 'time-3h',
+  ONEDAY = 'time-1d',
+  ONEWEEK = 'time-1w',
+  ALL = 'time-all'
+}
+
 @Component({
   selector: 'app-comment-list',
   templateUrl: './comment-list.component.html',
   styleUrls: ['./comment-list.component.scss'],
 })
+
 export class CommentListComponent implements OnInit, OnDestroy {
   @ViewChild('searchBox') searchField: ElementRef;
   @Input() user: User;
@@ -36,6 +45,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
   shortId: string;
   AppComponent = AppComponent;
   comments: Comment[] = [];
+  commentsFilteredByTime: Comment[] = [];
   room: Room;
   hideCommentsList = false;
   filteredComments: Comment[];
@@ -70,6 +80,8 @@ export class CommentListComponent implements OnInit, OnDestroy {
   newestComment: string;
   freeze = false;
   commentStream: Subscription;
+  periodsList = Object.values(Period);
+  period: Period = Period.ALL;
 
   constructor(
     private commentService: CommentService,
@@ -162,7 +174,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
     if (this.searchInput) {
       if (this.searchInput.length > 1) {
         this.hideCommentsList = true;
-        this.filteredComments = this.comments.filter(c => c.body.toLowerCase().includes(this.searchInput.toLowerCase()));
+        this.filteredComments = this.commentsFilteredByTime.filter(c => c.body.toLowerCase().includes(this.searchInput.toLowerCase()));
       }
     } else if (this.searchInput.length === 0 && this.currentFilter === '') {
       this.hideCommentsList = false;
@@ -193,7 +205,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
         this.setComments(this.comments.filter(x => x.score >= commentThreshold));
       }
     }
-    this.sortComments(this.currentSort);
+    this.setTimePeriod(this.period);
   }
 
   getVote(comment: Comment): Vote {
@@ -220,8 +232,8 @@ export class CommentListComponent implements OnInit, OnDestroy {
         });
 
         this.announceNewComment(c.body);
-
-        this.setComments(this.comments.concat(c));
+        this.comments = this.comments.concat(c);
+        this.setComments(this.comments);
         break;
       case 'CommentPatched':
         // ToDo: Use a map for comments w/ key = commentId
@@ -250,9 +262,10 @@ export class CommentListComponent implements OnInit, OnDestroy {
                 case this.ack:
                   const isNowAck = <boolean>value;
                   if (!isNowAck) {
-                    this.setComments(this.comments.filter(function (el) {
+                    this.comments = this.comments.filter(function (el) {
                       return el.id !== payload.id;
-                    }));
+                    });
+                    this.setTimePeriod(this.period);
                   }
                   break;
                 case this.tag:
@@ -282,9 +295,10 @@ export class CommentListComponent implements OnInit, OnDestroy {
         }
         break;
     }
-    this.filterComments(this.currentFilter);
-    this.sortComments(this.currentSort);
-    this.searchComments();
+    this.setTimePeriod(this.period);
+    if (this.hideCommentsList) {
+      this.searchComments();
+    }
   }
 
   openCreateDialog(): void {
@@ -339,12 +353,12 @@ export class CommentListComponent implements OnInit, OnDestroy {
   filterComments(type: string, compare?: any): void {
     this.currentFilter = type;
     if (type === '') {
-      this.filteredComments = this.comments;
+      this.filteredComments = this.commentsFilteredByTime;
       this.hideCommentsList = false;
       this.currentFilter = '';
       return;
     }
-    this.filteredComments = this.comments.filter(c => {
+    this.filteredComments = this.commentsFilteredByTime.filter(c => {
       switch (type) {
         case this.correct:
           return c.correct === CorrectWrong.CORRECT ? 1 : 0;
@@ -387,7 +401,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
     if (this.hideCommentsList === true) {
       this.filteredComments = this.sort(this.filteredComments, type);
     } else {
-      this.setComments(this.sort(this.comments, type));
+      this.setComments(this.sort(this.commentsFilteredByTime, type));
     }
     this.currentSort = type;
   }
@@ -432,8 +446,8 @@ export class CommentListComponent implements OnInit, OnDestroy {
   }
 
   setComments(comments: Comment[]) {
-    this.titleService.attachTitle('(' + comments.length + ')');
-    this.comments = comments;
+    this.commentsFilteredByTime = comments;
+    this.titleService.attachTitle('(' + this.commentsFilteredByTime.length + ')');
   }
 
   /**
@@ -454,4 +468,32 @@ export class CommentListComponent implements OnInit, OnDestroy {
       this.liveAnnouncer.announce(newCommentText).catch(err => { /* TODO error handling */ });
     }, 450);
   }
+
+  setTimePeriod(period: Period) {
+    this.period = period;
+    const currentTime = new Date();
+    const hourInSeconds = 3600000;
+    let periodInSeconds;
+    if (period !== Period.ALL) {
+      switch (period) {
+        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;
+      }
+      this.commentsFilteredByTime = this.comments
+        .filter(c => new Date(c.timestamp).getTime() >= (currentTime.getTime() - periodInSeconds));
+    } else {
+      this.commentsFilteredByTime = this.comments;
+    }
+    this.filterComments(this.currentFilter);
+    this.titleService.attachTitle('(' + this.commentsFilteredByTime.length + ')');
+  }
 }
-- 
GitLab