From e0acd31af56947da6a0ca79a1ac6388bd73884d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=20K=C3=A4sler?= <tom.kaesler@mni.thm.de>
Date: Thu, 10 Oct 2019 14:15:12 +0200
Subject: [PATCH] Refactor comment userId attribute to creatorId

Add snackbar notification when a users comment got favorited
---
 proxy.conf.json                               |  8 +++
 src/app/app.module.ts                         |  2 +
 .../bonus-token/bonus-token.component.html    |  9 +++
 .../bonus-token/bonus-token.component.scss    |  3 +
 .../bonus-token/bonus-token.component.ts      | 25 ++++++++
 .../comment-settings.component.ts             | 57 +++++++++++++------
 src/app/components/creator/creator.module.ts  |  3 +
 .../room-creator-page.component.html          |  4 ++
 .../room-creator-page.component.ts            |  8 +++
 .../create-comment.component.ts               |  2 +-
 .../user-bonus-token.component.html           |  9 +++
 .../user-bonus-token.component.scss           |  3 +
 .../user-bonus-token.component.ts             | 25 ++++++++
 .../comment-list/comment-list.component.ts    |  6 ++
 .../shared/header/header.component.html       |  4 ++
 .../shared/header/header.component.ts         |  8 +++
 src/app/components/shared/shared.module.ts    |  5 +-
 src/app/models/bonus-token.ts                 | 15 +++++
 src/app/models/comment-bonus-token-mixin.ts   |  5 ++
 src/app/models/comment.ts                     |  6 +-
 src/app/services/http/bonus-token.service.ts  | 47 +++++++++++++++
 .../websockets/ws-comment-service.service.ts  |  2 +-
 src/assets/i18n/creator/de.json               |  2 +
 src/assets/i18n/creator/en.json               |  2 +
 src/assets/i18n/home/de.json                  |  4 ++
 src/assets/i18n/home/en.json                  |  4 ++
 src/assets/i18n/participant/de.json           |  1 +
 src/assets/i18n/participant/en.json           |  1 +
 28 files changed, 247 insertions(+), 23 deletions(-)
 create mode 100644 src/app/components/creator/_dialogs/bonus-token/bonus-token.component.html
 create mode 100644 src/app/components/creator/_dialogs/bonus-token/bonus-token.component.scss
 create mode 100644 src/app/components/creator/_dialogs/bonus-token/bonus-token.component.ts
 create mode 100644 src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.html
 create mode 100644 src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.scss
 create mode 100644 src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.ts
 create mode 100644 src/app/models/bonus-token.ts
 create mode 100644 src/app/models/comment-bonus-token-mixin.ts
 create mode 100644 src/app/services/http/bonus-token.service.ts

diff --git a/proxy.conf.json b/proxy.conf.json
index 86e0c8f7a..64aac1fae 100644
--- a/proxy.conf.json
+++ b/proxy.conf.json
@@ -15,6 +15,14 @@
     },
 		"logLevel": "debug"
   },
+  "/api/bonustoken": {
+    "target": "http://localhost:8088",
+    "secure": false,
+    "pathRewrite": {
+      "^/api": ""
+    },
+		"logLevel": "debug"
+  },
   "/api/settings": {
     "target": "http://localhost:8088",
     "secure": false,
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 75d38da8e..7720c73ac 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -41,6 +41,7 @@ import { DemoVideoComponent } from './components/home/_dialogs/demo-video/demo-v
 import { HomeCreatorPageComponent } from './components/home/home-creator-page/home-creator-page.component';
 import { HomeParticipantPageComponent } from './components/home/home-participant-page/home-participant-page.component';
 import { CommentSettingsService } from './services/http/comment-settings.service';
+import { BonusTokenService } from './services/http/bonus-token.service';
 import { ModeratorModule } from './components/moderator/moderator.module';
 import { ImprintComponent } from './components/home/_dialogs/imprint/imprint.component';
 import { DataProtectionComponent } from './components/home/_dialogs/data-protection/data-protection.component';
@@ -150,6 +151,7 @@ export function initializeApp(appConfig: AppConfig) {
     VoteService,
     ModeratorService,
     CommentSettingsService,
+    BonusTokenService,
     WsConnectorService,
     {
       provide: MatDialogRef,
diff --git a/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.html b/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.html
new file mode 100644
index 000000000..0bf87b73c
--- /dev/null
+++ b/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.html
@@ -0,0 +1,9 @@
+<div mat-dialog-content>
+  <h1>{{'room-page.bonus-token-header' | translate }}</h1>
+  <mat-divider></mat-divider>
+  <div fxLayout="row" *ngFor="let bonusToken of bonusTokens">
+    <h2>
+      {{bonusToken.token}}
+    </h2>
+  </div>
+</div>
diff --git a/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.scss b/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.scss
new file mode 100644
index 000000000..1ca530279
--- /dev/null
+++ b/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.scss
@@ -0,0 +1,3 @@
+h1,h2,h3,h4,h5,p{
+  color: var(--on-surface);
+}
diff --git a/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.ts b/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.ts
new file mode 100644
index 000000000..f270f68dc
--- /dev/null
+++ b/src/app/components/creator/_dialogs/bonus-token/bonus-token.component.ts
@@ -0,0 +1,25 @@
+import { Component, OnInit } from '@angular/core';
+import { BonusTokenService } from '../../../../services/http/bonus-token.service';
+import { BonusToken } from '../../../../models/bonus-token';
+
+@Component({
+  selector: 'app-bonus-token',
+  templateUrl: './bonus-token.component.html',
+  styleUrls: ['./bonus-token.component.scss']
+})
+export class BonusTokenComponent implements OnInit {
+  roomId: string;
+  bonusTokens: BonusToken[] = [];
+
+  constructor(
+    private bonusTokenService: BonusTokenService
+  ) {
+
+  }
+
+  ngOnInit() {
+    this.bonusTokenService.getTokensByRoomId(this.roomId).subscribe( list => {
+      this.bonusTokens = list;
+    });
+  }
+}
diff --git a/src/app/components/creator/_dialogs/comment-settings/comment-settings.component.ts b/src/app/components/creator/_dialogs/comment-settings/comment-settings.component.ts
index 5204e34e3..a5571f0eb 100644
--- a/src/app/components/creator/_dialogs/comment-settings/comment-settings.component.ts
+++ b/src/app/components/creator/_dialogs/comment-settings/comment-settings.component.ts
@@ -7,10 +7,12 @@ import { TranslateService } from '@ngx-translate/core';
 import { RoomService } from '../../../../services/http/room.service';
 import { Router } from '@angular/router';
 import { CommentService } from '../../../../services/http/comment.service';
+import { BonusTokenService } from '../../../../services/http/bonus-token.service';
 import { CommentSettingsService } from '../../../../services/http/comment-settings.service';
 import { DeleteCommentsComponent } from '../delete-comments/delete-comments.component';
 import { CommentExportComponent } from '../comment-export/comment-export.component';
 import { Room } from '../../../../models/room';
+import { CommentBonusTokenMixin } from '../../../../models/comment-bonus-token-mixin';
 import { CommentSettings } from '../../../../models/comment-settings';
 import { CommentSettingsDialog } from '../../../../models/comment-settings-dialog';
 
@@ -22,13 +24,14 @@ import { CommentSettingsDialog } from '../../../../models/comment-settings-dialo
 export class CommentSettingsComponent implements OnInit {
 
   roomId: string;
-  comments: Comment[];
+  comments: CommentBonusTokenMixin[];
   commentThreshold = -100;
   editRoom: Room;
   settingThreshold = false;
   enableCommentModeration = false;
   directSend = true;
 
+
   constructor(
     public dialogRef: MatDialogRef<RoomCreatorPageComponent>,
     public dialog: MatDialog,
@@ -38,6 +41,7 @@ export class CommentSettingsComponent implements OnInit {
     public router: Router,
     public commentService: CommentService,
     public commentSettingsService: CommentSettingsService,
+    private bonusTokenService: BonusTokenService,
     @Inject(MAT_DIALOG_DATA) public data: any
   ) {
   }
@@ -93,23 +97,42 @@ export class CommentSettingsComponent implements OnInit {
   export(delimiter: string, date: string): void {
     this.commentService.getAckComments(this.roomId)
       .subscribe(comments => {
-        this.comments = comments;
-        const exportComments = JSON.parse(JSON.stringify(this.comments));
-        let csv: string;
-        let valueFields = '';
-        const keyFields = ['Frage', 'Zeitstempel', 'Präsentiert', 'Favorit', 'Richtig/Falsch', 'Zugestellt', 'Score', '\r\n'];
-        exportComments.forEach(element => {
-          element.body = '"' + element.body.replace(/[\r\n]/g, ' ').replace(/ +/g, ' ').replace(/"/g, '""') + '"';
-          valueFields += Object.values(element).slice(3).join(delimiter) + '\r\n';
+        this.bonusTokenService.getTokensByRoomId(this.roomId).subscribe( list => {
+          this.comments = comments.map(comment => {
+            const commentWithToken: CommentBonusTokenMixin = <CommentBonusTokenMixin>comment;
+            for (const bt of list) {
+              if (commentWithToken.creatorId === bt.userId) {
+                commentWithToken.bonusToken = bt.token;
+              }
+            }
+            return commentWithToken;
+          });
+          const exportComments = JSON.parse(JSON.stringify(this.comments));
+          let csv: string;
+          let valueFields = '';
+          const keyFields = [
+            'Frage',
+            'Zeitstempel',
+            'Präsentiert',
+            'Favorit',
+            'Richtig/Falsch',
+            'Zugestellt',
+            'Score',
+            'Bonus Token',
+            '\r\n'
+          ];
+          exportComments.forEach(element => {
+            element.body = '"' + element.body.replace(/[\r\n]/g, ' ').replace(/ +/g, ' ').replace(/"/g, '""') + '"';
+            valueFields += Object.values(element).slice(3).join(delimiter) + '\r\n';
+          });
+          csv = keyFields + valueFields;
+          const myBlob = new Blob([csv], { type: 'text/csv' });
+          const link = document.createElement('a');
+          const fileName = 'comments_' + date + '.csv';
+          link.setAttribute('download', fileName);
+          link.href = window.URL.createObjectURL(myBlob);
+          link.click();
         });
-        csv = keyFields + valueFields;
-        console.log(csv);
-        const myBlob = new Blob([csv], { type: 'text/csv' });
-        const link = document.createElement('a');
-        const fileName = 'comments_' + date + '.csv';
-        link.setAttribute('download', fileName);
-        link.href = window.URL.createObjectURL(myBlob);
-        link.click();
       });
   }
 
diff --git a/src/app/components/creator/creator.module.ts b/src/app/components/creator/creator.module.ts
index 049446dea..b6ec2e716 100644
--- a/src/app/components/creator/creator.module.ts
+++ b/src/app/components/creator/creator.module.ts
@@ -23,6 +23,7 @@ import { ContentEditComponent } from './_dialogs/content-edit/content-edit.compo
 import { ContentPresentationComponent } from './content-presentation/content-presentation.component';
 import { CommentExportComponent } from './_dialogs/comment-export/comment-export.component';
 import { ModeratorsComponent } from './_dialogs/moderators/moderators.component';
+import { BonusTokenComponent } from './_dialogs/bonus-token/bonus-token.component';
 import { CommentSettingsComponent } from './_dialogs/comment-settings/comment-settings.component';
 import { ModeratorDeleteComponent } from './_dialogs/moderator-delete/moderator-delete.component';
 import { DeleteCommentComponent } from './_dialogs/delete-comment/delete-comment.component';
@@ -59,6 +60,7 @@ import { DeleteCommentsComponent } from './_dialogs/delete-comments/delete-comme
     ContentPresentationComponent,
     CommentExportComponent,
     ModeratorsComponent,
+    BonusTokenComponent,
     CommentSettingsComponent,
     ModeratorDeleteComponent,
     DeleteCommentsComponent,
@@ -77,6 +79,7 @@ import { DeleteCommentsComponent } from './_dialogs/delete-comments/delete-comme
     ContentEditComponent,
     CommentExportComponent,
     ModeratorsComponent,
+    BonusTokenComponent,
     CommentSettingsComponent,
     ModeratorDeleteComponent,
     DeleteCommentsComponent,
diff --git a/src/app/components/creator/room-creator-page/room-creator-page.component.html b/src/app/components/creator/room-creator-page/room-creator-page.component.html
index 0c0c5543a..f0f4c8150 100644
--- a/src/app/components/creator/room-creator-page/room-creator-page.component.html
+++ b/src/app/components/creator/room-creator-page/room-creator-page.component.html
@@ -35,6 +35,10 @@
             <mat-icon>gavel</mat-icon>
             {{ 'room-page.moderators' | translate}}
           </button>
+          <button mat-menu-item (click)="showBonusTokenDialog()" aria-labelledby= "person">
+            <mat-icon>grade</mat-icon>
+            {{ 'room-page.bonus-token' | translate}}
+          </button>
         </mat-menu>
         <button  id="settings-menu"
                  mat-icon-button class="corner-icons" [matMenuTriggerFor]="settingsMenu" aria-labelledby="settings">
diff --git a/src/app/components/creator/room-creator-page/room-creator-page.component.ts b/src/app/components/creator/room-creator-page/room-creator-page.component.ts
index fa6f25839..a21e930f5 100644
--- a/src/app/components/creator/room-creator-page/room-creator-page.component.ts
+++ b/src/app/components/creator/room-creator-page/room-creator-page.component.ts
@@ -14,6 +14,7 @@ import { TSMap } from 'typescript-map';
 import { WsCommentServiceService } from '../../../services/websockets/ws-comment-service.service';
 import { CommentService } from '../../../services/http/comment.service';
 import { ModeratorsComponent } from '../_dialogs/moderators/moderators.component';
+import { BonusTokenComponent } from '../_dialogs/bonus-token/bonus-token.component';
 import { CommentSettingsComponent } from '../_dialogs/comment-settings/comment-settings.component';
 import { LiveAnnouncer } from '@angular/cdk/a11y';
 import { EventService } from '../../../services/util/event.service';
@@ -202,6 +203,13 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni
     dialogRef.componentInstance.roomId = this.room.id;
   }
 
+  showBonusTokenDialog(): void {
+    const dialogRef = this.dialog.open(BonusTokenComponent, {
+      width: '400px'
+    });
+    dialogRef.componentInstance.roomId = this.room.id;
+  }
+
   copyShortId(): void {
     const selBox = document.createElement('textarea');
     selBox.style.position = 'fixed';
diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts b/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts
index 62f54c8aa..f48f539b3 100644
--- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts
+++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts
@@ -56,7 +56,7 @@ export class CreateCommentComponent implements OnInit {
       const comment = new Comment();
       comment.roomId = localStorage.getItem(`roomId`);
       comment.body = body;
-      comment.userId = this.user.id;
+      comment.creatorId = this.user.id;
       comment.createdFromLecturer = this.user.role === 1;
       this.dialogRef.close(comment);
     }
diff --git a/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.html b/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.html
new file mode 100644
index 000000000..6584f5b1d
--- /dev/null
+++ b/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.html
@@ -0,0 +1,9 @@
+<div mat-dialog-content>
+  <h1>{{'user-bonus-token-dialog.header' | translate }}</h1>
+  <mat-divider></mat-divider>
+  <div fxLayout="row" *ngFor="let bonusToken of bonusTokens">
+    <h2>
+      {{bonusToken.token}}
+    </h2>
+  </div>
+</div>
diff --git a/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.scss b/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.scss
new file mode 100644
index 000000000..1ca530279
--- /dev/null
+++ b/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.scss
@@ -0,0 +1,3 @@
+h1,h2,h3,h4,h5,p{
+  color: var(--on-surface);
+}
diff --git a/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.ts b/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.ts
new file mode 100644
index 000000000..e9cab7ee7
--- /dev/null
+++ b/src/app/components/shared/_dialogs/user-bonus-token/user-bonus-token.component.ts
@@ -0,0 +1,25 @@
+import { Component, OnInit } from '@angular/core';
+import { BonusTokenService } from '../../../../services/http/bonus-token.service';
+import { BonusToken } from '../../../../models/bonus-token';
+
+@Component({
+  selector: 'app-user-bonus-token',
+  templateUrl: './user-bonus-token.component.html',
+  styleUrls: ['./user-bonus-token.component.scss']
+})
+export class UserBonusTokenComponent implements OnInit {
+  userId: string;
+  bonusTokens: BonusToken[] = [];
+
+  constructor(
+    private bonusTokenService: BonusTokenService
+  ) {
+
+  }
+
+  ngOnInit() {
+    this.bonusTokenService.getTokensByUserId(this.userId).subscribe( list => {
+      this.bonusTokens = list;
+    });
+  }
+}
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 45bf1a4bc..d24eb3271 100644
--- a/src/app/components/shared/comment-list/comment-list.component.ts
+++ b/src/app/components/shared/comment-list/comment-list.component.ts
@@ -194,6 +194,12 @@ export class CommentListComponent implements OnInit {
                   break;
                 case this.favorite:
                   this.comments[i].favorite = <boolean>value;
+                  console.log(this.comments[i]);
+                  if (this.user.id === this.comments[i].creatorId && <boolean>value) {
+                    this.translateService.get('comment-list.comment-got-favorited').subscribe(ret => {
+                      this.notificationService.show(ret);
+                    });
+                  }
                   break;
                 case 'score':
                   this.comments[i].score = <number>value;
diff --git a/src/app/components/shared/header/header.component.html b/src/app/components/shared/header/header.component.html
index 2787fecc6..29c3faa3c 100644
--- a/src/app/components/shared/header/header.component.html
+++ b/src/app/components/shared/header/header.component.html
@@ -34,6 +34,10 @@
         <span *ngIf="!user.isGuest">{{'header.my-sessions' | translate}}</span>
         <span *ngIf="user.isGuest" svgIcon="meeting_room">{{'header.visited-sessions' | translate}}</span>
       </button>
+      <button mat-menu-item *ngIf="user" (click)="openUserBonusTokenDialog()">
+        <mat-icon color="warn">grade</mat-icon>
+        <span>{{'header.user-bonus-token' | translate}}</span>
+      </button>
       <button mat-menu-item *ngIf="user && !user.isGuest" (click)="openDeleteUserDialog()">
         <mat-icon color="warn">delete</mat-icon>
         <span>{{'header.delete-account' | translate}}</span>
diff --git a/src/app/components/shared/header/header.component.ts b/src/app/components/shared/header/header.component.ts
index 9ebd8deab..16ad1a1fc 100644
--- a/src/app/components/shared/header/header.component.ts
+++ b/src/app/components/shared/header/header.component.ts
@@ -15,6 +15,7 @@ import { AppComponent } from '../../../app.component';
 import { Rescale } from '../../../models/rescale';
 import { KeyboardUtils } from '../../../utils/keyboard';
 import { KeyboardKey } from '../../../utils/keyboard/keys';
+import { UserBonusTokenComponent } from '../_dialogs/user-bonus-token/user-bonus-token.component';
 
 @Component({
   selector: 'app-header',
@@ -151,6 +152,13 @@ export class HeaderComponent implements OnInit {
       });
   }
 
+  openUserBonusTokenDialog() {
+    const dialogRef = this.dialog.open(UserBonusTokenComponent, {
+      width: '600px'
+    });
+    dialogRef.componentInstance.userId = this.user.id;
+  }
+
   cookiesDisabled(): boolean {
     return localStorage.getItem('cookieAccepted') === 'false';
   }
diff --git a/src/app/components/shared/shared.module.ts b/src/app/components/shared/shared.module.ts
index 215e162dc..b35a47dba 100644
--- a/src/app/components/shared/shared.module.ts
+++ b/src/app/components/shared/shared.module.ts
@@ -19,6 +19,7 @@ import { ChartsModule } from 'ng2-charts';
 import { StatisticComponent } from './statistic/statistic.component';
 import { RoomJoinComponent } from './room-join/room-join.component';
 import { RoomCreateComponent } from './_dialogs/room-create/room-create.component';
+import { UserBonusTokenComponent } from './_dialogs/user-bonus-token/user-bonus-token.component';
 import { LoginComponent } from './login/login.component';
 import { StatisticHelpComponent } from './_dialogs/statistic-help/statistic-help.component';
 import { CommentComponent } from './comment/comment.component';
@@ -54,6 +55,7 @@ import { MatRippleModule } from '@angular/material';
     ListStatisticComponent,
     StatisticComponent,
     RoomCreateComponent,
+    UserBonusTokenComponent,
     LoginComponent,
     StatisticHelpComponent,
     CommentComponent,
@@ -88,7 +90,8 @@ import { MatRippleModule } from '@angular/material';
     StatisticHelpComponent,
     CreateCommentComponent,
     PresentCommentComponent,
-    DeleteAccountComponent
+    DeleteAccountComponent,
+    UserBonusTokenComponent
   ]
 })
 export class SharedModule {
diff --git a/src/app/models/bonus-token.ts b/src/app/models/bonus-token.ts
new file mode 100644
index 000000000..2c1ac3fd2
--- /dev/null
+++ b/src/app/models/bonus-token.ts
@@ -0,0 +1,15 @@
+export class BonusToken {
+  roomId: string;
+  userId: string;
+  token: string;
+
+  constructor(
+    roomId: string,
+    userId: string,
+    token: string
+  ) {
+    this.roomId = roomId;
+    this.userId = userId;
+    this.token = token;
+  }
+}
diff --git a/src/app/models/comment-bonus-token-mixin.ts b/src/app/models/comment-bonus-token-mixin.ts
new file mode 100644
index 000000000..961890a2c
--- /dev/null
+++ b/src/app/models/comment-bonus-token-mixin.ts
@@ -0,0 +1,5 @@
+import { Comment } from './comment';
+
+export class CommentBonusTokenMixin extends Comment {
+  bonusToken: string;
+}
diff --git a/src/app/models/comment.ts b/src/app/models/comment.ts
index 51308497b..22062ef8c 100644
--- a/src/app/models/comment.ts
+++ b/src/app/models/comment.ts
@@ -3,7 +3,7 @@ import { CorrectWrong } from './correct-wrong.enum';
 export class Comment {
   id: string;
   roomId: string;
-  userId: string;
+  creatorId: string;
   revision: string;
   body: string;
   read: boolean;
@@ -16,7 +16,7 @@ export class Comment {
   ack: boolean;
 
   constructor(roomId: string = '',
-              userId: string = '',
+              creatorId: string = '',
               body: string = '',
               read: boolean = false,
               correct: CorrectWrong = CorrectWrong.NULL,
@@ -28,7 +28,7 @@ export class Comment {
               ack: boolean = true) {
     this.id = '';
     this.roomId = roomId;
-    this.userId = userId;
+    this.creatorId = creatorId;
     this.revision = '';
     this.body = body;
     this.read = read;
diff --git a/src/app/services/http/bonus-token.service.ts b/src/app/services/http/bonus-token.service.ts
new file mode 100644
index 000000000..e85583249
--- /dev/null
+++ b/src/app/services/http/bonus-token.service.ts
@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { BonusToken } from '../../models/bonus-token';
+import { catchError, tap } from 'rxjs/operators';
+import { BaseHttpService } from './base-http.service';
+
+const httpOptions = {
+  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
+};
+
+@Injectable()
+export class BonusTokenService extends BaseHttpService {
+  private apiUrl = {
+    base: '/api',
+    bonustoken: '/bonustoken',
+    find: '/find'
+  };
+
+  constructor(private http: HttpClient) {
+    super();
+  }
+
+  getTokensByRoomId(roomId: string): Observable<BonusToken[]> {
+    const connectionUrl = `${this.apiUrl.base + this.apiUrl.bonustoken + this.apiUrl.find}`;
+    return this.http.post<BonusToken[]>(connectionUrl, {
+      properties: {
+        roomId: roomId
+      }
+    }).pipe(
+      tap(() => ''),
+      catchError(this.handleError<BonusToken[]>(`get bonus token by roomid = ${roomId}`))
+    );
+  }
+
+  getTokensByUserId(userId: string): Observable<BonusToken[]> {
+    const connectionUrl = `${this.apiUrl.base + this.apiUrl.bonustoken + this.apiUrl.find}`;
+    return this.http.post<BonusToken[]>(connectionUrl, {
+      properties: {
+        userId: userId
+      }
+    }).pipe(
+      tap(() => ''),
+      catchError(this.handleError<BonusToken[]>(`get bonus token by userId = ${userId}`))
+    );
+  }
+}
diff --git a/src/app/services/websockets/ws-comment-service.service.ts b/src/app/services/websockets/ws-comment-service.service.ts
index 8694fe84b..4333dc741 100644
--- a/src/app/services/websockets/ws-comment-service.service.ts
+++ b/src/app/services/websockets/ws-comment-service.service.ts
@@ -20,7 +20,7 @@ export class WsCommentServiceService {
   constructor(private wsConnector: WsConnectorService) { }
 
   add(comment: Comment): void {
-    const message = new CreateComment(comment.roomId, comment.userId, comment.body);
+    const message = new CreateComment(comment.roomId, comment.creatorId, comment.body);
     this.wsConnector.send(`/queue/comment.command.create`, JSON.stringify(message));
   }
 
diff --git a/src/assets/i18n/creator/de.json b/src/assets/i18n/creator/de.json
index a6ab5c415..963dc9233 100644
--- a/src/assets/i18n/creator/de.json
+++ b/src/assets/i18n/creator/de.json
@@ -161,6 +161,8 @@
     "a11y-threshold": "Schieberegler zum Einstellen des Schwellenwerts für sichtbare Fragen",
     "abort": "Abbrechen",
     "answer-statistics": "Statistiken",
+    "bonus-token": "Tokens für Bonuspunkte",
+    "bonus-token-header": "Tokens für Bonuspunkte",
     "cancel": "Abbrechen",
     "cancel-description": "Abbrechen",
     "changes-successful": "Änderungen gespeichert.",
diff --git a/src/assets/i18n/creator/en.json b/src/assets/i18n/creator/en.json
index 8213a3901..3d501f59f 100644
--- a/src/assets/i18n/creator/en.json
+++ b/src/assets/i18n/creator/en.json
@@ -162,6 +162,8 @@
     "a11y-threshold": "Slider for setting the threshold for visible questions",
     "abort": "Abort",
     "answer-statistics": "Statistics",
+    "bonus-token": "Tokens for bonus points",
+    "bonus-token-header": "Tokens for bonus points",
     "cancel": "Cancel",
     "cancel-description": "Cancel",
     "changes-successful": "Successfully updated.",
diff --git a/src/assets/i18n/home/de.json b/src/assets/i18n/home/de.json
index 8de49a45f..4fe7a1d4b 100644
--- a/src/assets/i18n/home/de.json
+++ b/src/assets/i18n/home/de.json
@@ -59,6 +59,7 @@
     "my-sessions": "Meine Sitzungen",
     "really-delete-account": "Willst du dein Konto mit allen Sitzungen unwiderruflich löschen?",
     "sure": "Bist du sicher?",
+    "user-bonus-token": "Zu deinen Bonus Tokens",
     "visited-sessions": "Besuchte Sitzungen"
   },
   "help": {
@@ -220,5 +221,8 @@
     "activation-key-input-description": "Gib hier den Aktivierungs-Schlüssel ein, den du per E-Mail zugesendet bekommen hast.",
     "cancel": "Abbrechen",
     "cancel-description": "Abbrechen"
+  },
+  "user-bonus-token": {
+    "header": "Tokens für Bonuspunkte"
   }
 }
diff --git a/src/assets/i18n/home/en.json b/src/assets/i18n/home/en.json
index df04f9c15..7c38513ef 100644
--- a/src/assets/i18n/home/en.json
+++ b/src/assets/i18n/home/en.json
@@ -60,6 +60,7 @@
     "my-sessions": "My sessions",
     "really-delete-account": "Do you really want to irrevocably delete your account with the associated sessions?",
     "sure": "Are you sure?",
+    "user-bonus-token": "See your bonus tokens",
     "visited-sessions": "Visited sessions"
   },
   "help": {
@@ -222,5 +223,8 @@
     "activation-key-input-description": "Enter the activation key that you received by e-mail.",
     "cancel": "Cancel",
     "cancel-description": "Cancel"
+  },
+  "user-bonus-token-dialog": {
+    "header": "Tokens for bonus points"
   }
 }
diff --git a/src/assets/i18n/participant/de.json b/src/assets/i18n/participant/de.json
index 7e9017645..b94578f76 100644
--- a/src/assets/i18n/participant/de.json
+++ b/src/assets/i18n/participant/de.json
@@ -28,6 +28,7 @@
     "add-comment": "Stell deine Frage!",
     "pause-comments": "Pausiere den Fragen-Stream",
     "play-comments": "Starte den Fragen-Stream",
+    "comment-got-favorited": "Eine Frage von dir wurde favorisiert!",
     "comment-stream-stopped": "Der Fragen-Stream wurde gestoppt.",
     "comment-stream-started": "Der Fragen-Stream wurde gestartet.",
     "comment-sent": "Die Frage wurde veröffentlicht.",
diff --git a/src/assets/i18n/participant/en.json b/src/assets/i18n/participant/en.json
index bd5132264..cd14fcfe3 100644
--- a/src/assets/i18n/participant/en.json
+++ b/src/assets/i18n/participant/en.json
@@ -28,6 +28,7 @@
     "add-comment": "Ask a question!",
     "pause-comments": "Pause the question stream",
     "play-comments": "Start the question stream",
+    "comment-got-favorited": "A question of yours got favorited!",
     "comment-stream-stopped": "Question stream has been stopped.",
     "comment-stream-started": "Question stream has been started.",
     "comment-sent": "The question has been published.",
-- 
GitLab