diff --git a/projects/ars/src/lib/services/ars-compose.service.ts b/projects/ars/src/lib/services/ars-compose.service.ts index 36ad1aba090d7bd5a2180c3087183736ca2afffd..7a9b745422c826ff06e1368efc664f645e55fe2f 100644 --- a/projects/ars/src/lib/services/ars-compose.service.ts +++ b/projects/ars/src/lib/services/ars-compose.service.ts @@ -75,7 +75,9 @@ export class ArsComposeService{ altToggle(onFalse: ArsMatMenuItemConfig, onTrue: ArsMatMenuItemConfig, obs: ArsObserver<boolean>, condition: () => boolean): ArsObserver<boolean>{ onFalse.condition = () => !obs.get() && condition(); + onFalse.callback=() => obs.set(!obs.get()); onTrue.condition = () => obs.get() && condition(); + onTrue.callback=() => obs.set(!obs.get()); this.menuItem(onFalse); this.menuItem(onTrue); return obs; 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 ac7a221f4b20d0c6ffa09b8773b275fcb3b6dc03..e043677506a5470772d57f6f9842dd5bf20182c2 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 @@ -14,7 +14,7 @@ <h2 class="oldtypo-h2">»{{room.name}}«</h2> <button mat-icon-button class="inline-icon-button" - (click)="editSessionName()" + (click)="roomPageEdit.editSessionName()" aria-labelledby="edit_session_name"> <mat-icon matTooltip="{{ 'session.edit-session-name' | translate}}">edit</mat-icon> </button> @@ -38,31 +38,31 @@ [overlapTrigger]="false" xPosition="before"> <button mat-menu-item - (click)="showSettingsDialog()" + (click)="roomPageEdit.showSettingsDialog()" aria-labelledby="edit"> <mat-icon>edit</mat-icon> {{ 'room-page.general' | translate}} </button> <button mat-menu-item - (click)="showCommentsDialog()" + (click)="roomPageEdit.showCommentsDialog()" aria-labelledby="insert_comment"> <mat-icon>insert_comment</mat-icon> {{ 'room-page.comments' | translate}} </button> <button mat-menu-item - (click)="showModeratorsDialog()" + (click)="roomPageEdit.showModeratorsDialog()" aria-labelledby="person"> <mat-icon>gavel</mat-icon> {{ 'room-page.moderators' | translate}} </button> <button mat-menu-item - (click)="showBonusTokenDialog()" + (click)="roomPageEdit.showBonusTokenDialog()" aria-labelledby="person"> <mat-icon class="star">grade</mat-icon> {{ 'room-page.bonus-token' | translate}} </button> <button mat-menu-item - (click)="showTagsDialog()" + (click)="roomPageEdit.showTagsDialog()" aria-labelledby="person"> <mat-icon svgIcon="comment_tag"></mat-icon> {{ 'room-page.tags' | translate}} 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 a4d1c17977747bdf2abb46c22d1d766d371fc339..d54fe904b1bfbd75a69ab856c1c786469ca56b19 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 @@ -1,4 +1,4 @@ -import { AfterContentInit, AfterViewInit, Component, ComponentRef, EventEmitter, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { AfterContentInit, AfterViewInit, Component, EventEmitter, OnDestroy, OnInit, Renderer2 } from '@angular/core'; import { RoomService } from '../../../services/http/room.service'; import { ActivatedRoute, Router } from '@angular/router'; import { RoomPageComponent } from '../../shared/room-page/room-page.component'; @@ -7,36 +7,21 @@ import { CommentSettingsDialog } from '../../../models/comment-settings-dialog'; import { Location } from '@angular/common'; import { NotificationService } from '../../../services/util/notification.service'; import { MatDialog } from '@angular/material/dialog'; -import { RoomEditComponent } from '../_dialogs/room-edit/room-edit.component'; import { TranslateService } from '@ngx-translate/core'; import { LanguageService } from '../../../services/util/language.service'; import { WsCommentService } from '../../../services/websockets/ws-comment.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 { TagsComponent } from '../_dialogs/tags/tags.component'; import { LiveAnnouncer } from '@angular/cdk/a11y'; import { EventService } from '../../../services/util/event.service'; import { KeyboardUtils } from '../../../utils/keyboard'; import { KeyboardKey } from '../../../utils/keyboard/keys'; import { TitleService } from '../../../services/util/title.service'; -import { DeleteCommentsComponent } from '../_dialogs/delete-comments/delete-comments.component'; -import { Export } from '../../../models/export'; import { BonusTokenService } from '../../../services/http/bonus-token.service'; -import { TopicCloudFilterComponent } from '../../shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component'; -import { RoomDeleteComponent } from '../_dialogs/room-delete/room-delete.component'; -import { RoomDeleted } from '../../../models/events/room-deleted'; -import { ProfanitySettingsComponent } from '../_dialogs/profanity-settings/profanity-settings.component'; -import { RoomDescriptionSettingsComponent } from '../_dialogs/room-description-settings/room-description-settings.component'; import { AuthenticationService } from '../../../services/http/authentication.service'; import { User } from '../../../models/user'; import { HeaderService } from '../../../services/util/header.service'; import { ArsComposeService } from '../../../../../projects/ars/src/lib/services/ars-compose.service'; -import { UserRole } from '../../../models/user-roles.enum'; -import { Palette } from '../../../../theme/Theme'; -import { ArsObserver } from '../../../../../projects/ars/src/lib/models/util/ars-observer'; -import { RoomNameSettingsComponent } from '../_dialogs/room-name-settings/room-name-settings.component'; +import { RoomPageEdit } from '../../shared/room-page/room-page-edit/room-page-edit'; @Component({ selector:'app-room-creator-page', @@ -57,6 +42,9 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni urlToCopy = `${window.location.protocol}//${window.location.hostname}/participant/room/`; headerInterface = null; onDestroyListener: EventEmitter<void> = new EventEmitter<void>(); + onAfterViewInitListener: EventEmitter<void> = new EventEmitter<void>(); + onInitListener: EventEmitter<void> = new EventEmitter<void>(); + roomPageEdit:RoomPageEdit; constructor(protected roomService: RoomService, protected notification: NotificationService, @@ -83,185 +71,32 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni this.titleService.attachTitle('(' + e + ')'); }); langService.langEmitter.subscribe(lang => translateService.use(lang)); - } - - ngAfterViewInit(){ - let isBuild = false; - this.authenticationService.watchUser.subscribe(user => { - this.user = user; - this.roomService.getRoomByShortId(this.encodedShortId).subscribe(e => { - this.room = e; - if (isBuild){ - return; + this.roomPageEdit=new RoomPageEdit( + dialog, + translationService, + notification, + roomService, + eventService, + location, + commentService, + bonusTokenService, + headerService, + composeService, + authenticationService, + route, + { + onInitListener:this.onInitListener, + onAfterViewInitListener:this.onAfterViewInitListener, + onDestroyListener:this.onDestroyListener, + updateCommentSettings(settings: CommentSettingsDialog){ + this.updateCommentSettings(settings); } - this.initNavigation(); - isBuild = true; - }); - }); - } - - initNavigation(){ - const list: ComponentRef<any>[] = this.composeService.builder(this.headerService.getHost(), e => { - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'flag', - text:'header.edit-session-description', - callback:() => this.editSessionDescription(), - condition:() => this.user.role > UserRole.PARTICIPANT - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'visibility_off', - isSVGIcon:false, - text:'header.moderation-mode', - callback:() => this.showCommentsDialog(), - condition:() => this.user.role > UserRole.PARTICIPANT - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'gavel', - text:'header.edit-moderator', - callback:() => this.showModeratorsDialog(), - condition:() => this.user.role > UserRole.PARTICIPANT - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'comment_tag', - isSVGIcon:true, - text:'header.edit-tags', - callback:() => this.showTagsDialog(), - condition:() => this.user.role > UserRole.PARTICIPANT - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'grade', - iconColor:Palette.YELLOW, - text:'header.bonustoken', - callback:() => this.showBonusTokenDialog(), - condition:() => this.user.role >= UserRole.PARTICIPANT - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'file_download', - text:'header.export-questions', - callback:() => this.exportQuestions(), - condition:() => this.user.role >= UserRole.PARTICIPANT - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'clear', - text:'header.profanity-filter', - callback:() => this.toggleProfanityFilter(), - condition:() => this.user.role > UserRole.PARTICIPANT - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'delete_sweep', - iconColor:Palette.RED, - text:'header.delete-questions', - callback:() => this.openDeleteRoomDialog(), - condition:() => this.user.role === UserRole.CREATOR - }); - e.menuItem({ - translate:this.headerService.getTranslate(), - icon:'delete', - iconColor:Palette.RED, - isSVGIcon:false, - text:'header.delete-room', - callback:() => this.openDeleteRoomDialog(), - condition:() => this.user.role === UserRole.CREATOR - }); - e.altToggle( - { - translate:this.headerService.getTranslate(), - text:'header.unlock', - icon:'block', - iconColor:Palette.RED, - color:Palette.RED - }, - { - translate:this.headerService.getTranslate(), - text:'header.block', - icon:'block', - iconColor:Palette.RED - }, - ArsObserver.build<boolean>(e => { - e.set(this.room.questionsBlocked); - e.onChange(a => { - this.room.questionsBlocked = a.get(); - this.roomService.updateRoom(this.room).subscribe(); - if (a.get()){ - this.headerService.getTranslate().get('header.questions-blocked').subscribe(msg => { - this.headerService.getNotificationService().show(msg); - }); - } - }); - }) - , - () => this.user.role > UserRole.PARTICIPANT - ); - }); - this.onDestroyListener.subscribe(() => { - list.forEach(e => e.destroy()); - }); - } - - navigateModeratorSettings(){ - this.showCommentsDialog(); - } - - toggleProfanityFilter(){ - const dialogRef = this.dialog.open(ProfanitySettingsComponent, { - width:'400px' - }); - dialogRef.componentInstance.editRoom = this.room; - } - - editSessionName() { - const dialogRef = this.dialog.open(RoomNameSettingsComponent, { - width: '900px', - maxWidth: 'calc( 100% - 50px )', - maxHeight: 'calc( 100vh - 50px )', - autoFocus: false - }); - dialogRef.componentInstance.editRoom = this.room; - } - - editSessionDescription(){ - const dialogRef = this.dialog.open(RoomDescriptionSettingsComponent, { - width:'900px', - maxWidth:'calc( 100% - 50px )', - maxHeight:'calc( 100vh - 50px )', - autoFocus:false - }); - dialogRef.componentInstance.editRoom = this.room; - } - - exportQuestions(){ - const exp: Export = new Export( - this.room, - this.commentService, - this.bonusTokenService, - this.translateService, - 'comment-list', - this.notificationService); - exp.exportAsCsv(); + } + ); } - deleteQuestions(){ - const dialogRef = this.dialog.open(DeleteCommentsComponent, { - width:'400px' - }); - dialogRef.componentInstance.roomId = this.room.id; - dialogRef.afterClosed() - .subscribe(result => { - if (result === 'delete'){ - this.translateService.get('room-page.comments-deleted').subscribe(msg => { - this.notificationService.show(msg); - }); - this.commentService.deleteCommentsByRoomId(this.room.id).subscribe(); - } - }); + ngAfterViewInit(){ + this.onAfterViewInitListener.emit(); } ngOnDestroy(){ @@ -313,6 +148,7 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni this.eventService.makeFocusOnInputFalse(); } }); + this.onInitListener.emit(); } public announce(){ @@ -345,19 +181,6 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni } - updateCommentSettings(settings: CommentSettingsDialog){ - this.room.tags = settings.tags; - - if (this.moderationEnabled && !settings.enableModeration){ - this.viewModuleCount = this.viewModuleCount - 1; - }else if (!this.moderationEnabled && settings.enableModeration){ - this.viewModuleCount = this.viewModuleCount + 1; - } - - this.moderationEnabled = settings.enableModeration; - localStorage.setItem('moderationEnabled', String(this.moderationEnabled)); - } - resetThreshold(): void{ this.room.moderated = undefined; this.room.threshold = undefined; @@ -365,129 +188,6 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni this.room.tags = undefined; } - saveChanges(){ - this.roomService.updateRoom(this.updRoom) - .subscribe((room) => { - this.room = room; - this.translateService.get('room-page.changes-successful').subscribe(msg => { - this.notification.show(msg); - }); - }, - error => { - this.translateService.get('room-page.changes-gone-wrong').subscribe(msg => { - this.notification.show(msg); - }); - }); - } - - showSettingsDialog(): void{ - this.updRoom = JSON.parse(JSON.stringify(this.room)); - const dialogRef = this.dialog.open(RoomEditComponent, { - width:'400px' - }); - dialogRef.componentInstance.editRoom = this.updRoom; - dialogRef.afterClosed() - .subscribe(result => { - if (result === 'abort'){ - return; - }else if (result !== 'delete'){ - this.saveChanges(); - } - }); - dialogRef.backdropClick().subscribe(res => { - dialogRef.close('abort'); - }); - } - - showCommentsDialog(): void{ - this.updRoom = JSON.parse(JSON.stringify(this.room)); - - const dialogRef = this.dialog.open(CommentSettingsComponent, { - width:'400px' - }); - dialogRef.componentInstance.roomId = this.room.id; - dialogRef.componentInstance.editRoom = this.updRoom; - dialogRef.afterClosed() - .subscribe(result => { - if (result === 'abort'){ - return; - }else{ - if (result instanceof CommentSettingsDialog){ - this.updateCommentSettings(result); - this.saveChanges(); - } - } - }); - dialogRef.backdropClick().subscribe(res => { - dialogRef.close('abort'); - }); - } - - showModeratorsDialog(): void{ - const dialogRef = this.dialog.open(ModeratorsComponent, { - width:'400px' - }); - dialogRef.componentInstance.roomId = this.room.id; - } - - showBonusTokenDialog(): void{ - const dialogRef = this.dialog.open(BonusTokenComponent, { - width:'400px' - }); - dialogRef.componentInstance.room = this.room; - } - - showTagCloud(): void{ - const dialogRef = this.dialog.open(TopicCloudFilterComponent, { - width:'400px' - }); - } - - showTagsDialog(): void{ - this.updRoom = JSON.parse(JSON.stringify(this.room)); - const dialogRef = this.dialog.open(TagsComponent, { - width:'400px' - }); - let tags = []; - if (this.room.tags !== undefined){ - tags = this.room.tags; - } - dialogRef.componentInstance.tags = tags; - dialogRef.afterClosed() - .subscribe(result => { - if (!result || result === 'abort'){ - return; - }else{ - this.updRoom.tags = result; - this.saveChanges(); - } - }); - } - - openDeleteRoomDialog(): void{ - const dialogRef = this.dialog.open(RoomDeleteComponent, { - width:'400px' - }); - dialogRef.componentInstance.room = this.room; - dialogRef.afterClosed() - .subscribe(result => { - if (result === 'delete'){ - this.deleteRoom(); - } - }); - } - - deleteRoom(): void{ - this.translationService.get('room-page.deleted').subscribe(msg => { - this.notificationService.show(this.room.name + msg); - }); - this.roomService.deleteRoom(this.room.id).subscribe(result => { - const event = new RoomDeleted(this.room.id); - this.eventService.broadcast(event.type, event.payload); - this.location.back(); - }); - } - copyShortId(): void{ const selBox = document.createElement('textarea'); selBox.style.position = 'fixed'; @@ -504,5 +204,19 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni this.notification.show(msg, '', {duration:2000}); }); } + + updateCommentSettings(settings: CommentSettingsDialog){ + this.room.tags = settings.tags; + + if (this.moderationEnabled && !settings.enableModeration){ + this.viewModuleCount = this.viewModuleCount - 1; + }else if (!this.moderationEnabled && settings.enableModeration){ + this.viewModuleCount = this.viewModuleCount + 1; + } + + this.moderationEnabled = settings.enableModeration; + localStorage.setItem('moderationEnabled', String(this.moderationEnabled)); + } + } diff --git a/src/app/components/moderator/moderator-comment-page/moderator-comment-page.component.ts b/src/app/components/moderator/moderator-comment-page/moderator-comment-page.component.ts index 99250435d0a9dc6a3a618249e7b08c3fb4e1c559..d1c321f8c28c7c4ca06aa8b36f55319ab1930e33 100644 --- a/src/app/components/moderator/moderator-comment-page/moderator-comment-page.component.ts +++ b/src/app/components/moderator/moderator-comment-page/moderator-comment-page.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Renderer2, OnDestroy, AfterContentInit } from '@angular/core'; +import { AfterContentInit, AfterViewInit, Component, EventEmitter, OnDestroy, OnInit, Renderer2 } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { User } from '../../../models/user'; import { NotificationService } from '../../../services/util/notification.service'; @@ -7,30 +7,89 @@ import { LiveAnnouncer } from '@angular/cdk/a11y'; import { EventService } from '../../../services/util/event.service'; import { KeyboardUtils } from '../../../utils/keyboard'; import { KeyboardKey } from '../../../utils/keyboard/keys'; +import { Room } from '../../../models/room'; +import { HeaderService } from '../../../services/util/header.service'; +import { RoomService } from '../../../services/http/room.service'; +import { ArsComposeService } from '../../../../../projects/ars/src/lib/services/ars-compose.service'; +import { RoomPageEdit } from '../../shared/room-page/room-page-edit/room-page-edit'; +import { CommentSettingsDialog } from '../../../models/comment-settings-dialog'; +import { DialogActionButtonsComponent } from '../../shared/dialog/dialog-action-buttons/dialog-action-buttons.component'; +import { MatDialog } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; +import { Location } from '@angular/common'; +import { CommentService } from '../../../services/http/comment.service'; +import { BonusTokenService } from '../../../services/http/bonus-token.service'; +import { throwError } from 'rxjs'; +import { RoomPageComponent } from '../../shared/room-page/room-page.component'; +import { WsCommentService } from '../../../services/websockets/ws-comment.service'; @Component({ selector: 'app-moderator-comment-page', templateUrl: './moderator-comment-page.component.html', styleUrls: ['./moderator-comment-page.component.scss'] }) -export class ModeratorCommentPageComponent implements OnInit, OnDestroy, AfterContentInit { +export class ModeratorCommentPageComponent extends RoomPageComponent implements OnInit, OnDestroy, AfterContentInit, AfterViewInit { roomId: string; user: User; + room: Room; + onDestroyListener: EventEmitter<void> = new EventEmitter<void>(); + onAfterViewInitListener: EventEmitter<void> = new EventEmitter<void>(); + onInitListener: EventEmitter<void> = new EventEmitter<void>(); + roomPageEdit:RoomPageEdit; + viewModuleCount = 1; listenerFn: () => void; - constructor(private route: ActivatedRoute, + constructor(protected route: ActivatedRoute, private notification: NotificationService, private authenticationService: AuthenticationService, public eventService: EventService, private _r: Renderer2, - private liveAnnouncer: LiveAnnouncer) { } + private liveAnnouncer: LiveAnnouncer, + public headerService:HeaderService, + public roomService:RoomService, + public composeService:ArsComposeService, + public dialog:MatDialog, + public translationService:TranslateService, + public location:Location, + public commentService:CommentService, + public bonusTokenService:BonusTokenService, + public wsCommentService:WsCommentService) { + super(roomService, route, location, wsCommentService, commentService, eventService); + this.roomPageEdit=new RoomPageEdit( + dialog, + translationService, + notification, + roomService, + eventService, + location, + commentService, + bonusTokenService, + headerService, + composeService, + authenticationService, + route, + { + onInitListener:this.onInitListener, + onAfterViewInitListener:this.onAfterViewInitListener, + onDestroyListener:this.onDestroyListener, + updateCommentSettings(settings: CommentSettingsDialog){ + this.updateCommentSettings(settings); + } + } + ); + } ngAfterContentInit(): void { setTimeout( () => { document.getElementById('live_announcer-button').focus(); }, 500); } + + ngAfterViewInit(){ + this.onAfterViewInitListener.emit(); + } + ngOnInit(): void { this.roomId = localStorage.getItem('roomId'); this.user = this.authenticationService.getUser(); @@ -58,11 +117,26 @@ export class ModeratorCommentPageComponent implements OnInit, OnDestroy, AfterCo document.getElementById('sort-button').focus(); } }); + this.onInitListener.emit(); } ngOnDestroy() { this.listenerFn(); this.eventService.makeFocusOnInputFalse(); + this.onDestroyListener.emit(); + } + + updateCommentSettings(settings: CommentSettingsDialog){ + this.room.tags = settings.tags; + + if (this.moderationEnabled && !settings.enableModeration){ + this.viewModuleCount = this.viewModuleCount - 1; + }else if (!this.moderationEnabled && settings.enableModeration){ + this.viewModuleCount = this.viewModuleCount + 1; + } + + this.moderationEnabled = settings.enableModeration; + localStorage.setItem('moderationEnabled', String(this.moderationEnabled)); } public announce() { diff --git a/src/app/components/participant/room-participant-page/room-participant-page.component.ts b/src/app/components/participant/room-participant-page/room-participant-page.component.ts index 3e4dce340b68f63eb6bfddc28500fddd8c70ffc7..9f71b7cf90e93affa5f80d19d029f87678b79829 100644 --- a/src/app/components/participant/room-participant-page/room-participant-page.component.ts +++ b/src/app/components/participant/room-participant-page/room-participant-page.component.ts @@ -1,4 +1,4 @@ -import { AfterContentInit, Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'; +import { AfterContentInit, AfterViewInit, Component, EventEmitter, OnDestroy, OnInit, Renderer2 } from '@angular/core'; import { Room } from '../../../models/room'; import { User } from '../../../models/user'; import { UserRole } from '../../../models/user-roles.enum'; @@ -17,18 +17,30 @@ import { KeyboardUtils } from '../../../utils/keyboard'; import { KeyboardKey } from '../../../utils/keyboard/keys'; import { map } from 'rxjs/operators'; import { Observable, of } from 'rxjs'; +import { CommentSettingsDialog } from '../../../models/comment-settings-dialog'; +import { RoomPageEdit } from '../../shared/room-page/room-page-edit/room-page-edit'; +import { MatDialog } from '@angular/material/dialog'; +import { NotificationService } from '../../../services/util/notification.service'; +import { BonusTokenService } from '../../../services/http/bonus-token.service'; +import { HeaderService } from '../../../services/util/header.service'; +import { ArsComposeService } from '../../../../../projects/ars/src/lib/services/ars-compose.service'; @Component({ selector: 'app-room-participant-page', templateUrl: './room-participant-page.component.html', styleUrls: ['./room-participant-page.component.scss'] }) -export class RoomParticipantPageComponent extends RoomPageComponent implements OnInit, OnDestroy, AfterContentInit { +export class RoomParticipantPageComponent extends RoomPageComponent implements OnInit, OnDestroy, AfterContentInit, AfterViewInit { room: Room; isLoading = true; deviceType = localStorage.getItem('deviceType'); user: User; + viewModuleCount = 1; + roomPageEdit:RoomPageEdit; + onDestroyListener: EventEmitter<void> = new EventEmitter<void>(); + onAfterViewInitListener: EventEmitter<void> = new EventEmitter<void>(); + onInitListener: EventEmitter<void> = new EventEmitter<void>(); constructor(protected location: Location, protected roomService: RoomService, @@ -40,9 +52,41 @@ export class RoomParticipantPageComponent extends RoomPageComponent implements O protected authenticationService: AuthenticationService, private liveAnnouncer: LiveAnnouncer, private _r: Renderer2, - public eventService: EventService) { + public eventService: EventService, + public dialog:MatDialog, + public notification:NotificationService, + public bonusTokenService:BonusTokenService, + public headerService:HeaderService, + public composeService:ArsComposeService) { super(roomService, route, location, wsCommentService, commentService, eventService); langService.langEmitter.subscribe(lang => translateService.use(lang)); + this.roomPageEdit=new RoomPageEdit( + dialog, + translateService, + notification, + roomService, + eventService, + location, + commentService, + bonusTokenService, + headerService, + composeService, + authenticationService, + route, + { + onInitListener:this.onInitListener, + onAfterViewInitListener:this.onAfterViewInitListener, + onDestroyListener:this.onDestroyListener, + updateCommentSettings(settings: CommentSettingsDialog){ + this.updateCommentSettings(settings); + } + } + ); + } + + ngOnDestroy(){ + super.ngOnDestroy(); + this.onDestroyListener.emit(); } ngAfterContentInit(): void { @@ -51,6 +95,10 @@ export class RoomParticipantPageComponent extends RoomPageComponent implements O }, 700); } + ngAfterViewInit(){ + this.onAfterViewInitListener.emit(); + } + ngOnInit() { window.scroll(0, 0); this.route.params.subscribe(params => { @@ -72,6 +120,7 @@ export class RoomParticipantPageComponent extends RoomPageComponent implements O this.eventService.makeFocusOnInputFalse(); } }); + this.onInitListener.emit(); } public announce() { @@ -103,6 +152,19 @@ export class RoomParticipantPageComponent extends RoomPageComponent implements O } } + updateCommentSettings(settings: CommentSettingsDialog){ + this.room.tags = settings.tags; + + if (this.moderationEnabled && !settings.enableModeration){ + this.viewModuleCount = this.viewModuleCount - 1; + }else if (!this.moderationEnabled && settings.enableModeration){ + this.viewModuleCount = this.viewModuleCount + 1; + } + + this.moderationEnabled = settings.enableModeration; + localStorage.setItem('moderationEnabled', String(this.moderationEnabled)); + } + postRoomLoadHook() { if (!this.authenticationService.hasAccess(this.room.shortId, UserRole.PARTICIPANT)) { this.authenticationService.setAccess(this.room.shortId, UserRole.PARTICIPANT); diff --git a/src/app/components/shared/header/header.component.html b/src/app/components/shared/header/header.component.html index f864a02026a522293c8260b57fd02762e195ebad..1050c0c203b8d023d91cc8d770e1f9393643733f 100644 --- a/src/app/components/shared/header/header.component.html +++ b/src/app/components/shared/header/header.component.html @@ -43,9 +43,6 @@ {{cTime}} </h2> - <span class="fill-remaining-space" - *ngIf="room && room.questionsBlocked"></span> - <h1 *ngIf="room && room.questionsBlocked"> {{'header.questions-blocked'|translate}} diff --git a/src/app/components/shared/room-page/room-page-edit/room-page-edit.ts b/src/app/components/shared/room-page/room-page-edit/room-page-edit.ts new file mode 100644 index 0000000000000000000000000000000000000000..1bdc7d764dc6e027104b4dfc878e32ff2aef8425 --- /dev/null +++ b/src/app/components/shared/room-page/room-page-edit/room-page-edit.ts @@ -0,0 +1,292 @@ +import { CommentSettingsComponent } from '../../../creator/_dialogs/comment-settings/comment-settings.component'; +import { CommentSettingsDialog } from '../../../../models/comment-settings-dialog'; +import { ModeratorsComponent } from '../../../creator/_dialogs/moderators/moderators.component'; +import { BonusTokenComponent } from '../../../creator/_dialogs/bonus-token/bonus-token.component'; +import { TopicCloudFilterComponent } from '../../_dialogs/topic-cloud-filter/topic-cloud-filter.component'; +import { TagsComponent } from '../../../creator/_dialogs/tags/tags.component'; +import { RoomDeleteComponent } from '../../../creator/_dialogs/room-delete/room-delete.component'; +import { RoomDeleted } from '../../../../models/events/room-deleted'; +import { Room } from '../../../../models/room'; +import { User } from '../../../../models/user'; +import { MatDialog } from '@angular/material/dialog'; +import { ProfanitySettingsComponent } from '../../../creator/_dialogs/profanity-settings/profanity-settings.component'; +import { RoomNameSettingsComponent } from '../../../creator/_dialogs/room-name-settings/room-name-settings.component'; +import { RoomDescriptionSettingsComponent } from '../../../creator/_dialogs/room-description-settings/room-description-settings.component'; +import { Export } from '../../../../models/export'; +import { DeleteCommentsComponent } from '../../../creator/_dialogs/delete-comments/delete-comments.component'; +import { RoomEditComponent } from '../../../creator/_dialogs/room-edit/room-edit.component'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationService } from '../../../../services/util/notification.service'; +import { RoomService } from '../../../../services/http/room.service'; +import { EventService } from '../../../../services/util/event.service'; +import { Location } from '@angular/common'; +import { CommentService } from '../../../../services/http/comment.service'; +import { BonusTokenService } from '../../../../services/http/bonus-token.service'; +import { RoomPageHeader } from '../room-page-header/room-page-header'; +import { ArsComposeService } from '../../../../../../projects/ars/src/lib/services/ars-compose.service'; +import { HeaderService } from '../../../../services/util/header.service'; +import { EventEmitter } from '@angular/core'; +import { AuthenticationService } from '../../../../services/http/authentication.service'; +import { ActivatedRoute, Router } from '@angular/router'; + +export interface RoomPageEditConfig{ + + updateCommentSettings(settings: CommentSettingsDialog): void; + + onDestroyListener: EventEmitter<void>; + onAfterViewInitListener: EventEmitter<void>; + onInitListener: EventEmitter<void>; + +} + +export class RoomPageEdit{ + + private updRoom: Room; + private room: Room; + private user: User; + private encodedShortId: string; + + constructor( + private dialog: MatDialog, + private translate: TranslateService, + private notification: NotificationService, + private roomService: RoomService, + private eventService: EventService, + private location: Location, + private commentService: CommentService, + private bonusTokenService: BonusTokenService, + private headerService: HeaderService, + private composeService: ArsComposeService, + private authenticationService: AuthenticationService, + private route: ActivatedRoute, + private config: RoomPageEditConfig + ){ + config.onInitListener.subscribe(() => { + this.route.params.subscribe(params => { + this.encodedShortId = params['shortId']; + }); + }); + config.onAfterViewInitListener.subscribe(() => { + let isBuild = false; + this.authenticationService.watchUser.subscribe(user => { + this.user = user; + this.roomService.getRoomByShortId(this.encodedShortId).subscribe(e => { + this.room = e; + if (isBuild){ + return; + } + this.initNavigation(); + isBuild = true; + }); + }); + }); + } + + initNavigation(){ + // This could be simplified by the component implementing the interface and passing it instead + // esp. with a visitor pattern + new RoomPageHeader( + { + onDeleteRoom:() => this.deleteRoom(), + onDeleteQuestions:() => this.deleteQuestions(), + onToggleProfanity:() => this.toggleProfanityFilter(), + onExportQuestions:() => this.exportQuestions(), + onShowTags:() => this.showTagsDialog(), + onShowComments:() => this.showCommentsDialog(), + onEditSessionDescription:() => this.editSessionDescription(), + onShowBonusToken:() => this.showBonusTokenDialog(), + onShowModerators:() => this.showModeratorsDialog() + }, { + onDestroy:this.config.onDestroyListener, + room:() => this.room, + user:() => this.user, + headerService:this.headerService, + composeService:this.composeService, + roomService:this.roomService + } + ).build(); + } + + //Dialogs + + showCommentsDialog(): void{ + this.updRoom = JSON.parse(JSON.stringify(this.room)); + + const dialogRef = this.dialog.open(CommentSettingsComponent, { + width:'400px' + }); + dialogRef.componentInstance.roomId = this.room.id; + dialogRef.componentInstance.editRoom = this.updRoom; + dialogRef.afterClosed() + .subscribe(result => { + if (result === 'abort'){ + return; + }else{ + if (result instanceof CommentSettingsDialog){ + this.config.updateCommentSettings(result); + this.saveChanges(); + } + } + }); + dialogRef.backdropClick().subscribe(res => { + dialogRef.close('abort'); + }); + } + + showModeratorsDialog(): void{ + const dialogRef = this.dialog.open(ModeratorsComponent, { + width:'400px' + }); + dialogRef.componentInstance.roomId = this.room.id; + } + + showBonusTokenDialog(): void{ + const dialogRef = this.dialog.open(BonusTokenComponent, { + width:'400px' + }); + dialogRef.componentInstance.room = this.room; + } + + showTagCloud(): void{ + const dialogRef = this.dialog.open(TopicCloudFilterComponent, { + width:'400px' + }); + } + + showTagsDialog(): void{ + this.updRoom = JSON.parse(JSON.stringify(this.room)); + const dialogRef = this.dialog.open(TagsComponent, { + width:'400px' + }); + let tags = []; + if (this.room.tags !== undefined){ + tags = this.room.tags; + } + dialogRef.componentInstance.tags = tags; + dialogRef.afterClosed() + .subscribe(result => { + if (!result || result === 'abort'){ + return; + }else{ + this.updRoom.tags = result; + this.saveChanges(); + } + }); + } + + openDeleteRoomDialog(): void{ + const dialogRef = this.dialog.open(RoomDeleteComponent, { + width:'400px' + }); + dialogRef.componentInstance.room = this.room; + dialogRef.afterClosed() + .subscribe(result => { + if (result === 'delete'){ + this.deleteRoom(); + } + }); + } + + deleteRoom(): void{ + this.translate.get('room-page.deleted').subscribe(msg => { + this.notification.show(this.room.name + msg); + }); + this.roomService.deleteRoom(this.room.id).subscribe(result => { + const event = new RoomDeleted(this.room.id); + this.eventService.broadcast(event.type, event.payload); + this.location.back(); + }); + } + + + toggleProfanityFilter(){ + const dialogRef = this.dialog.open(ProfanitySettingsComponent, { + width:'400px' + }); + dialogRef.componentInstance.editRoom = this.room; + } + + editSessionName(){ + const dialogRef = this.dialog.open(RoomNameSettingsComponent, { + width:'900px', + maxWidth:'calc( 100% - 50px )', + maxHeight:'calc( 100vh - 50px )', + autoFocus:false + }); + dialogRef.componentInstance.editRoom = this.room; + } + + editSessionDescription(){ + const dialogRef = this.dialog.open(RoomDescriptionSettingsComponent, { + width:'900px', + maxWidth:'calc( 100% - 50px )', + maxHeight:'calc( 100vh - 50px )', + autoFocus:false + }); + dialogRef.componentInstance.editRoom = this.room; + } + + exportQuestions(){ + const exp: Export = new Export( + this.room, + this.commentService, + this.bonusTokenService, + this.translate, + 'comment-list', + this.notification); + exp.exportAsCsv(); + } + + deleteQuestions(){ + const dialogRef = this.dialog.open(DeleteCommentsComponent, { + width:'400px' + }); + dialogRef.componentInstance.roomId = this.room.id; + dialogRef.afterClosed() + .subscribe(result => { + if (result === 'delete'){ + this.translate.get('room-page.comments-deleted').subscribe(msg => { + this.notification.show(msg); + }); + this.commentService.deleteCommentsByRoomId(this.room.id).subscribe(); + } + }); + } + + showSettingsDialog(): void{ + this.updRoom = JSON.parse(JSON.stringify(this.room)); + const dialogRef = this.dialog.open(RoomEditComponent, { + width:'400px' + }); + dialogRef.componentInstance.editRoom = this.updRoom; + dialogRef.afterClosed() + .subscribe(result => { + if (result === 'abort'){ + return; + }else if (result !== 'delete'){ + this.saveChanges(); + } + }); + dialogRef.backdropClick().subscribe(res => { + dialogRef.close('abort'); + }); + } + + // tools + + saveChanges(){ + this.roomService.updateRoom(this.updRoom) + .subscribe((room) => { + this.room = room; + this.translate.get('room-page.changes-successful').subscribe(msg => { + this.notification.show(msg); + }); + }, + error => { + this.translate.get('room-page.changes-gone-wrong').subscribe(msg => { + this.notification.show(msg); + }); + }); + } + +} diff --git a/src/app/components/shared/room-page/room-page-header/room-page-header.ts b/src/app/components/shared/room-page/room-page-header/room-page-header.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed63fe370f17e65d9e4ed2e42e7bcb0ab07d5506 --- /dev/null +++ b/src/app/components/shared/room-page/room-page-header/room-page-header.ts @@ -0,0 +1,157 @@ +import { ComponentRef, EventEmitter } from '@angular/core'; +import { UserRole } from '../../../../models/user-roles.enum'; +import { Palette } from '../../../../../theme/Theme'; +import { ArsObserver } from '../../../../../../projects/ars/src/lib/models/util/ars-observer'; +import { HeaderService } from '../../../../services/util/header.service'; +import { ArsComposeService } from '../../../../../../projects/ars/src/lib/services/ars-compose.service'; +import { User } from '../../../../models/user'; +import { Room } from '../../../../models/room'; +import { RoomService } from '../../../../services/http/room.service'; + +export interface RoomPageHeaderAction{ + + onEditSessionDescription: () => void; + onShowComments: () => void; + onShowModerators: () => void; + onShowTags: () => void; + onShowBonusToken: () => void; + onExportQuestions: () => void; + onToggleProfanity: () => void; + onDeleteRoom: () => void; + onDeleteQuestions: () => void; + +} + +export interface RoomPageHeaderConfig{ + + onDestroy: EventEmitter<void>; + + user: () => User; + room: () => Room; + + headerService: HeaderService; + composeService: ArsComposeService; + roomService: RoomService; + +} + +export class RoomPageHeader{ + + constructor( + private action: RoomPageHeaderAction, + private config: RoomPageHeaderConfig + ){ + } + + build(){ + const list: ComponentRef<any>[] = this.config.composeService.builder(this.config.headerService.getHost(), e => { + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'flag', + text:'header.edit-session-description', + callback:() => this.action.onEditSessionDescription(), + condition:() => this.config.user().role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'visibility_off', + isSVGIcon:false, + text:'header.moderation-mode', + callback:() => this.action.onShowComments(), + condition:() => this.config.user().role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'gavel', + text:'header.edit-moderator', + callback:() => this.action.onShowModerators(), + condition:() => this.config.user().role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'comment_tag', + isSVGIcon:true, + text:'header.edit-tags', + callback:() => this.action.onShowTags(), + condition:() => this.config.user().role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'grade', + iconColor:Palette.YELLOW, + text:'header.bonustoken', + callback:() => this.action.onShowTags(), + condition:() => this.config.user().role >= UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'file_download', + text:'header.export-questions', + callback:() => this.action.onExportQuestions(), + condition:() => this.config.user().role >= UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'clear', + text:'header.profanity-filter', + callback:() => this.action.onToggleProfanity(), + condition:() => this.config.user().role > UserRole.PARTICIPANT + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'delete_sweep', + iconColor:Palette.RED, + text:'header.delete-questions', + callback:() => this.action.onDeleteQuestions(), + condition:() => this.config.user().role === UserRole.CREATOR + }); + e.menuItem({ + translate:this.config.headerService.getTranslate(), + icon:'delete', + iconColor:Palette.RED, + isSVGIcon:false, + text:'header.delete-room', + callback:() => this.action.onDeleteRoom(), + condition:() => this.config.user().role === UserRole.CREATOR + }); + e.altToggle( + { + translate:this.config.headerService.getTranslate(), + text:'header.block', + icon:'block', + iconColor:Palette.RED + }, + { + translate:this.config.headerService.getTranslate(), + text:'header.unlock', + icon:'block', + iconColor:Palette.RED, + color:Palette.RED + }, + ArsObserver.build<boolean>(e => { + e.set(this.config.room().questionsBlocked); + e.onChange(a => { + this.config.room().questionsBlocked = a.get(); + this.config.roomService.updateRoom(this.config.room()).subscribe(); + if (a.get()){ + this.config.headerService.getTranslate().get('header.questions-blocked').subscribe(msg => { + this.config.headerService.getNotificationService().show(msg); + }); + } + else { + this.config.headerService.getTranslate().get('header.questions-unblocked').subscribe(msg => { + this.config.headerService.getNotificationService().show(msg); + }); + } + }); + }) + , + () => this.config.user().role > UserRole.PARTICIPANT + ); + }); + this.config.onDestroy.subscribe(() => { + list.forEach(e => e.destroy()); + }); + } + +} diff --git a/src/app/components/shared/room-page/room-page.component.ts b/src/app/components/shared/room-page/room-page.component.ts index b9bb29d8b0b168f22db6f859113ce9acbe10d480..75646e1ef38f157c0e25bfc20c9cf071c8bacbc3 100644 --- a/src/app/components/shared/room-page/room-page.component.ts +++ b/src/app/components/shared/room-page/room-page.component.ts @@ -24,6 +24,7 @@ export class RoomPageComponent implements OnInit, OnDestroy { protected commentWatch: Observable<IMessage>; protected listenerFn: () => void; public commentCounterEmit: EventEmitter<number> = new EventEmitter<number>(); + public encodedShortId:string; constructor(protected roomService: RoomService, protected route: ActivatedRoute, @@ -37,6 +38,7 @@ export class RoomPageComponent implements OnInit, OnDestroy { ngOnInit() { this.route.params.subscribe(params => { this.initializeRoom(params['shortId']); + this.encodedShortId = params['shortId']; }); } diff --git a/src/assets/i18n/home/de.json b/src/assets/i18n/home/de.json index 4facef22093e71b43bda58391e50df258c1ac90c..388007efe4e564f79bd7363d90c683fbe55fc5d8 100644 --- a/src/assets/i18n/home/de.json +++ b/src/assets/i18n/home/de.json @@ -123,6 +123,7 @@ "tag-cloud-config": "Aussehen & Animation", "tag-cloud-administration": "Einstellungen & Suche", "questions-blocked": "Neue Fragen deaktiviert ", + "questions-unblocked": "Neue Fragen aktiviert", "overview-question-tooltip": "Anzahl Fragen", "overview-questioners-tooltip": "Anzahl Fragensteller", "overview-keywords-tooltip": "Anzahl Stichwörter", diff --git a/src/assets/i18n/home/en.json b/src/assets/i18n/home/en.json index d736f132fbe8fd1664b2cf12ea65d8b44a0c5fed..4a5f8d6f2d801676af872e66a313664ce8277641 100644 --- a/src/assets/i18n/home/en.json +++ b/src/assets/i18n/home/en.json @@ -110,6 +110,7 @@ "tag-cloud-config": "Modify cloud view", "tag-cloud-administration": "Edit cloud topics", "questions-blocked": "New questions blocked", + "questions-unblocked": "New questions allowed", "overview-question-tooltip": "Number of questions", "overview-questioners-tooltip": "Number of questioners", "overview-keywords-tooltip": "Number of Keywords",