From d0b46d8abdc9c04d433da4daae608fd00feb1a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20K=C3=A4sler?= <tom.kaesler@mni.thm.de> Date: Wed, 24 Jul 2019 15:14:05 +0200 Subject: [PATCH] Implement new moderators module Add moderation icon to moderators room page component in preparation for blacklist/whitelist Implement delete function --- src/app/app-routing.module.ts | 4 + .../moderator/moderator-routing.module.ts | 24 ++++ .../components/moderator/moderator.module.ts | 35 ++++++ .../room-moderator-page.component.html | 53 +++++++++ .../room-moderator-page.component.scss | 105 ++++++++++++++++++ .../room-moderator-page.component.ts | 42 +++++++ .../participant/participant-routing.module.ts | 6 - .../shared/comment/comment.component.html | 5 + .../shared/header/header.component.html | 6 +- .../shared/room-list/room-list.component.ts | 8 +- .../services/http/authentication.service.ts | 6 + src/assets/i18n/moderator/de.json | 36 ++++++ src/assets/i18n/moderator/en.json | 36 ++++++ 13 files changed, 354 insertions(+), 12 deletions(-) create mode 100644 src/app/components/moderator/moderator-routing.module.ts create mode 100644 src/app/components/moderator/moderator.module.ts create mode 100644 src/app/components/moderator/room-moderator-page/room-moderator-page.component.html create mode 100644 src/app/components/moderator/room-moderator-page/room-moderator-page.component.scss create mode 100644 src/app/components/moderator/room-moderator-page/room-moderator-page.component.ts create mode 100644 src/assets/i18n/moderator/de.json create mode 100644 src/assets/i18n/moderator/en.json diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 889a9ee26..fb9afda9c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -26,6 +26,10 @@ const routes: Routes = [ path: 'participant', loadChildren: './components/participant/participant.module#ParticipantModule' }, + { + path: 'moderator', + loadChildren: './components/moderator/moderator.module#ModeratorModule' + }, { path: '**', component: PageNotFoundComponent diff --git a/src/app/components/moderator/moderator-routing.module.ts b/src/app/components/moderator/moderator-routing.module.ts new file mode 100644 index 000000000..21150ea03 --- /dev/null +++ b/src/app/components/moderator/moderator-routing.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { AuthenticationGuard } from '../../guards/authentication.guard'; +import { RoomModeratorPageComponent } from './room-moderator-page/room-moderator-page.component'; +import { CommentPageComponent } from '../shared/comment-page/comment-page.component'; + +const routes: Routes = [ + { + path: 'room/:roomId', + component: RoomModeratorPageComponent, + canActivate: [AuthenticationGuard], + }, + { + path: 'room/:roomId/comments', + component: CommentPageComponent, + canActivate: [AuthenticationGuard], + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ModeratorRoutingModule { } diff --git a/src/app/components/moderator/moderator.module.ts b/src/app/components/moderator/moderator.module.ts new file mode 100644 index 000000000..4181ca584 --- /dev/null +++ b/src/app/components/moderator/moderator.module.ts @@ -0,0 +1,35 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ModeratorRoutingModule } from './moderator-routing.module'; +import { RoomModeratorPageComponent } from './room-moderator-page/room-moderator-page.component'; +import { EssentialsModule } from '../essentials/essentials.module'; +import { SharedModule } from '../shared/shared.module'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { HttpClient } from '@angular/common/http'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; + +@NgModule({ + imports: [ + CommonModule, + ModeratorRoutingModule, + EssentialsModule, + SharedModule, + TranslateModule.forChild({ + loader: { + provide: TranslateLoader, + useFactory: (HttpLoaderFactory), + deps: [HttpClient] + }, + isolate: true + }) + ], + declarations: [ + RoomModeratorPageComponent + ] +}) +export class ModeratorModule { +} + +export function HttpLoaderFactory(http: HttpClient) { + return new TranslateHttpLoader(http, '../../assets/i18n/moderator/', '.json'); +} diff --git a/src/app/components/moderator/room-moderator-page/room-moderator-page.component.html b/src/app/components/moderator/room-moderator-page/room-moderator-page.component.html new file mode 100644 index 000000000..beb79d0bb --- /dev/null +++ b/src/app/components/moderator/room-moderator-page/room-moderator-page.component.html @@ -0,0 +1,53 @@ +<div fxLayout="column" fxLayoutAlign="center" fxLayoutGap="20px" fxFill> + <div fxLayout="row" fxLayoutAlign="center"> + <mat-progress-spinner *ngIf="isLoading" mode="indeterminate"></mat-progress-spinner> + <mat-card *ngIf="room"> + <div fxLayout="row"> + <span class="fill-remaining-space"></span> + <mat-card-header fxLayoutAlign="center"> + <mat-card-title fxLayoutAlign="center"> + <h2>{{ room.name }}</h2> + </mat-card-title> + <mat-card-subtitle fxLayoutAlign="center"> + <h3> + {{ 'room-page.session-id' | translate}}: {{ room.shortId.slice(0, 4) }} {{ room.shortId.slice(4, 8) }} + </h3> + </mat-card-subtitle> + </mat-card-header> + <span class="fill-remaining-space"></span> + </div> + <mat-divider></mat-divider> + <mat-card-content *ngIf="room.description" fxLayoutAlign="center"> + <h4>{{room.description.trim()}}</h4> + </mat-card-content> + <mat-grid-list cols="2" rowHeight="2:1"> + <mat-grid-tile> + <button mat-icon-button routerLink="/moderator/room/{{ room.shortId }}/comments"> + <mat-icon matBadge="{{commentCounter}}" matBadgeColor="primary" + [ngClass]="{'desktop' : deviceType === 'desktop'}">question_answer + </mat-icon> + <h3>{{ 'room-page.public-stream' | translate}}</h3> <!-- *ngIf="deviceType === 'desktop'" --> + </button> + </mat-grid-tile> + <mat-grid-tile> + <button mat-icon-button routerLink="/moderator/room/{{ room.shortId }}/comments"> + <mat-icon matBadge="{{commentCounter}}" matBadgeColor="primary" + [ngClass]="{'desktop' : deviceType === 'desktop'}">gavel + </mat-icon> + <h3>{{ 'room-page.moderating-stream' | translate}}</h3> <!-- *ngIf="deviceType === 'desktop'" --> + </button> + </mat-grid-tile> + <!-- <mat-grid-tile> + <button mat-icon-button routerLink="/participant/room/{{ room.shortId }}/feedback-barometer"> + <mat-icon [ngClass]="{'desktop' : deviceType === 'desktop'}">thumbs_up_down</mat-icon> + <h3 *ngIf="deviceType === 'desktop'">{{ 'room-page.give-feedback' | translate}}</h3> + </button> + </mat-grid-tile> --> + </mat-grid-list> + + <!-- <app-content-groups *ngIf="room && room.contentGroups" [contentGroups]="room.contentGroups"></app-content-groups> --> + </mat-card> + + </div> + </div> + \ No newline at end of file diff --git a/src/app/components/moderator/room-moderator-page/room-moderator-page.component.scss b/src/app/components/moderator/room-moderator-page/room-moderator-page.component.scss new file mode 100644 index 000000000..c321c4e16 --- /dev/null +++ b/src/app/components/moderator/room-moderator-page/room-moderator-page.component.scss @@ -0,0 +1,105 @@ +@import '../../../../styles'; + +mat-card { + width: 100%; + max-width: 800px; + min-height: 350px; + background-color: var(--surface)!important; +} + +mat-card-content>:first-child { + margin-top: 5%; +} + +.mat-icon-button { + width: 60%; // 100% + height: 75%; + margin-bottom: 3%; + color: var(--primary)!important; +} + +/*mat-icon { + font-size: 60px; + height: 60px; + width: 60px; + line-height: 100%!important; +}*/ + +mat-icon { + font-size: 80px; + height: 80px; + width: 80px; + line-height: 100%!important; +} + +.desktop { + font-size: 70px; + height: 70px; + width: 70px; +} + +button { + width: 30%; + transition: all 0.3s; + + &:hover { + transform: scale(1.2) + } +} + +h2 { + font-size: large; + color: var(--on-surface); +} + +h3 { + font-size: larger; + color: var(--on-surface)!important; + margin-top: 15px; + margin-bottom: 10px; +} + +h4 { + font-size: 15px; + color: var(--on-surface)!important; + padding: 0 1% 0 1%; +} + +mat-card-header { + min-height: 80px!important; + height: 12%!important; +} + +mat-card-title { + height: 40%; +} + +mat-card-subtitle { + height: 20%; +} + +mat-expansion-panel { + background-color: var(--surface)!important; + min-width: 200px; + hyphens: auto; +} + +mat-grid-list { + margin-bottom: 20px !important; +} + +mat-grid-tile { + height: 100%!important; + padding: 2%!important; +} + +#settings { + width: 10%; + max-width: 40px; +} + +#settings-icon { + font-size: 35px; + height: 35px; + width: 35px; +} diff --git a/src/app/components/moderator/room-moderator-page/room-moderator-page.component.ts b/src/app/components/moderator/room-moderator-page/room-moderator-page.component.ts new file mode 100644 index 000000000..0361c3189 --- /dev/null +++ b/src/app/components/moderator/room-moderator-page/room-moderator-page.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { Room } from '../../../models/room'; +import { RoomPageComponent } from '../../shared/room-page/room-page.component'; +import { Location } from '@angular/common'; +import { RoomService } from '../../../services/http/room.service'; +import { ActivatedRoute } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { LanguageService } from '../../../services/util/language.service'; +import { WsCommentServiceService } from '../../../services/websockets/ws-comment-service.service'; +import { CommentService } from '../../../services/http/comment.service'; + +@Component({ + selector: 'app-room-moderator-page', + templateUrl: './room-moderator-page.component.html', + styleUrls: ['./room-moderator-page.component.scss'] +}) +export class RoomModeratorPageComponent extends RoomPageComponent implements OnInit { + + room: Room; + isLoading = true; + deviceType = localStorage.getItem('deviceType'); + + + constructor(protected location: Location, + protected roomService: RoomService, + protected route: ActivatedRoute, + private translateService: TranslateService, + protected langService: LanguageService, + protected wsCommentService: WsCommentServiceService, + protected commentService: CommentService) { + super(roomService, route, location, wsCommentService, commentService); + langService.langEmitter.subscribe(lang => translateService.use(lang)); + } + + ngOnInit() { + window.scroll(0, 0); + this.route.params.subscribe(params => { + this.initializeRoom(params['roomId']); + }); + this.translateService.use(localStorage.getItem('currentLang')); + } +} diff --git a/src/app/components/participant/participant-routing.module.ts b/src/app/components/participant/participant-routing.module.ts index 9c7b4ce2d..95f9263d0 100644 --- a/src/app/components/participant/participant-routing.module.ts +++ b/src/app/components/participant/participant-routing.module.ts @@ -13,37 +13,31 @@ const routes: Routes = [ { path: 'room/:roomId', component: RoomParticipantPageComponent, - canActivate: [AuthenticationGuard], data: { roles: [UserRole.PARTICIPANT] } }, { path: 'room/:roomId/statistics', component: StatisticsPageComponent, - canActivate: [AuthenticationGuard], data: { roles: [UserRole.PARTICIPANT] } }, { path: 'room/:roomId/statistics/:contentId', component: StatisticComponent, - canActivate: [AuthenticationGuard], data: { roles: [UserRole.PARTICIPANT] } }, { path: 'room/:roomId/comments', component: CommentPageComponent, - canActivate: [AuthenticationGuard], data: { roles: [UserRole.PARTICIPANT] } }, { path: 'room/:roomId/feedback-barometer', component: FeedbackBarometerPageComponent, - canActivate: [AuthenticationGuard], data: { roles: [UserRole.PARTICIPANT] } }, { path: 'room/:roomId/:contentGroup', component: ParticipantContentCarouselPageComponent, - canActivate: [AuthenticationGuard], data: { roles: [UserRole.PARTICIPANT] } } ]; diff --git a/src/app/components/shared/comment/comment.component.html b/src/app/components/shared/comment/comment.component.html index c553b23ce..172a759e3 100644 --- a/src/app/components/shared/comment/comment.component.html +++ b/src/app/components/shared/comment/comment.component.html @@ -26,6 +26,11 @@ matTooltip="{{ 'comment-page.mark-read' | translate }}">speaker_notes </mat-icon> </button> + <button mat-icon-button *ngIf="!isStudent" [disabled]="isStudent" (click)="delete(comment)"> + <mat-icon [ngClass]="{'read-icon': comment.read, 'not-marked' : !comment.read}" + matTooltip="{{ 'comment-page.delete' | translate }}">delete + </mat-icon> + </button> </div> <div fxLayout="row"> <div class="body click" (click)="openPresentDialog(comment)">{{comment.body.trim()}}</div> diff --git a/src/app/components/shared/header/header.component.html b/src/app/components/shared/header/header.component.html index 109f4dddd..a59db1e27 100644 --- a/src/app/components/shared/header/header.component.html +++ b/src/app/components/shared/header/header.component.html @@ -73,14 +73,10 @@ </button> <mat-menu #userMenu="matMenu" [overlapTrigger]="false"> - <button mat-menu-item *ngIf="user && user.role === 1" routerLink="/creator"> + <button mat-menu-item *ngIf="user" routerLink="/user"> <mat-icon class="sessions">work</mat-icon> <span>{{'header.my-sessions' | translate}}</span> </button> - <button mat-menu-item *ngIf="user && user.role === 0" routerLink="/participant"> - <mat-icon class="sessions">attach_file</mat-icon> - <span>{{'header.visited-sessions' | translate}}</span> - </button> <button mat-menu-item (click)="logout()"> <mat-icon color="warn">exit_to_app</mat-icon> <span>{{ 'header.logout' | translate }}</span> diff --git a/src/app/components/shared/room-list/room-list.component.ts b/src/app/components/shared/room-list/room-list.component.ts index 329a3ef22..f04f99a06 100644 --- a/src/app/components/shared/room-list/room-list.component.ts +++ b/src/app/components/shared/room-list/room-list.component.ts @@ -54,6 +54,7 @@ export class RoomListComponent implements OnInit { if (isOwner) { roomWithRole.role = UserRole.CREATOR; } else { + // TODO: acknowledge the other role option too roomWithRole.role = UserRole.PARTICIPANT; this.moderatorService.get(room.id).subscribe((moderators: Moderator[]) => { for (const m of moderators) { @@ -69,7 +70,12 @@ export class RoomListComponent implements OnInit { } setCurrentRoom(shortId: string) { - localStorage.setItem('shortId', shortId); + for (const r of this.roomsWithRole) { + if (r.shortId === shortId) { + this.authenticationService.assignRole(r.role); + localStorage.setItem('shortId', shortId); + } + } } roleToString(role: UserRole): string { diff --git a/src/app/services/http/authentication.service.ts b/src/app/services/http/authentication.service.ts index 64127a9ae..cb5eccd37 100644 --- a/src/app/services/http/authentication.service.ts +++ b/src/app/services/http/authentication.service.ts @@ -105,6 +105,12 @@ export class AuthenticationService { return this.user.getValue() !== undefined; } + assignRole(role: UserRole): void { + const u = this.user.getValue(); + u.role = role; + this.setUser(u); + } + getRole(): UserRole { return this.isLoggedIn() ? this.user.getValue().role : undefined; } diff --git a/src/assets/i18n/moderator/de.json b/src/assets/i18n/moderator/de.json new file mode 100644 index 000000000..5de058b30 --- /dev/null +++ b/src/assets/i18n/moderator/de.json @@ -0,0 +1,36 @@ +{ + "room-page": { + "session-id": "Session-ID", + "public-stream": "Öffentliche Kommentare", + "moderating-stream": "Moderation" + }, + "comment-list": { + "search": "Suchen", + "filter-comments": "Fragen filtern", + "sort-comments": "Fragen sortieren", + "add-comment": "Stell deine Frage!", + "correct": "Bejaht", + "favorite": "Hervorgehoben", + "read": "Beantwortet", + "unread": "Nicht beantwortet", + "vote-desc": "Absteigende Votes", + "vote-asc": "Aufsteigende Votes", + "time": "Zeit" + }, + "comment-page": { + "enter-title": "Titel", + "enter-comment": "Deine Frage", + "send": "Senden", + "abort": "Abbrechen", + "error-comment": "Bitte gib deine Frage ein.", + "error-title": "Bitte gib einen Titel ein.", + "error-both-fields": "Bitte fülle alle Felder aus.", + "no-comments": "Noch keine Fragen", + "mark-correct": "Dozent/in hat die Frage bejaht.", + "mark-favorite": "Dozent/in hält die Frage für besonders interessant.", + "mark-read": "Dozent/in hat die Frage beantwortet.", + "vote-up": "Hochvoten", + "vote-down": "Runtervoten", + "delete": "Löschen" + } +} diff --git a/src/assets/i18n/moderator/en.json b/src/assets/i18n/moderator/en.json new file mode 100644 index 000000000..95b3ad265 --- /dev/null +++ b/src/assets/i18n/moderator/en.json @@ -0,0 +1,36 @@ +{ + "room-page": { + "session-id": "Session-ID", + "public-stream": "Public Comments", + "moderating-stream": "Moderation" + }, + "comment-list": { + "search": "Search", + "filter-comments": "Filter questions", + "sort-comments": "Sort questions", + "add-comment": "Ask a question!", + "correct": "Marked as correct by the professor", + "favorite": "Professor's favorites", + "read": "Discussed by the professor", + "unread": "Not yet discussed", + "vote-desc": "Descending votes", + "vote-asc": "Ascending votes", + "time": "Time" + }, + "comment-page": { + "enter-title": "Title", + "enter-comment": "Your question", + "send": "Send", + "abort": "Cancel", + "error-title": "Please enter a title.", + "error-comment": "Please enter a question.", + "error-both-fields": "Please fill in all fields.", + "no-comments": "No questions yet", + "mark-correct": "Marked as correct by the professor", + "mark-favorite": "Professor's favorite", + "mark-read": "Already discussed by the professor", + "vote-up": "Vote up", + "vote-down": "Vote down", + "delete": "Delete" + } +} -- GitLab