From 9d8af1f6da79604f23bfc7e16f493f44638cec0e Mon Sep 17 00:00:00 2001
From: Lukas Phillip Haase <lukas.haase@mni.thm.de>
Date: Fri, 18 Oct 2019 15:17:53 +0200
Subject: [PATCH] Resolve "Scrolling-to-top FAB missing on moderation board"

---
 src/app/app.component.scss                    |   2 +-
 .../moderator-comment-list.component.html     |   2 +-
 .../moderator-comment-list.component.scss     |  13 +-
 .../moderator-comment-list.component.ts       |  93 +++++++-
 .../moderator-comment-list.component.ts.orig  | 200 ++++++++++++++++++
 .../comment-list/comment-list.component.html  |   2 +-
 .../comment-list/comment-list.component.ts    |   5 +-
 src/styles.scss                               |   8 +-
 8 files changed, 300 insertions(+), 25 deletions(-)
 create mode 100644 src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts.orig

diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index 0825e8bbc..6dd0d9b86 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -56,7 +56,7 @@ main {
   top:15px;
   z-index:2;
   display:none;
-  box-shadow:0px 2px 4px rgba(0,0,0,0.5);
+  box-shadow:0px 2px 2px rgba(0,0,0,0.4);
   transition:all 0.2s ease-in-out;
 }
 
diff --git a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.html b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.html
index f9f0a7365..abb4ad252 100644
--- a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.html
+++ b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.html
@@ -58,7 +58,7 @@
 
 </div>
 
-<button mat-icon-button class="scrollTop" [ngClass]="{'visible': scrollExtended}" (click)="scrollToTop()" tabIndex="-1">
+<button mat-icon-button class="scrollTop" [ngClass]="{'visible': isScrollButtonVisible()}" (click)="AppComponent.scrollTop()" tabIndex="-1">
   <mat-icon>arrow_upward</mat-icon>
 </button>
 
diff --git a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.scss b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.scss
index a418df968..fcf910f16 100644
--- a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.scss
+++ b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.scss
@@ -116,16 +116,15 @@ h3 {
 
 .scrollTop {
   position: fixed;
-  bottom: 3%;
-  left: -40px;
+  left: 25px;
+  bottom: 65px;
+  transform: scale(0);
   z-index: 1;
   background-color: var(--primary);
-  color: white;
-  transition: 0.5s;
+  color: var(--on-primary);
+  transition: 0.1s;
 }
 
 .visible {
-  left: 5%;
-  transition: 0.5s;
-  background-color: var(--primary);
+  transform: scale(1);
 }
diff --git a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts
index f5f2e0811..308e9c787 100644
--- a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts
+++ b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts
@@ -11,8 +11,11 @@ import { Vote } from '../../../models/vote';
 import { UserRole } from '../../../models/user-roles.enum';
 import { Room } from '../../../models/room';
 import { RoomService } from '../../../services/http/room.service';
+import { VoteService } from '../../../services/http/vote.service';
+import { CorrectWrong } from '../../../models/correct-wrong.enum';
 import { EventService } from '../../../services/util/event.service';
 import { Router } from '@angular/router';
+import { AppComponent } from '../../../app.component';
 
 @Component({
   selector: 'app-moderator-comment-list',
@@ -23,6 +26,7 @@ export class ModeratorCommentListComponent implements OnInit {
   @ViewChild('searchBox') searchField: ElementRef;
   @Input() user: User;
   @Input() roomId: string;
+  AppComponent = AppComponent;
   comments: Comment[] = [];
   room: Room;
   hideCommentsList = false;
@@ -34,7 +38,13 @@ export class ModeratorCommentListComponent implements OnInit {
   votedesc = 'votedesc';
   time = 'time';
   currentSort: string;
+  read = 'read';
+  unread = 'unread';
+  favorite = 'favorite';
+  correct = 'correct';
+  wrong = 'wrong';
   ack = 'ack';
+  currentFilter = '';
   commentVoteMap = new Map<string, Vote>();
   scroll = false;
   scrollExtended = false;
@@ -57,6 +67,7 @@ export class ModeratorCommentListComponent implements OnInit {
 
   ngOnInit() {
     this.roomId = localStorage.getItem(`roomId`);
+    const userId = this.user.id;
     this.userRole = this.user.role;
     this.roomService.getRoom(this.roomId).subscribe(room => this.room = room);
     this.hideCommentsList = false;
@@ -86,8 +97,8 @@ export class ModeratorCommentListComponent implements OnInit {
     this.scrollExtended = currentScroll >= 300;
   }
 
-  scrollToTop(): void {
-    document.documentElement.scrollTo({ top: 0, behavior: 'smooth' });
+  isScrollButtonVisible(): boolean {
+    return !AppComponent.isScrolledTop() && this.comments.length > 5;
   }
 
   searchComments(): void {
@@ -105,6 +116,20 @@ export class ModeratorCommentListComponent implements OnInit {
     this.searchField.nativeElement.focus();
   }
 
+  getComments(): void {
+    this.isLoading = false;
+    let commentThreshold = -10;
+    if (this.room && this.room.extensions && this.room.extensions['comments']) {
+      commentThreshold = this.room.extensions['comments'].commentThreshold;
+      if (this.hideCommentsList) {
+        this.filteredComments = this.filteredComments.filter(x => x.score >= commentThreshold);
+      } else {
+        this.comments = this.comments.filter(x => x.score >= commentThreshold);
+      }
+    }
+    this.sortComments(this.currentSort);
+  }
+
   getVote(comment: Comment): Vote {
     if (this.userRole === 0) {
       return this.commentVoteMap.get(comment.id);
@@ -127,6 +152,28 @@ export class ModeratorCommentListComponent implements OnInit {
                     return el.id !== payload.id;
                   });
                 }
+                switch (key) {
+                  case this.read:
+                    this.comments[i].read = <boolean>value;
+                    break;
+                  case this.correct:
+                    this.comments[i].correct = <CorrectWrong>value;
+                    break;
+                  case this.favorite:
+                    this.comments[i].favorite = <boolean>value;
+                    break;
+                  case 'score':
+                    this.comments[i].score = <number>value;
+                    break;
+                  case this.ack:
+                    // tslint:disable-next-line:no-shadowed-variable
+                    const isNowAck = <boolean>value;
+                    if (isNowAck) {
+                      this.comments = this.comments.filter(function (el) {
+                        return el.id !== payload.id;
+                      });
+                    }
+                }
               }
             }
           }
@@ -141,6 +188,7 @@ export class ModeratorCommentListComponent implements OnInit {
         break;
     }
 
+    this.filterComments(this.currentFilter);
     this.sortComments(this.currentSort);
     this.searchComments();
   }
@@ -148,18 +196,45 @@ export class ModeratorCommentListComponent implements OnInit {
   parseIncomingModeratorMessage(message: Message) {
     const msg = JSON.parse(message.body);
     const payload = msg.payload;
-    if (msg.type === 'CommentCreated') {
-      const c = new Comment();
-      c.roomId = this.roomId;
-      c.body = payload.body;
-      c.id = payload.id;
-      c.timestamp = payload.timestamp;
-      this.comments = this.comments.concat(c);
+    switch (msg.type) {
+      case 'CommentCreated':
+        const c = new Comment();
+        c.roomId = this.roomId;
+        c.body = payload.body;
+        c.id = payload.id;
+        c.timestamp = payload.timestamp;
+        this.comments = this.comments.concat(c);
+        break;
     }
+    console.log(msg);
+    this.filterComments(this.currentFilter);
     this.sortComments(this.currentSort);
     this.searchComments();
   }
 
+  filterComments(type: string): void {
+    this.currentFilter = type;
+    if (type === '') {
+      this.filteredComments = this.comments;
+      return;
+    }
+    this.filteredComments = this.comments.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.read:
+          return c.read;
+        case this.unread:
+          return !c.read;
+      }
+    });
+    this.sortComments(this.currentSort);
+  }
+
   sort(array: any[], type: string): void {
     array.sort((a, b) => {
       if (type === this.voteasc) {
diff --git a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts.orig b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts.orig
new file mode 100644
index 000000000..608a95ef8
--- /dev/null
+++ b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts.orig
@@ -0,0 +1,200 @@
+import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { Comment } from '../../../models/comment';
+import { CommentService } from '../../../services/http/comment.service';
+import { TranslateService } from '@ngx-translate/core';
+import { LanguageService } from '../../../services/util/language.service';
+import { Message } from '@stomp/stompjs';
+import { MatDialog } from '@angular/material';
+import { WsCommentServiceService } from '../../../services/websockets/ws-comment-service.service';
+import { User } from '../../../models/user';
+import { Vote } from '../../../models/vote';
+import { UserRole } from '../../../models/user-roles.enum';
+import { Room } from '../../../models/room';
+import { RoomService } from '../../../services/http/room.service';
+import { EventService } from '../../../services/util/event.service';
+<<<<<<< HEAD
+import { Router } from '@angular/router';
+=======
+import { AppComponent } from '../../../app.component';
+>>>>>>> 231fd12c... Scroll-To-Top Button for Moderator-Comment-List, Fix for Firefox Body-Scroll-Bug
+
+@Component({
+  selector: 'app-moderator-comment-list',
+  templateUrl: './moderator-comment-list.component.html',
+  styleUrls: ['./moderator-comment-list.component.scss']
+})
+export class ModeratorCommentListComponent implements OnInit {
+  @ViewChild('searchBox') searchField: ElementRef;
+  @Input() user: User;
+  @Input() roomId: string;
+  AppComponent = AppComponent;
+  comments: Comment[] = [];
+  room: Room;
+  hideCommentsList = false;
+  filteredComments: Comment[];
+  userRole: UserRole;
+  deviceType: string;
+  isLoading = true;
+  voteasc = 'voteasc';
+  votedesc = 'votedesc';
+  time = 'time';
+  currentSort: string;
+  ack = 'ack';
+  commentVoteMap = new Map<string, Vote>();
+  scroll = false;
+  scrollExtended = false;
+  searchInput = '';
+  search = false;
+  searchPlaceholder = '';
+
+  constructor(
+    private commentService: CommentService,
+    private translateService: TranslateService,
+    public dialog: MatDialog,
+    protected langService: LanguageService,
+    private wsCommentService: WsCommentServiceService,
+    protected roomService: RoomService,
+    public eventService: EventService,
+    private router: Router
+  ) {
+    langService.langEmitter.subscribe(lang => translateService.use(lang));
+  }
+
+  ngOnInit() {
+    this.roomId = localStorage.getItem(`roomId`);
+    this.userRole = this.user.role;
+    this.roomService.getRoom(this.roomId).subscribe(room => this.room = room);
+    this.hideCommentsList = false;
+    this.wsCommentService.getModeratorCommentStream(this.roomId).subscribe((message: Message) => {
+      this.parseIncomingModeratorMessage(message);
+    });
+    this.wsCommentService.getCommentStream(this.roomId).subscribe((message: Message) => {
+      this.parseIncomingMessage(message);
+    });
+    this.translateService.use(localStorage.getItem('currentLang'));
+    this.deviceType = localStorage.getItem('deviceType');
+    this.currentSort = this.votedesc;
+    this.commentService.getRejectedComments(this.roomId)
+      .subscribe(comments => {
+        this.comments = comments;
+        this.isLoading = false;
+        this.sortComments(this.currentSort);
+      });
+    this.translateService.get('comment-list.search').subscribe(msg => {
+      this.searchPlaceholder = msg;
+    });
+  }
+
+  checkScroll(): void {
+    const currentScroll = document.documentElement.scrollTop;
+    this.scroll = currentScroll >= 65;
+    this.scrollExtended = currentScroll >= 300;
+  }
+
+  isScrollButtonVisible(): boolean {
+    return !AppComponent.isScrolledTop() && this.comments.length > 5;
+  }
+
+  searchComments(): void {
+    if (this.searchInput && this.searchInput.length > 2) {
+      this.hideCommentsList = true;
+      this.filteredComments = this.comments.filter(c => c.body.toLowerCase().includes(this.searchInput.toLowerCase()));
+    }
+  }
+
+  activateSearch() {
+    this.translateService.get('comment-list.search').subscribe(msg => {
+      this.searchPlaceholder = msg;
+    });
+    this.search = true;
+    this.searchField.nativeElement.focus();
+  }
+
+  getVote(comment: Comment): Vote {
+    if (this.userRole === 0) {
+      return this.commentVoteMap.get(comment.id);
+    }
+  }
+
+  parseIncomingMessage(message: Message) {
+    const msg = JSON.parse(message.body);
+    const payload = msg.payload;
+    switch (msg.type) {
+      case 'CommentPatched':
+        // ToDo: Use a map for comments w/ key = commentId
+        for (let i = 0; i < this.comments.length; i++) {
+          if (payload.id === this.comments[i].id) {
+            for (const [key, value] of Object.entries(payload.changes)) {
+              if (key === this.ack) {
+                const isNowAck = <boolean>value;
+                if (isNowAck) {
+                  this.comments = this.comments.filter(function (el) {
+                    return el.id !== payload.id;
+                  });
+                }
+              }
+            }
+          }
+        }
+        break;
+      case 'CommentDeleted':
+        for (let i = 0; i < this.comments.length; i++) {
+          this.comments = this.comments.filter(function (el) {
+            return el.id !== payload.id;
+          });
+        }
+        break;
+    }
+
+    this.sortComments(this.currentSort);
+    this.searchComments();
+  }
+
+  parseIncomingModeratorMessage(message: Message) {
+    const msg = JSON.parse(message.body);
+    const payload = msg.payload;
+    if (msg.type === 'CommentCreated') {
+      const c = new Comment();
+      c.roomId = this.roomId;
+      c.body = payload.body;
+      c.id = payload.id;
+      c.timestamp = payload.timestamp;
+      this.comments = this.comments.concat(c);
+    }
+    this.sortComments(this.currentSort);
+    this.searchComments();
+  }
+
+  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;
+      }
+    });
+  }
+
+  sortComments(type: string): void {
+    if (this.hideCommentsList === true) {
+      this.sort(this.filteredComments, type);
+    } else {
+      this.sort(this.comments, type);
+    }
+    this.currentSort = type;
+  }
+
+  switchToCommentList(): void {
+    let role;
+    if (this.userRole === UserRole.CREATOR.valueOf()) {
+      role = 'creator';
+    } else if (this.userRole === UserRole.EXECUTIVE_MODERATOR) {
+      role = 'moderator';
+    }
+    this.router.navigate([`/${role}/room/${this.room.shortId}/comments`]);
+  }
+}
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 0f8cd978e..d19ba7331 100644
--- a/src/app/components/shared/comment-list/comment-list.component.html
+++ b/src/app/components/shared/comment-list/comment-list.component.html
@@ -119,7 +119,7 @@
   </mat-menu>
 </div>
 
-<button mat-icon-button class="scrollTop" [ngClass]="{'visible': isScrollButtonVisible()}" (click)="scrollToTop()">
+<button mat-icon-button class="scrollTop" [ngClass]="{'visible': isScrollButtonVisible()}" (click)="AppComponent.scrollTop()">
   <mat-icon>arrow_upward</mat-icon>
 </button>
 
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 f84010cc6..04399c86b 100644
--- a/src/app/components/shared/comment-list/comment-list.component.ts
+++ b/src/app/components/shared/comment-list/comment-list.component.ts
@@ -30,6 +30,7 @@ export class CommentListComponent implements OnInit {
   @ViewChild('searchBox') searchField: ElementRef;
   @Input() user: User;
   @Input() roomId: string;
+  AppComponent = AppComponent;
   comments: Comment[] = [];
   room: Room;
   hideCommentsList = false;
@@ -118,10 +119,6 @@ export class CommentListComponent implements OnInit {
     this.scrollExtended = currentScroll >= 300;
   }
 
-  scrollToTop(): void {
-    AppComponent.scrollTop();
-  }
-
   isScrollButtonVisible(): boolean {
     return !AppComponent.isScrolledTop() && this.comments.length > 5;
   }
diff --git a/src/styles.scss b/src/styles.scss
index 350780ad9..9680a8981 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -14,6 +14,12 @@
 // that you are using.
 @include angular-material-theme($arsnova-theme);
 
+body{
+  width:100%;
+  height:100%;
+  overflow:hidden;
+}
+
 ::-webkit-scrollbar{
   width:2px;
   background-color:var(--on-secondary);
@@ -60,5 +66,3 @@
 }
 
 
-
-
-- 
GitLab