Skip to content
Snippets Groups Projects
Commit 4deeec96 authored by Tom Käsler's avatar Tom Käsler
Browse files

Add answer feature to comment

parent cbe2fa4c
Branches
Tags
No related merge requests found
Showing
with 271 additions and 3 deletions
<div fxLayout="column" fxLayoutAlign="center" fxLayoutGap="20px">
<mat-form-field>
<textarea (focus)="eventService.makeFocusOnInputTrue()" (blur)="eventService.makeFocusOnInputFalse()" matInput #commentBody matTextareaAutosize matAutosizeMinRows=5 matAutosizeMaxRows=10 maxlength="250"
[formControl]="bodyForm"></textarea>
<mat-placeholder class="placeholder">{{ 'comment-answer-form.answer' | translate }}</mat-placeholder>
<mat-hint align="end"><span aria-hidden="true">{{commentBody.value.length}} / 250</span></mat-hint>
</mat-form-field>
</div>
<app-dialog-action-buttons
buttonsLabelSection="comment-page"
confirmButtonLabel="send"
[cancelButtonClickAction]="buildCloseDialogActionCallback()"
[confirmButtonClickAction]="buildCreateCommentActionCallback(commentBody)"
></app-dialog-action-buttons>
button {
min-width: 80px;
}
form {
display: block;
width: 100%;
max-width: 800px;
margin-bottom: 50px;
}
app-comment-list {
width: 100%;
max-width: 800px;
}
textarea {
line-height: 120%;
color: var(--on-surface);
caret-color: var(--on-surface);
}
.send {
color: var(--on-primary);
background-color: var(--primary);
}
mat-hint {
color: var(--on-surface) !important;
}
.mat-select-value-text {
color: var(--on-surface);
caret-color: var(--on-surface);
}
.placeholder {
color: var(--on-surface);
}
::ng-deep .mat-form-field-label {
color: var(--on-surface)!important;
}
::ng-deep .mat-form-field-underline {
background-color: var(--on-surface)!important;
}
::ng-deep .mat-form-field-ripple {
background-color: var(--on-surface)!important;
}
::ng-deep .mat-select-arrow-wrapper .mat-select-arrow {
color: var(--on-surface);
}
::ng-deep .mat-select-value-text {
color: var(--on-surface);
}
::ng-deep .mat-primary .mat-option.mat-selected:not(.mat-option-disabled) {
color: var(--primary);
}
\ No newline at end of file
import { Component, Inject, OnInit } from '@angular/core';
import { Comment } from '../../../../models/comment';
import { NotificationService } from '../../../../services/util/notification.service';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';
import { FormControl, Validators } from '@angular/forms';
import { CommentListComponent } from '../../../shared/comment-list/comment-list.component';
import { EventService } from '../../../../services/util/event.service';
@Component({
selector: 'app-comment-answer-form',
templateUrl: './comment-answer-form.component.html',
styleUrls: ['./comment-answer-form.component.scss']
})
export class CommentAnswerFormComponent implements OnInit {
comment: Comment;
bodyForm = new FormControl('', [Validators.required]);
constructor(
private notification: NotificationService,
public dialogRef: MatDialogRef<CommentListComponent>,
private translateService: TranslateService,
public dialog: MatDialog,
private translationService: TranslateService,
public eventService: EventService,
@Inject(MAT_DIALOG_DATA) public data: any) {
}
ngOnInit() {
this.translateService.use(localStorage.getItem('currentLang'));
}
onNoClick(): void {
this.dialogRef.close();
}
checkInputData(body: string): boolean {
body = body.trim();
if (!body) {
this.translationService.get('comment-page.error-comment').subscribe(message => {
this.notification.show(message);
});
return false;
}
return true;
}
closeDialog(body: string) {
if (this.checkInputData(body) === true) {
this.dialogRef.close(body);
}
}
/**
* Returns a lambda which closes the dialog on call.
*/
buildCloseDialogActionCallback(): () => void {
return () => this.onNoClick();
}
/**
* Returns a lambda which executes the dialog dedicated action on call.
*/
buildCreateCommentActionCallback(text: HTMLInputElement): () => void {
return () => this.closeDialog(text.value);
}
}
......@@ -29,6 +29,7 @@ import { TagsComponent } from './_dialogs/tags/tags.component';
import { ModeratorDeleteComponent } from './_dialogs/moderator-delete/moderator-delete.component';
import { DeleteCommentComponent } from './_dialogs/delete-comment/delete-comment.component';
import { DeleteCommentsComponent } from './_dialogs/delete-comments/delete-comments.component';
import { CommentAnswerFormComponent } from './_dialogs/comment-answer-form/comment-answer-form.component';
import { BonusDeleteComponent } from './_dialogs/bonus-delete/bonus-delete.component';
import { MarkdownModule } from 'ngx-markdown';
......@@ -71,6 +72,7 @@ import { MarkdownModule } from 'ngx-markdown';
DeleteCommentsComponent,
DeleteCommentComponent,
BonusDeleteComponent,
CommentAnswerFormComponent,
],
exports: [],
entryComponents: [
......@@ -91,7 +93,8 @@ import { MarkdownModule } from 'ngx-markdown';
TagsComponent,
DeleteCommentsComponent,
DeleteCommentComponent,
BonusDeleteComponent
BonusDeleteComponent,
CommentAnswerFormComponent
]
})
export class CreatorModule {
......
<p class="comment-answer">
{{ answer }}
</p>
.comment-answer {
color: var(--on-surface);
font-size: 140%;
text-align: start;
white-space: pre-line;
hyphens: auto;
overflow-wrap: anywhere !important;
}
import { Component, Inject, OnInit } from '@angular/core';
import { Comment } from '../../../../models/comment';
import { NotificationService } from '../../../../services/util/notification.service';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material';
import { TranslateService } from '@ngx-translate/core';
import { FormControl, Validators } from '@angular/forms';
import { User } from '../../../../models/user';
import { CommentListComponent } from '../../comment-list/comment-list.component';
import { EventService } from '../../../../services/util/event.service';
@Component({
selector: 'app-comment-answer-text',
templateUrl: './comment-answer-text.component.html',
styleUrls: ['./comment-answer-text.component.scss']
})
export class CommentAnswerTextComponent implements OnInit {
answer: string;
constructor(
private notification: NotificationService,
public dialogRef: MatDialogRef<CommentListComponent>,
private translateService: TranslateService,
public dialog: MatDialog,
private translationService: TranslateService,
public eventService: EventService,
@Inject(MAT_DIALOG_DATA) public data: any) {
}
ngOnInit() {
this.translateService.use(localStorage.getItem('currentLang'));
}
onNoClick(): void {
this.dialogRef.close();
}
}
......@@ -217,6 +217,8 @@ export class CommentListComponent implements OnInit {
return el.id !== payload.id;
});
}
case this.tag:
this.comments[i].tag = <string>value;
}
}
}
......
......@@ -17,6 +17,19 @@
</mat-icon>
</button>
<span class="fill-remaining-space"></span>
<button mat-icon-button *ngIf="(isCreator || isModerator) && !comment.answer" (click)="openAnswerDialog()"
tabindex="0">
<mat-icon class="not-marked" matTooltip="{{ 'comment-page.answer' | translate }}">comment
</mat-icon>
</button>
<button mat-icon-button *ngIf="comment.answer" (click)="openAnswerTextDialog()"
tabindex="0">
<mat-icon matTooltip="{{ 'comment-page.has-answer' | translate }}">comment
</mat-icon>
</button>
<button mat-icon-button *ngIf="(!isStudent || comment.correct === 1) && !moderator" [disabled]="isStudent"
(click)="markCorrect(comment, 1)" tabindex="0" attr.aria-labelledby="comment_correct{{ comment.id }}">
<mat-icon [ngClass]="{'correct-icon' : comment.correct === 1,
......
......@@ -10,9 +10,11 @@ import { TranslateService } from '@ngx-translate/core';
import { LanguageService } from '../../../services/util/language.service';
import { WsCommentServiceService } from '../../../services/websockets/ws-comment-service.service';
import { PresentCommentComponent } from '../_dialogs/present-comment/present-comment.component';
import { CommentAnswerTextComponent } from '../_dialogs/comment-answer-text/comment-answer-text.component';
import { MatDialog } from '@angular/material';
import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
import { DeleteCommentComponent } from '../../creator/_dialogs/delete-comment/delete-comment.component';
import { CommentAnswerFormComponent } from '../../creator/_dialogs/comment-answer-form/comment-answer-form.component';
import { CorrectWrong } from '../../../models/correct-wrong.enum';
import { UserRole } from '../../../models/user-roles.enum';
import { Rescale } from '../../../models/rescale';
......@@ -158,6 +160,26 @@ export class CommentComponent implements OnInit {
});
}
openAnswerDialog(): void {
const dialogRef = this.dialog.open(CommentAnswerFormComponent, {
width: '400px'
});
dialogRef.afterClosed()
.subscribe(result => {
this.wsCommentService.answer(this.comment, result);
this.translateService.get('comment-list.comment-answered').subscribe(msg => {
this.notification.show(msg);
});
});
}
openAnswerTextDialog(): void {
const dialogRef = this.dialog.open(CommentAnswerTextComponent, {
width: '400px'
});
dialogRef.componentInstance.answer = this.comment.answer;
}
delete(): void {
this.commentService.deleteComment(this.comment.id).subscribe(room => {
this.translateService.get('comment-list.comment-deleted').subscribe(msg => {
......
......@@ -26,6 +26,7 @@ import { CommentComponent } from './comment/comment.component';
import { CreateCommentComponent } from './_dialogs/create-comment/create-comment.component';
import { PresentCommentComponent } from './_dialogs/present-comment/present-comment.component';
import { DeleteAccountComponent } from './_dialogs/delete-account/delete-account.component';
import { CommentAnswerTextComponent } from './_dialogs/comment-answer-text/comment-answer-text.component';
import { DialogActionButtonsComponent } from './dialog/dialog-action-buttons/dialog-action-buttons.component';
import { MatRippleModule } from '@angular/material';
......@@ -62,6 +63,7 @@ import { MatRippleModule } from '@angular/material';
CreateCommentComponent,
PresentCommentComponent,
DeleteAccountComponent,
CommentAnswerTextComponent,
DialogActionButtonsComponent
],
exports: [
......@@ -92,7 +94,8 @@ import { MatRippleModule } from '@angular/material';
CreateCommentComponent,
PresentCommentComponent,
DeleteAccountComponent,
UserBonusTokenComponent
UserBonusTokenComponent,
CommentAnswerTextComponent
]
})
export class SharedModule {
......
......@@ -15,6 +15,7 @@ export class Comment {
highlighted: boolean;
ack: boolean;
tag: string;
answer: string;
constructor(roomId: string = '',
creatorId: string = '',
......@@ -27,7 +28,8 @@ export class Comment {
createdFromLecturer = false,
highlighted: boolean = false,
ack: boolean = true,
tag: string = '') {
tag: string = '',
answer: string = '') {
this.id = '';
this.roomId = roomId;
this.creatorId = creatorId;
......@@ -42,5 +44,6 @@ export class Comment {
this.highlighted = highlighted;
this.ack = ack;
this.tag = tag;
this.answer = answer;
}
}
......@@ -24,6 +24,14 @@ export class WsCommentServiceService {
this.wsConnector.send(`/queue/comment.command.create`, JSON.stringify(message));
}
answer(comment: Comment, answer: string): Comment {
comment.answer = answer;
const changes = new TSMap<string, any>();
changes.set('answer', comment.answer);
this.patchComment(comment, changes);
return comment;
}
toggleRead(comment: Comment): Comment {
console.log(comment);
comment.read = !comment.read;
......
......@@ -18,6 +18,7 @@
"add-comment": "Frage stellen",
"pause-comments": "Pausiere den Fragen-Stream",
"play-comments": "Starte den Fragen-Stream",
"comment-answered": "Antwort abgesendet",
"comment-stream-stopped": "Der Fragen-Stream wurde gestoppt.",
"comment-stream-started": "Der Fragen-Stream wurde gestartet.",
"comment-sent": "Die Frage wurde veröffentlicht.",
......@@ -65,6 +66,7 @@
"abort": "Abbrechen",
"ask-question-description": "Gib hier deine Frage ans Auditorium ein!",
"acknowledge": "Lässt die Frage zu und setzt sie zurück auf die öffentliche Liste",
"answer": "Antworte auf die Frage",
"cancel": "Abbrechen",
"cancel-description": "Abbrechen",
"comment": "Die Frage {{ comment }} wurde um {{ time }} Uhr gestellt und hat derzeitig {{ votes }}. {{correct}} {{wrong}} {{bonus}} {{beamer}}",
......@@ -79,6 +81,7 @@
"exit-description": "Präsentationsmodus verlassen",
"export": "Exportieren",
"export-description": "Exportieren",
"has-answer": "Diese Frage wurde beantwortet.",
"live-announcer": "Du befindest dich jetzt auf der Fragen-Seite. Um Informationen zu Tastenkombinationen zu erhalten drücke jetzt die Enter-Taste oder rufe die Ansage zu einem späteren Zeitpunkt mit der Escape-Taste auf.",
"live-announcer-moderation": "Du befindest dich jetzt auf der Moderations-Seite. Um Informationen zu Tastenkombinationen zu erhalten drücke jetzt die Enter-Taste oder rufe die Ansage zu einem späteren Zeitpunkt mit der Escape-Taste auf.",
"mark-correct": "Frage bejahen",
......@@ -227,6 +230,11 @@
"token-deleted": "Der Token wurde gelöscht.",
"tokens-deleted": "Alle Tokens dieser Sitzung wurden gelöscht."
},
"comment-answer-form": {
"answer": "Gib eine Antwort.",
"send": "Senden",
"cancel": "Abbrechen"
},
"session": {
"a11y-description": "Gib eine Beschreibung für die Sitzung ein.",
"create-session": "Speichern",
......
......@@ -18,6 +18,7 @@
"add-comment": "Ask a question!",
"pause-comments": "Pause the question stream",
"play-comments": "Start the question stream",
"comment-answered": "Answer has been sent!",
"comment-stream-stopped": "The question stream has been stopped.",
"comment-stream-started": "The question stream has been started.",
"comment-sent": "The question has been published.",
......@@ -65,6 +66,7 @@
"a11y-text_wrong": "Die Frage wurde verneint.",
"abort": "Cancel",
"acknowledge": "Let the question go",
"answer": "Answer to this question",
"ask-question-description": "Enter your question to the audience here!",
"cancel": "Cancel",
"cancel-description": "Cancel",
......@@ -80,6 +82,7 @@
"exit-description": "Exit Presentation Mode",
"export": "Export",
"export-description": "Export",
"has-answer": "This question got answered.",
"live-announcer": "You are now on the questions page. To get information about key combinations press the Enter key or call the announcement later with the Escape key.",
"live-announcer-moderation": "You are now on the moderation page. To get information about key combinations press the Enter key or call the announcement later with the Escape key.",
"mark-correct": "Mark as correct",
......@@ -228,6 +231,9 @@
"token-deleted": "Token has been deleted.",
"tokens-deleted": "All tokens of this sessions have been deleted."
},
"comment-answer-form": {
"answer": "Give an answer."
},
"session": {
"a11y-description": "Enter a description for the session",
"create-session": "Create session",
......
......@@ -75,6 +75,7 @@
"error-both-fields": "Bitte fülle alle Felder aus.",
"error-comment": "Bitte gib deine Frage ein.",
"error-title": "Bitte gib einen Titel ein.",
"has-answer": "Diese Frage wurde beantwortet.",
"exit-description": "Präsentationsmodus verlassen",
"live-announcer": "Du befindest dich jetzt auf der Fragen-Seite. Um Informationen zu Tastenkombinationen zu erhalten drücke jetzt die Enter-Taste oder rufe die Ansage zu einem späteren Zeitpunkt mit der Escape-Taste auf.",
"mark-not-correct": "Dozent hat die Frage bejaht.",
......
......@@ -76,6 +76,7 @@
"error-comment": "Please enter a question.",
"error-title": "Please enter a title.",
"exit-description": "Exit Presentation Mode",
"has-answer": "This question got answered.",
"live-announcer": "You are now on the questions page. To get information about key combinations press the Enter key or call the announcement later with the Escape key.",
"mark-not-correct": "Marked as correct by the professor",
"mark-not-favorite": "Bonus question: Your professor intends to give a bonus for that question.",
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment