diff --git a/angular.json b/angular.json index 31f04944fd9bafa1df96f4a112516d30fd47097e..a01348f9a136f5de4bc054c13803727cee986091 100644 --- a/angular.json +++ b/angular.json @@ -76,7 +76,7 @@ "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "arsnova-angular-frontend:build", - "proxyConfig": "proxy.conf.json" + "proxyConfig": "proxy.conf.js" }, "configurations": { "production": { diff --git a/proxy.conf.json b/proxy.conf.js similarity index 67% rename from proxy.conf.json rename to proxy.conf.js index 6aa7e2adcba2940d5444320a585b4fbedcb387ba..94414ef324d14a7934ae79c17326c9e896f7baff 100644 --- a/proxy.conf.json +++ b/proxy.conf.js @@ -1,45 +1,58 @@ -{ - "/languagetool": { - "target": "https://lt.frag.jetzt/v2/check", - "secure": true, - "changeOrigin": true, - "pathRewrite": { - "^/languagetool": "" - }, - "logLevel": "debug" - }, - "/spacy": { - "target": "https://spacy.frag.jetzt/spacy", - "secure": true, - "changeOrigin": true, - "pathRewrite": { - "^/spacy": "" - }, - "logLevel": "debug" - }, - "/api/ws/websocket": { - "target": "ws://localhost:8080", - "secure": false, - "pathRewrite": { - "^/api": "" - }, - "ws": true, - "logLevel": "debug" - }, - "/api/roomsubscription": { - "target": "http://localhost:8080", - "secure": false, - "pathRewrite": { - "^/api": "" - }, - "logLevel": "debug" - }, - "/api": { - "target": "http://localhost:8888", - "secure": false, - "pathRewrite": { - "^/api": "" - }, - "logLevel": "debug" - } -} +const PROXY_CONFIG = { + "/deepl": { + "target": "https://api-free.deepl.com/v2", + "secure": true, + "changeOrigin": true, + "logLevel": "debug", + "router": function (req) { + const DEEPL_API_KEY = 'DEEPL_API_KEY'; + req.url = req.url.substr(6) + '&auth_key=' + DEEPL_API_KEY; + return 'https://api-free.deepl.com/v2'; + } + }, + "/languagetool": { + "target": "https://lt.frag.jetzt/v2/check", + "secure": true, + "changeOrigin": true, + "pathRewrite": { + "^/languagetool": "" + }, + "logLevel": "debug" + }, + "/spacy": { + "target": "https://spacy.frag.jetzt/spacy", + "secure": true, + "changeOrigin": true, + "pathRewrite": { + "^/spacy": "" + }, + "logLevel": "debug" + }, + "/api/ws/websocket": { + "target": "ws://localhost:8080", + "secure": false, + "pathRewrite": { + "^/api": "" + }, + "ws": true, + "logLevel": "debug" + }, + "/api/roomsubscription": { + "target": "http://localhost:8080", + "secure": false, + "pathRewrite": { + "^/api": "" + }, + "logLevel": "debug" + }, + "/api": { + "target": "http://localhost:8888", + "secure": false, + "pathRewrite": { + "^/api": "" + }, + "logLevel": "debug" + } +}; + +module.exports = PROXY_CONFIG; diff --git a/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.html b/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.html index 14641669e982af754bd533ac273d5506f11cdabc..978a830181d30b3304dbf923111f9253c1f4cd42 100644 --- a/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.html +++ b/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.html @@ -1,38 +1,11 @@ <div mat-dialog-content *ngIf="editRoom"> <h2 class="oldtypo-h2">{{ 'room-page.description' | translate }}</h2> - <mat-tab-group> - <mat-tab label="{{'room-page.tab1' | translate}}"> - <mat-form-field class="input-block" style="width:100%;min-width:100%;"> - <textarea - (focus)="eventService.makeFocusOnInputTrue()" - (blur)="eventService.makeFocusOnInputFalse()" - [(ngModel)]="editRoom.description" - matInput - matTextareaAutosize - matAutosizeMinRows="2" - matAutosizeMaxRows="5" - maxlength="500" - name="description" - aria-labelledby="description" - ></textarea> - <mat-hint align="end"> - <span aria-hidden="true"> - {{ editRoom.description?editRoom.description.length:0 }} / 500 - </span> - </mat-hint> - </mat-form-field> - </mat-tab> - <mat-tab label="{{'session.preview' | translate}}" - [disabled]="!editRoom.description"> - <app-custom-markdown class="images" [data]="editRoom.description"></app-custom-markdown> - </mat-tab> - </mat-tab-group> + <app-write-comment [isModerator]="true" + [placeholder]="''" + [onClose]="buildCloseDialogActionCallback()" + [onSubmit]="buildSaveActionCallback()" + [i18nSection]="'room-page'" + [confirmLabel]="'update'"> + </app-write-comment> </div> -<app-dialog-action-buttons - buttonsLabelSection="room-page" - confirmButtonLabel="update" - - [cancelButtonClickAction]="buildCloseDialogActionCallback()" - [confirmButtonClickAction]="buildSaveActionCallback()" -></app-dialog-action-buttons> diff --git a/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.ts b/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.ts index cd5f3ac74bf44d7f926629a15046dadc5bebdfc1..1006684aa26ca7e6f900d42ad02ed35504193fda 100644 --- a/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.ts +++ b/src/app/components/creator/_dialogs/room-description-settings/room-description-settings.component.ts @@ -1,22 +1,25 @@ -import {Component,Inject,OnInit} from '@angular/core'; -import {MAT_DIALOG_DATA,MatDialog,MatDialogRef} from '@angular/material/dialog'; -import {RoomCreatorPageComponent} from '../../room-creator-page/room-creator-page.component'; -import {NotificationService} from '../../../../services/util/notification.service'; -import {TranslateService} from '@ngx-translate/core'; -import {RoomService} from '../../../../services/http/room.service'; -import {Router} from '@angular/router'; -import {EventService} from '../../../../services/util/event.service'; -import {ProfanityFilter,Room} from '../../../../models/room'; -import {FormControl,Validators} from '@angular/forms'; +import { AfterViewInit, Component, Inject, ViewChild } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { RoomCreatorPageComponent } from '../../room-creator-page/room-creator-page.component'; +import { NotificationService } from '../../../../services/util/notification.service'; +import { TranslateService } from '@ngx-translate/core'; +import { RoomService } from '../../../../services/http/room.service'; +import { Router } from '@angular/router'; +import { EventService } from '../../../../services/util/event.service'; +import { Room } from '../../../../models/room'; +import { FormControl, Validators } from '@angular/forms'; +import { WriteCommentComponent } from '../../../shared/write-comment/write-comment.component'; @Component({ - selector:'app-room-description-settings', - templateUrl:'./room-description-settings.component.html', - styleUrls:['./room-description-settings.component.scss'] + selector: 'app-room-description-settings', + templateUrl: './room-description-settings.component.html', + styleUrls: ['./room-description-settings.component.scss'] }) -export class RoomDescriptionSettingsComponent implements OnInit{ +export class RoomDescriptionSettingsComponent implements AfterViewInit { + + @ViewChild(WriteCommentComponent) writeComment: WriteCommentComponent; editRoom: Room; - roomNameFormControl=new FormControl('',[Validators.required,Validators.minLength(3),Validators.maxLength(30)]); + roomNameFormControl = new FormControl('', [Validators.required, Validators.minLength(3), Validators.maxLength(30)]); constructor(public dialogRef: MatDialogRef<RoomCreatorPageComponent>, public dialog: MatDialog, @@ -25,30 +28,34 @@ export class RoomDescriptionSettingsComponent implements OnInit{ protected roomService: RoomService, public router: Router, public eventService: EventService, - @Inject(MAT_DIALOG_DATA) public data: any){ + @Inject(MAT_DIALOG_DATA) public data: any) { } - ngOnInit(){ + ngAfterViewInit() { + if (this.editRoom) { + this.writeComment.commentData.currentData = this.editRoom.description; + } } - buildCloseDialogActionCallback(): () => void{ - return ()=>this.closeDialog('abort'); + buildCloseDialogActionCallback(): () => void { + return () => this.closeDialog('abort'); } - buildSaveActionCallback(): () => void{ - return ()=>this.save(); + buildSaveActionCallback(): () => void { + return () => this.save(); } - closeDialog(type: string): void{ + closeDialog(type: string): void { this.dialogRef.close(type); } - save(): void{ - this.roomService.updateRoom(this.editRoom).subscribe(r=>this.editRoom=r); - if(!this.roomNameFormControl.hasError('required') + save(): void { + this.editRoom.description = this.writeComment.commentData.currentData; + this.roomService.updateRoom(this.editRoom).subscribe(r => this.editRoom = r); + if (!this.roomNameFormControl.hasError('required') && !this.roomNameFormControl.hasError('minlength') - && !this.roomNameFormControl.hasError('maxlength')){ + && !this.roomNameFormControl.hasError('maxlength')) { this.closeDialog('update'); } this.closeDialog('update'); diff --git a/src/app/components/creator/_dialogs/room-edit/room-edit.component.html b/src/app/components/creator/_dialogs/room-edit/room-edit.component.html index cd4de45f001a5760ce489a3f58dc13be51464565..13cc82a372f6d9187fbcf9e88c383060051e2075 100644 --- a/src/app/components/creator/_dialogs/room-edit/room-edit.component.html +++ b/src/app/components/creator/_dialogs/room-edit/room-edit.component.html @@ -46,7 +46,7 @@ </mat-tab> <mat-tab label="{{'session.preview' | translate}}" [disabled]="!editRoom.description"> - <app-custom-markdown class="images" [data]="editRoom.description"></app-custom-markdown> + <app-view-comment-data class="images" [currentData]="editRoom.description"></app-view-comment-data> </mat-tab> </mat-tab-group> <div fxLayout="column"> 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 b4d745512dc7295038fc4f211966c104a1fe46da..3fdd9b3c900921f0004752f7bbbbe02a727ff03c 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 @@ -65,7 +65,7 @@ </div> <mat-card-content *ngIf="room.description" fxLayoutAlign="center"> - <app-custom-markdown class="images" [data]="room.description.trim()"></app-custom-markdown> + <app-view-comment-data class="images" [currentData]="room.description"></app-view-comment-data> </mat-card-content> <div fxLayout="column" fxLayoutAlign="center" 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 6f4b6d344027f77a976eb6fec79a2dc081090a5c..a783278555b4dc61ce7ac3558d8d9a7f530f32e3 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,8 +1,8 @@ import { Component, OnInit, Renderer2, OnDestroy, AfterContentInit } from '@angular/core'; import { RoomService } from '../../../services/http/room.service'; -import {ActivatedRoute,Router} from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { RoomPageComponent } from '../../shared/room-page/room-page.component'; -import {ProfanityFilter,Room} from '../../../models/room'; +import { Room } from '../../../models/room'; import { CommentSettingsDialog } from '../../../models/comment-settings-dialog'; import { Location } from '@angular/common'; import { NotificationService } from '../../../services/util/notification.service'; @@ -10,7 +10,6 @@ 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 { TSMap } from 'typescript-map'; import { WsCommentService } from '../../../services/websockets/ws-comment.service'; import { CommentService } from '../../../services/http/comment.service'; import { ModeratorsComponent } from '../_dialogs/moderators/moderators.component'; @@ -26,14 +25,10 @@ import { DeleteCommentsComponent } from '../_dialogs/delete-comments/delete-comm 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 {HeaderService} from '../../../services/util/header.service'; -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 {ModeratorDeleteComponent} from '../_dialogs/moderator-delete/moderator-delete.component'; -import {ModeratorCommentPageComponent} from '../../moderator/moderator-comment-page/moderator-comment-page.component'; -import {ModeratorCommentListComponent} from '../../moderator/moderator-comment-list/moderator-comment-list.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'; @Component({ selector: 'app-room-creator-page', @@ -110,9 +105,12 @@ export class RoomCreatorPageComponent extends RoomPageComponent implements OnIni editSessionDescription(){ const dialogRef = this.dialog.open(RoomDescriptionSettingsComponent, { - width: '400px' + width: '900px', + maxWidth: 'calc( 100% - 50px )', + maxHeight: 'calc( 100vh - 50px )', + autoFocus: false }); - dialogRef.componentInstance.editRoom=this.room; + dialogRef.componentInstance.editRoom = this.room; } exportQuestions(){ diff --git a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts index 854f7595a5f0ec417b4cb034b2964583ac93b828..b062e1ea34d777f5dcfbd7785adb485c5c9bd8d9 100644 --- a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts +++ b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts @@ -19,7 +19,6 @@ import { ModeratorsComponent } from '../../creator/_dialogs/moderators/moderator import { TagsComponent } from '../../creator/_dialogs/tags/tags.component'; import { DeleteCommentsComponent } from '../../creator/_dialogs/delete-comments/delete-comments.component'; import { Export } from '../../../models/export'; -import { CreateCommentComponent } from '../../shared/_dialogs/create-comment/create-comment.component'; import { NotificationService } from '../../../services/util/notification.service'; import { BonusTokenService } from '../../../services/http/bonus-token.service'; import { CommentFilter, Period } from '../../../utils/filter-options'; 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 index 61a847b46dc6ed9dac012768edb59457c3e5256f..e41c74dc5c36e13c62bf52337bd2a8706f481a53 100644 --- 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 @@ -34,7 +34,7 @@ <mat-card-content *ngIf="room.description" fxLayoutAlign="center"> - <app-custom-markdown class="images" [data]="room.description.trim()"></app-custom-markdown> + <app-view-comment-data class="images" [currentData]="room.description"></app-view-comment-data> </mat-card-content> <div fxLayout="column" fxLayoutAlign="center" diff --git a/src/app/components/participant/room-participant-page/room-participant-page.component.html b/src/app/components/participant/room-participant-page/room-participant-page.component.html index 5da0230922f98d2fa9238d034ffd97aea4cbaa39..86b613e8beb52e5278a21d6cd99b937b911abc6a 100644 --- a/src/app/components/participant/room-participant-page/room-participant-page.component.html +++ b/src/app/components/participant/room-participant-page/room-participant-page.component.html @@ -21,7 +21,7 @@ </div> <mat-card-content *ngIf="room.description" fxLayoutAlign="center"> - <app-custom-markdown class="images" [data]="room.description.trim()"></app-custom-markdown> + <app-view-comment-data class="images" [currentData]="room.description"></app-view-comment-data> </mat-card-content> <mat-grid-list cols="1" rowHeight="2:1"> <mat-grid-tile> diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.html b/src/app/components/shared/_dialogs/create-comment/create-comment.component.html index 3dc3a4528245855d1a48f62e446ef04c7755b6b0..25933dfdbd6d57e587884afc93afaeef630e203d 100644 --- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.html +++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.html @@ -4,6 +4,6 @@ [onClose]="this.onNoClick.bind(this)" [isSpinning]="isSendingToSpacy" [tags]="tags" - [user]="user"> + [isModerator]="user && user.role > 0"> </app-write-comment> </ars-row> 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 00d57e8ff6115ec5d362d3ef3163b564edbd5b06..6d2293ef26933cb45f39916938efdf56e5fa54ba 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 @@ -4,12 +4,13 @@ import { NotificationService } from '../../../../services/util/notification.serv import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; import { User } from '../../../../models/user'; -import { CommentListComponent } from '../../comment-list/comment-list.component'; import { EventService } from '../../../../services/util/event.service'; import { SpacyDialogComponent } from '../spacy-dialog/spacy-dialog.component'; import { LanguagetoolService, Language } from '../../../../services/http/languagetool.service'; import { CreateCommentKeywords } from '../../../../utils/create-comment-keywords'; import { WriteCommentComponent } from '../../write-comment/write-comment.component'; +import { DeepLDialogComponent } from '../deep-ldialog/deep-ldialog.component'; +import { DeepLService } from '../../../../services/http/deep-l.service'; @Component({ selector: 'app-submit-comment', @@ -19,7 +20,6 @@ import { WriteCommentComponent } from '../../write-comment/write-comment.compone export class CreateCommentComponent implements OnInit { @ViewChild(WriteCommentComponent) commentComponent: WriteCommentComponent; - comment: Comment; user: User; roomId: string; tags: string[]; @@ -27,11 +27,12 @@ export class CreateCommentComponent implements OnInit { constructor( private notification: NotificationService, - public dialogRef: MatDialogRef<CommentListComponent>, + public dialogRef: MatDialogRef<CreateCommentComponent>, private translateService: TranslateService, public dialog: MatDialog, public languagetoolService: LanguagetoolService, public eventService: EventService, + private deeplService: DeepLService, @Inject(MAT_DIALOG_DATA) public data: any) { } @@ -51,7 +52,32 @@ export class CreateCommentComponent implements OnInit { comment.createdFromLecturer = this.user.role > 0; comment.tag = tag; this.isSendingToSpacy = true; - this.openSpacyDialog(comment, text); + this.openDeeplDialog(body, text, (newBody: string, newText: string) => { + comment.body = newBody; + this.openSpacyDialog(comment, newText); + }); + } + + openDeeplDialog(body: string, text: string, onClose: (data: string, text: string) => void) { + this.deeplService.improveTextStyle(text).subscribe(improvedText => { + this.isSendingToSpacy = false; + this.dialog.open(DeepLDialogComponent, { + width: '900px', + maxWidth: '100%', + data: { + body, + text, + improvedText + } + }).afterClosed().subscribe((res) => { + if (res) { + onClose(res.body, res.text); + } + }); + }, (_) => { + this.isSendingToSpacy = false; + onClose(body, text); + }); } openSpacyDialog(comment: Comment, rawText: string): void { diff --git a/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.html b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..56e44687aefdc4b495dc8df440c6163d21fe2b3d --- /dev/null +++ b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.html @@ -0,0 +1,25 @@ +<h2>{{'deepl.header' | translate}}</h2> +<div mat-dialog-content> + <label id="deepl-radio-group-label">{{'deepl.label' | translate}}</label> + <br> + <mat-radio-group + aria-labelledby="deepl-radio-group-label" + [(ngModel)]="radioButtonValue"> + <mat-radio-button [value]="normalValue"> + <strong>{{'deepl.option-normal' | translate}}</strong> + <app-view-comment-data [currentData]="normalValue.body"></app-view-comment-data> + </mat-radio-button> + <br> + <mat-radio-button [value]="improvedValue"> + <strong>{{'deepl.option-improved' | translate}}</strong> + <app-view-comment-data [currentData]="improvedValue.body"></app-view-comment-data> + </mat-radio-button> + </mat-radio-group> +</div> + +<app-dialog-action-buttons + [buttonsLabelSection]="'comment-page'" + [confirmButtonLabel]="'continue'" + [cancelButtonClickAction]="buildCloseDialogActionCallback()" + [confirmButtonClickAction]="buildSubmitBodyActionCallback()"> +</app-dialog-action-buttons> diff --git a/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.scss b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..a667f841e767403a0ef2706b9ec756474a0afdde --- /dev/null +++ b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.scss @@ -0,0 +1,20 @@ +::ng-deep { + + mat-radio-button { + width: 100%; + } + + .mat-radio-label { + width: calc(100% - 16px) !important; + } + + .mat-radio-label-content { + width: 100%; + } + + app-view-comment-data > div { + background: var(--surface); + border-radius: 10px; + padding: 7px; + } +} diff --git a/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.spec.ts b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4b6a83115767f3ee8f5269d603c269f4a6ed2218 --- /dev/null +++ b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.spec.ts @@ -0,0 +1,26 @@ +/*import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeepLDialogComponent } from './deep-ldialog.component'; + +describe('DeepLDialogComponent', () => { + let component: DeepLDialogComponent; + let fixture: ComponentFixture<DeepLDialogComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DeepLDialogComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DeepLDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); + */ diff --git a/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.ts b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a835d9ec84cb57fee7ac368541910baf775652b --- /dev/null +++ b/src/app/components/shared/_dialogs/deep-ldialog/deep-ldialog.component.ts @@ -0,0 +1,90 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ViewCommentDataComponent } from '../../view-comment-data/view-comment-data.component'; + +interface ResultValue { + body: string; + text: string; +} + +@Component({ + selector: 'app-deep-ldialog', + templateUrl: './deep-ldialog.component.html', + styleUrls: ['./deep-ldialog.component.scss'] +}) +export class DeepLDialogComponent implements OnInit { + + radioButtonValue: ResultValue; + normalValue: ResultValue; + improvedValue: ResultValue; + + constructor( + private dialogRef: MatDialogRef<DeepLDialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: any) { + } + + ngOnInit(): void { + this.normalValue = { + body: this.data.body, + text: this.data.text + }; + const sentences = this.data.improvedText.split('\n').filter(sent => sent.length > 0); + const delta = ViewCommentDataComponent.getDeltaFromData(this.data.body); + if (delta === null) { + setTimeout(() => this.dialogRef.close(this.normalValue)); + return; + } + const ops = delta.ops; + let i = 0; + let sentenceIndex = 0; + let lastFoundIndex = -1; + for (; i < ops.length && sentenceIndex < sentences.length; i++) { + const data = ops[i]['insert']; + if (typeof data !== 'string') { + continue; + } + if (data === '\n') { + continue; + } + const endsNewline = data.endsWith('\n'); + const mod = (endsNewline ? -1 : 0) + (data.startsWith('\n') ? -1 : 0); + const occurrence = data.split('\n').length + mod; + ops[i]['insert'] = sentences.slice(sentenceIndex, sentenceIndex + occurrence).join('\n') + + (endsNewline ? '\n' : ''); + sentenceIndex += occurrence; + lastFoundIndex = i; + } + for (let j = ops.length - 1; j >= i; j--) { + const data = ops[i]['insert']; + if (data === 'string' && data.trim().length) { + ops.splice(j, 1); + } + } + if (sentenceIndex < sentences.length) { + if (lastFoundIndex < 0) { + setTimeout(() => this.dialogRef.close(this.normalValue)); + return; + } + let data = ops[i]['insert']; + const endsNewline = data.endsWith('\n'); + if (endsNewline) { + data = data.substring(0, data.length - 1); + } + ops[i]['insert'] = data + sentences.slice(sentenceIndex).join('\n') + (endsNewline ? '\n' : ''); + } + this.improvedValue = { + body: ViewCommentDataComponent.getDataFromDelta(delta), + text: this.data.improvedText + }; + this.radioButtonValue = this.normalValue; + } + + buildCloseDialogActionCallback(): () => void { + return () => this.dialogRef.close(); + } + + buildSubmitBodyActionCallback(): () => void { + return () => this.dialogRef.close(this.radioButtonValue); + } + +} diff --git a/src/app/components/shared/_dialogs/present-comment/present-comment.component.html b/src/app/components/shared/_dialogs/present-comment/present-comment.component.html index cf4217111f40b7f7b6b7345310dccb992d4be044..c9fcc94778685b6540fc53efa58300adf728869d 100644 --- a/src/app/components/shared/_dialogs/present-comment/present-comment.component.html +++ b/src/app/components/shared/_dialogs/present-comment/present-comment.component.html @@ -12,7 +12,7 @@ <mat-icon >close</mat-icon> </button> <div id="comment"> - <app-custom-markdown [data]="body"></app-custom-markdown> + <app-view-comment-data [currentData]="body"></app-view-comment-data> </div> <div class="visually-hidden"> diff --git a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts index dd6c33c9308826aa764fcdae8a24ba0b1ebf5dd1..c2bb0e10937c821b378f7f4e0956b0a264424dec 100644 --- a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts +++ b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts @@ -1,7 +1,5 @@ import { AfterContentInit, Component, Inject, OnInit, ViewChild } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; - -import { CreateCommentComponent } from '../create-comment/create-comment.component'; import { SpacyService, SpacyKeyword } from '../../../../services/http/spacy.service'; import { LanguagetoolService } from '../../../../services/http/languagetool.service'; import { Comment } from '../../../../models/comment'; @@ -39,7 +37,7 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit { constructor( protected langService: LanguagetoolService, private spacyService: SpacyService, - public dialogRef: MatDialogRef<CreateCommentComponent>, + public dialogRef: MatDialogRef<SpacyDialogComponent>, @Inject(MAT_DIALOG_DATA) public data) { } diff --git a/src/app/components/shared/comment-answer/comment-answer.component.html b/src/app/components/shared/comment-answer/comment-answer.component.html index 5015e4a847f0e22e8b7b865f841afc17271b8578..14edcc800a77c439679c936abe00a9dfb4e54856 100644 --- a/src/app/components/shared/comment-answer/comment-answer.component.html +++ b/src/app/components/shared/comment-answer/comment-answer.component.html @@ -10,8 +10,9 @@ fxLayoutAlign="center"> <mat-card class="answer border-answer" *ngIf="!isStudent || answer"> - <app-write-comment [user]="user" + <app-write-comment [isModerator]="user && user.role > 0" [isCommentAnswer]="true" + [placeholder]="'comment-page.your-answer'" [onClose]="openDeleteAnswerDialog()" [onSubmit]="saveAnswer()" [disableCancelButton]="!answer && commentComponent && commentComponent.commentData.currentText.length > 0" diff --git a/src/app/components/shared/questionwall/question-wall/question-wall.component.html b/src/app/components/shared/questionwall/question-wall/question-wall.component.html index b5422dc36c252ebfc5abeeb0cb1c1dedc1c9ece5..1b3c1387af097a5752b90bcd9c9d962c743f6b64 100644 --- a/src/app/components/shared/questionwall/question-wall/question-wall.component.html +++ b/src/app/components/shared/questionwall/question-wall/question-wall.component.html @@ -132,9 +132,9 @@ </ars-row> <ars-row> <ars-row class="bound" style="padding:16px 32px 16px 32px;box-sizing:border-box;max-width:100%;"> - <app-custom-markdown [ngStyle]="{'font-size':fontSize+'%'}" + <app-view-comment-data [ngStyle]="{'font-size':fontSize+'%'}" class="questionwall-present-markdown images" - [data]="commentFocus.comment.body"></app-custom-markdown> + [currentData]="commentFocus.comment.body"></app-view-comment-data> </ars-row> </ars-row> <ars-row [height]="50"> @@ -251,7 +251,7 @@ (click)="focusComment(comment)" style="box-sizing:border-box;padding:0 16px;cursor:pointer"> <p class="questionwall-comment-body"> - <app-custom-markdown class="images" [data]="comment.comment.body"></app-custom-markdown> + <app-view-comment-data class="images" [currentData]="comment.comment.body"></app-view-comment-data> </p> </ars-row> <ars-row [height]="50"> diff --git a/src/app/components/shared/questionwall/question-wall/question-wall.component.scss b/src/app/components/shared/questionwall/question-wall/question-wall.component.scss index ab93a6b659338545b3acc70abf95afcb65522c9f..32d942671cba0bc2f74bad8eef29a3690983838e 100644 --- a/src/app/components/shared/questionwall/question-wall/question-wall.component.scss +++ b/src/app/components/shared/questionwall/question-wall/question-wall.component.scss @@ -293,10 +293,6 @@ hr { border: 1px solid yellow; } - - img { - width: 100% !important; - } } .questionwall-present-options-slider { diff --git a/src/app/components/shared/shared.module.ts b/src/app/components/shared/shared.module.ts index 52a78cd959336e7bc6839c700a1bf873a3ec0c75..75057040a34d0062331612f014eddd78c0d7fdd7 100644 --- a/src/app/components/shared/shared.module.ts +++ b/src/app/components/shared/shared.module.ts @@ -51,6 +51,7 @@ import { CustomMarkdownComponent } from './custom-markdown/custom-markdown.compo import { ScrollIntoViewDirective } from '../../directives/scroll-into-view.directive'; import { QuillModule } from 'ngx-quill'; import { ViewCommentDataComponent } from './view-comment-data/view-comment-data.component'; +import { DeepLDialogComponent } from './_dialogs/deep-ldialog/deep-ldialog.component'; @NgModule({ imports: [ @@ -107,7 +108,8 @@ import { ViewCommentDataComponent } from './view-comment-data/view-comment-data. WriteCommentComponent, CustomMarkdownComponent, ScrollIntoViewDirective, - ViewCommentDataComponent + ViewCommentDataComponent, + DeepLDialogComponent ], exports: [ RoomJoinComponent, @@ -130,7 +132,9 @@ import { ViewCommentDataComponent } from './view-comment-data/view-comment-data. JoyrideTemplateDirective, AutofocusDirective, CustomMarkdownComponent, - ScrollIntoViewDirective + ScrollIntoViewDirective, + ViewCommentDataComponent, + WriteCommentComponent ] }) export class SharedModule { diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.ts b/src/app/components/shared/tag-cloud/tag-cloud.component.ts index 6348f420599ff5dfa9ffaca1ced35b424bd2659c..29ca074b931364abc322288bc1e8e52d549d9788 100644 --- a/src/app/components/shared/tag-cloud/tag-cloud.component.ts +++ b/src/app/components/shared/tag-cloud/tag-cloud.component.ts @@ -193,30 +193,14 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit { this.authenticationService.guestLogin(UserRole.PARTICIPANT).subscribe(r => { this.roomService.getRoomByShortId(this.shortId).subscribe(room => { this.room = room; + this.retrieveTagCloudSettings(room); this.roomId = room.id; this._subscriptionRoom = this.wsRoomService.getRoomStream(this.roomId).subscribe(msg => { const message = JSON.parse(msg.body); if (message.type === 'RoomPatched') { this.room.questionsBlocked = message.payload.changes.questionsBlocked; this.room.blacklistIsActive = message.payload.changes.blacklistIsActive; - const data = JSON.parse(message.payload.changes.tagCloudSettings); - if (data) { - const admin = TopicCloudAdminService.getDefaultAdminData; - admin.considerVotes = data.admin.considerVotes; - admin.keywordORfulltext = data.admin.keywordORfulltext; - admin.wantedLabels = data.admin.wantedLabels; - admin.minQuestioners = data.admin.minQuestioners; - admin.minQuestions = data.admin.minQuestions; - admin.minUpvotes = data.admin.minUpvotes; - admin.startDate = data.admin.startDate; - admin.endDate = data.admin.endDate; - data.admin = undefined; - this.topicCloudAdmin.setAdminData(admin, false, this.userRole); - this.setCloudParameters(data as CloudParameters); - } else { - this.resetColorsToTheme(); - this.setCloudParameters(this.currentCloudParameters); - } + this.retrieveTagCloudSettings(message.payload.changes); } }); this.directSend = this.room.directSend; @@ -370,18 +354,39 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit { TopicCloudAdminService.applySettingsToRoom(room); this.room = room; this.roomService.updateRoom(room).subscribe(_ => { - this.translateService.get('topic-cloud.changes-successful').subscribe(msg => { + this.translateService.get('tag-cloud.changes-successful').subscribe(msg => { this.notificationService.show(msg); }); }, error => { - this.translateService.get('topic-cloud.changes-gone-wrong').subscribe(msg => { + this.translateService.get('tag-cloud.changes-gone-wrong').subscribe(msg => { this.notificationService.show(msg); }); }); }); } + private retrieveTagCloudSettings(room: Room) { + const data = JSON.parse(room.tagCloudSettings); + if (data) { + const admin = TopicCloudAdminService.getDefaultAdminData; + admin.considerVotes = data.admin.considerVotes; + admin.keywordORfulltext = data.admin.keywordORfulltext; + admin.wantedLabels = data.admin.wantedLabels; + admin.minQuestioners = data.admin.minQuestioners; + admin.minQuestions = data.admin.minQuestions; + admin.minUpvotes = data.admin.minUpvotes; + admin.startDate = data.admin.startDate; + admin.endDate = data.admin.endDate; + data.admin = undefined; + this.topicCloudAdmin.setAdminData(admin, false, this.userRole); + this.setCloudParameters(data as CloudParameters); + } else { + this.resetColorsToTheme(); + this.setCloudParameters(this.currentCloudParameters); + } + } + private updateAlphabeticalPosition(elements: TagComment[]): void { const sizes = new Array(10); const fontRange = (this._currentSettings.fontSizeMax - this._currentSettings.fontSizeMin) / 10; diff --git a/src/app/components/shared/view-comment-data/view-comment-data.component.html b/src/app/components/shared/view-comment-data/view-comment-data.component.html index 647fe95b42a1a8139aa0555f730318a8fdaef538..df7e8b0c54aca137defbe59559438a755877a67f 100644 --- a/src/app/components/shared/view-comment-data/view-comment-data.component.html +++ b/src/app/components/shared/view-comment-data/view-comment-data.component.html @@ -1,16 +1,12 @@ <ars-row *ngIf="isEditor"> <div #editorErrorLayer id="editorErrorLayer"></div> <quill-editor #editor - [maxLength]="user.role === 3 ? 1000 : 500" - placeholder="{{ 'comment-page.enter-comment' | translate }}" + placeholder="{{ placeHolderText | translate }}" [modules]="quillModules" (document:click)="onDocumentClick($event)"> </quill-editor> <div #tooltipContainer></div> - <div fxLayout="row" style="justify-content: space-between; padding: 0 5px"> - <span aria-hidden="true" style="font-size: 75%"> - {{ 'comment-page.Markdown-hint' | translate }} - </span> + <div fxLayout="row" style="justify-content: flex-end; padding: 0 5px"> <span aria-hidden="true" style="font-size: 75%"> {{currentText.length}} / {{maxTextCharacters}} </span> diff --git a/src/app/components/shared/view-comment-data/view-comment-data.component.scss b/src/app/components/shared/view-comment-data/view-comment-data.component.scss index a9de9fcdc4172ecad0277c0a759b4606b27dde80..db69027a8a8b2b20c0967a8033ce5cfba1432074 100644 --- a/src/app/components/shared/view-comment-data/view-comment-data.component.scss +++ b/src/app/components/shared/view-comment-data/view-comment-data.component.scss @@ -3,6 +3,13 @@ filter: opacity(0.6); } +::ng-deep { + .images .ql-video { + width: 100%; + aspect-ratio: 16 / 9; + } +} + ::ng-deep .ql-snow { :focus { outline-offset: 0; @@ -12,12 +19,22 @@ min-height: 8.8em; } + &.ql-disabled { + .ql-editor { + min-height: unset; + padding: unset; + } + } + .ql-stroke { stroke: var(--on-surface); } .ql-picker { color: var(--on-surface); + font-size: inherit !important; + height: 1.5em !important; + width: 1.75em !important; } .ql-tooltip { @@ -86,10 +103,14 @@ &.ql-container { border-color: var(--on-surface); height: 80%; + font-size: inherit !important; } &.ql-toolbar, .ql-toolbar { border-color: var(--on-surface); + border-top: unset; + border-right: unset; + border-left: unset; .ql-formats { margin-right: 0; @@ -112,6 +133,12 @@ } } + button { + font-size: inherit !important; + height: 1.5em !important; + width: 1.75em !important; + } + button:hover, button:focus, button.ql-active, .ql-picker-label:hover, .ql-picker-label.ql-active, .ql-picker-item:hover, .ql-picker-item.ql-selected { diff --git a/src/app/components/shared/view-comment-data/view-comment-data.component.ts b/src/app/components/shared/view-comment-data/view-comment-data.component.ts index ac59274cf715e20971985300e0c73cb986b194b4..ccf7af27b688a1fec6c6c847d6317e249445ed3c 100644 --- a/src/app/components/shared/view-comment-data/view-comment-data.component.ts +++ b/src/app/components/shared/view-comment-data/view-comment-data.component.ts @@ -1,5 +1,4 @@ import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; -import { User } from '../../../models/user'; import { QuillEditorComponent, QuillModules, QuillViewComponent } from 'ngx-quill'; import Delta from 'quill-delta'; import Quill from 'quill'; @@ -7,6 +6,7 @@ import ImageResize from 'quill-image-resize-module'; import 'quill-emoji/dist/quill-emoji.js'; import { LanguageService } from '../../../services/util/language.service'; import { TranslateService } from '@ngx-translate/core'; +import { DeviceInfoService } from '../../../services/util/device-info.service'; Quill.register('modules/imageResize', ImageResize); @@ -31,9 +31,23 @@ export class ViewCommentDataComponent implements OnInit, AfterViewInit { @ViewChild('editorErrorLayer') editorErrorLayer: ElementRef<HTMLDivElement>; @ViewChild('tooltipContainer') tooltipContainer: ElementRef<HTMLDivElement>; @Input() isEditor = false; - @Input() user: User; - @Input() currentData = ''; + @Input() isModerator = false; + + @Input() + set currentData(data: string) { + this._currentData = data; + if ((this.editor && this.editor.quillEditor) || (this.quillView && this.quillView.quillEditor)) { + this.set(this._currentData); + } + } + + get currentData(): string { + return this._currentData || ''; + } + @Input() maxTextCharacters = 500; + @Input() maxDataCharacters = 1500; + @Input() placeHolderText = ''; @Input() markEvents?: { onCreate: (markContainer: HTMLDivElement, tooltipContainer: HTMLDivElement, editor: QuillEditorComponent) => void; onChange: (delta: any) => void; @@ -52,9 +66,11 @@ export class ViewCommentDataComponent implements OnInit, AfterViewInit { } } }; + private _currentData = null; constructor(private languageService: LanguageService, - private translateService: TranslateService) { + private translateService: TranslateService, + private deviceInfo: DeviceInfoService) { this.languageService.langEmitter.subscribe(lang => { this.translateService.use(lang); if (this.isEditor) { @@ -63,7 +79,7 @@ export class ViewCommentDataComponent implements OnInit, AfterViewInit { }); } - private static getDataFromDelta(contentDelta) { + public static getDataFromDelta(contentDelta) { return JSON.stringify(contentDelta.ops.map(op => { let hasOnlyInsert = true; for (const key in op) { @@ -76,7 +92,7 @@ export class ViewCommentDataComponent implements OnInit, AfterViewInit { })); } - private static getDeltaFromData(jsonData: string) { + public static getDeltaFromData(jsonData: string) { return { ops: JSON.parse(jsonData).map(elem => { if (!elem['insert']) { @@ -89,12 +105,13 @@ export class ViewCommentDataComponent implements OnInit, AfterViewInit { } ngOnInit(): void { - if (this.user && this.user.role > 0) { + if (this.isModerator) { this.quillModules.toolbar['container'] = moderatorToolbar; } + const isMobile = this.deviceInfo.isUserAgentMobile; if (this.isEditor) { - this.quillModules['emoji-toolbar'] = true; - this.quillModules['emoji-shortname'] = true; + this.quillModules['emoji-toolbar'] = !isMobile; + this.quillModules['emoji-shortname'] = !isMobile; this.quillModules.imageResize = { modules: ['Resize', 'DisplaySize'] }; @@ -111,13 +128,17 @@ export class ViewCommentDataComponent implements OnInit, AfterViewInit { if (this.markEvents && this.markEvents.onChange) { this.markEvents.onChange(e.delta); } - this.currentData = ViewCommentDataComponent.getDataFromDelta(e.content); + this._currentData = ViewCommentDataComponent.getDataFromDelta(e.content); this.currentText = e.text; }); this.editor.onEditorCreated.subscribe(_ => { if (this.markEvents && this.markEvents.onCreate) { this.markEvents.onCreate(this.editorErrorLayer.nativeElement, this.tooltipContainer.nativeElement, this.editor); } + if (this._currentData) { + this.set(this._currentData); + } + (this.editor.editorElem.firstElementChild as HTMLElement).focus(); this.syncErrorLayer(); setTimeout(() => this.syncErrorLayer(), 200); // animations? }); @@ -144,7 +165,7 @@ export class ViewCommentDataComponent implements OnInit, AfterViewInit { }); } else { this.quillView.onEditorCreated.subscribe(_ => { - this.set(this.currentData); + this.set(this._currentData); }); } } diff --git a/src/app/components/shared/write-comment/write-comment.component.html b/src/app/components/shared/write-comment/write-comment.component.html index 5a2f19a711057f5dcce84df71d9a45443943806f..32a9d67ce13b57558f363173015f13b35dd7050e 100644 --- a/src/app/components/shared/write-comment/write-comment.component.html +++ b/src/app/components/shared/write-comment/write-comment.component.html @@ -46,9 +46,11 @@ </ars-row> <ars-row [height]="12"></ars-row> <app-view-comment-data *ngIf="enabled" - [user]="user" + [isModerator]="isModerator" [isEditor]="true" [maxTextCharacters]="maxTextCharacters" + [maxDataCharacters]="maxDataCharacters" + [placeHolderText]="placeholder" [markEvents]="getMarkEvents()"></app-view-comment-data> <ars-row ars-flex-box *ngIf="enabled" class="spellcheck"> <ars-col> @@ -65,7 +67,7 @@ </ars-col> <ars-col> <app-dialog-action-buttons - buttonsLabelSection="comment-page" + [buttonsLabelSection]="i18nSection" [confirmButtonLabel]="confirmLabel" [cancelButtonLabel]="cancelLabel" [showLoadingCycle]="isSpinning" diff --git a/src/app/components/shared/write-comment/write-comment.component.scss b/src/app/components/shared/write-comment/write-comment.component.scss index 0f303ef0146cb8af2965e175a00a2eadd31dd795..a54b280fe5f29f6cad4c4d64bf7a340970c1e1fb 100644 --- a/src/app/components/shared/write-comment/write-comment.component.scss +++ b/src/app/components/shared/write-comment/write-comment.component.scss @@ -234,122 +234,3 @@ $borderOffset: 2px; text-align: center; } } - -/* - For quill - */ - -::ng-deep .ql-editor.ql-blank::before { - color: var(--on-surface); - filter: opacity(0.6); -} - -::ng-deep .ql-snow { - :focus { - outline-offset: 0; - } - - .ql-editor { - min-height: 8.8em; - } - - .ql-stroke { - stroke: var(--on-surface); - } - - .ql-picker { - color: var(--on-surface); - } - - .ql-tooltip { - &[data-mode=formula]::before { - --quill-tooltip-label: var(--quill-tooltip-label-formula); - } - &[data-mode=video]::before { - --quill-tooltip-label: var(--quill-tooltip-label-video); - } - &[data-mode=image]::before { - --quill-tooltip-label: var(--quill-tooltip-label-image); - } - &[data-mode=link]::before { - --quill-tooltip-label: var(--quill-tooltip-label-link); - } - &::before { - content: var(--quill-tooltip-label) !important; - } - - color: var(--on-surface); - background-color: var(--surface); - - .ql-action::after { - padding: 7px !important; - background: var(--primary); - border-radius: 4px; - color: var(--on-primary); - content: var(--quill-tooltip-action) !important; - } - - &.ql-editing .ql-action::after { - --quill-tooltip-action: var(--quill-tooltip-action-save); - } - - .ql-remove::before { - content: var(--quill-tooltip-remove) !important; - padding: 7px !important; - background: var(--cancel); - border-radius: 4px; - color: var(--on-cancel); - } - - &.ql-editing input[type=text] { - border-color: var(--on-surface); - color: var(--on-surface); - background-color: var(--dialog); - } - } - - .ql-fill, .ql-stroke.ql-fill { - fill: var(--on-surface); - } - - .ql-picker.ql-expanded .ql-picker-label { - color: var(--primary); - - .ql-stroke { - stroke: var(--primary); - } - } - - &.ql-container { - border-color: var(--on-surface); - height: 80%; - } - - &.ql-toolbar, .ql-toolbar { - border-color: var(--on-surface); - - .ql-picker.ql-expanded { - .ql-picker-label { - border-color: var(--on-surface); - } - - .ql-picker-options { - background-color: var(--surface); - } - } - - button:hover, button:focus, button.ql-active, - .ql-picker-label:hover, .ql-picker-label.ql-active, - .ql-picker-item:hover, .ql-picker-item.ql-selected { - color: var(--primary); - - .ql-stroke { - stroke: var(--primary); - } - - .ql-fill, .ql-stroke.ql-fill { - fill: var(--primary); - } - } - } -} diff --git a/src/app/components/shared/write-comment/write-comment.component.ts b/src/app/components/shared/write-comment/write-comment.component.ts index 6a00264bae2ad3575bea300121dd32844fa1d221..324be593f6953fb88b24dec562dd298a7351e268 100644 --- a/src/app/components/shared/write-comment/write-comment.component.ts +++ b/src/app/components/shared/write-comment/write-comment.component.ts @@ -2,14 +2,13 @@ import { Component, ElementRef, Input, OnInit, TemplateRef, ViewChild } from '@a import { TranslateService } from '@ngx-translate/core'; import { Language, LanguagetoolService } from '../../../services/http/languagetool.service'; import { Comment } from '../../../models/comment'; -import { User } from '../../../models/user'; import { NotificationService } from '../../../services/util/notification.service'; import { EventService } from '../../../services/util/event.service'; import { Marks } from './write-comment.marks'; import { LanguageService } from '../../../services/util/language.service'; import { QuillEditorComponent } from 'ngx-quill'; import { ViewCommentDataComponent } from '../view-comment-data/view-comment-data.component'; - +import { DeepLService } from '../../../services/http/deep-l.service'; @Component({ selector: 'app-write-comment', @@ -20,7 +19,7 @@ export class WriteCommentComponent implements OnInit { @ViewChild(ViewCommentDataComponent) commentData: ViewCommentDataComponent; @ViewChild('langSelect') langSelect: ElementRef<HTMLDivElement>; - @Input() user: User; + @Input() isModerator = false; @Input() tags: string[]; @Input() onClose: () => any; @Input() onSubmit: (commentData: string, commentText: string, selectedTag: string) => any; @@ -31,6 +30,8 @@ export class WriteCommentComponent implements OnInit { @Input() additionalTemplate: TemplateRef<any>; @Input() enabled = true; @Input() isCommentAnswer = false; + @Input() placeholder = 'comment-page.enter-comment'; + @Input() i18nSection = 'comment-page'; comment: Comment; selectedTag: string; maxTextCharacters = 500; @@ -48,7 +49,8 @@ export class WriteCommentComponent implements OnInit { private languageService: LanguageService, private translateService: TranslateService, public eventService: EventService, - public languagetoolService: LanguagetoolService) { + public languagetoolService: LanguagetoolService, + public deepl: DeepLService) { this.languageService.langEmitter.subscribe(lang => { this.translateService.use(lang); }); @@ -57,11 +59,11 @@ export class WriteCommentComponent implements OnInit { ngOnInit(): void { this.translateService.use(localStorage.getItem('currentLang')); if (this.isCommentAnswer) { - this.maxTextCharacters = this.user.role > 0 ? 2000 : 0; + this.maxTextCharacters = this.isModerator ? 2000 : 0; } else { - this.maxTextCharacters = this.user.role > 0 ? 1000 : 500; + this.maxTextCharacters = this.isModerator ? 1000 : 500; } - this.maxDataCharacters = this.user.role > 0 ? this.maxTextCharacters * 5 : this.maxTextCharacters * 3; + this.maxDataCharacters = this.isModerator ? this.maxTextCharacters * 5 : this.maxTextCharacters * 3; } buildCloseDialogActionCallback(): () => void { diff --git a/src/app/services/http/base-http.service.ts b/src/app/services/http/base-http.service.ts index 6da60736a0b3cadf74868321c75e9afc58a24647..ed667e0795cff96cf0445d56a9b986ba45089ec3 100644 --- a/src/app/services/http/base-http.service.ts +++ b/src/app/services/http/base-http.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { Observable, throwError, TimeoutError } from 'rxjs'; +import { HttpErrorResponse } from '@angular/common/http'; @Injectable() export class BaseHttpService { @@ -13,6 +14,10 @@ export class BaseHttpService { return (error: any): Observable<T> => { if (error instanceof TimeoutError) { this.nextRequest = new Date().getTime() + 1_000; + } else if (error instanceof HttpErrorResponse) { + if (error.status === 429 || error.status === 456) { + this.nextRequest = new Date().getTime() + 30_000; + } } console.error(error); return throwError(error); diff --git a/src/app/services/http/deep-l.service.spec.ts b/src/app/services/http/deep-l.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f402fba5ea9c57acd24e3d8b5298b3d95b38068a --- /dev/null +++ b/src/app/services/http/deep-l.service.spec.ts @@ -0,0 +1,17 @@ +/*import { TestBed } from '@angular/core/testing'; + +import { DeepLService } from './deep-l.service'; + +describe('DeepLService', () => { + let service: DeepLService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DeepLService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); + */ diff --git a/src/app/services/http/deep-l.service.ts b/src/app/services/http/deep-l.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..7125235d5dee9ab6e2435d9993df5de8e8f00ee5 --- /dev/null +++ b/src/app/services/http/deep-l.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { BaseHttpService } from './base-http.service'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { catchError, map, tap } from 'rxjs/operators'; +import { flatMap } from 'rxjs/internal/operators'; + +const httpOptions = { + // eslint-disable-next-line @typescript-eslint/naming-convention + headers: new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }) +}; + +interface DeepLResult { + translations: { + detected_source_language: string; + text: string; + }[]; +} + +@Injectable({ + providedIn: 'root' +}) +export class DeepLService extends BaseHttpService { + + constructor(private http: HttpClient) { + super(); + } + + improveTextStyle(text: string): Observable<string> { + return this.makeTranslateRequest([text], 'EN-US').pipe( + flatMap(result => + this.makeTranslateRequest([result.translations[0].text], result.translations[0].detected_source_language)), + map(result => result.translations[0].text) + ); + } + + private makeTranslateRequest(text: string[], targetLang: string): Observable<DeepLResult> { + const url = '/deepl/translate'; + console.assert(text.length > 0, 'You need at least one text entry.'); + console.assert(text.length <= 50, 'Maximum 50 text entries are allowed'); + const additional = '?target_lang=' + encodeURIComponent(targetLang) + + '&text=' + text.map(e => encodeURIComponent(e)).join('&text='); + return this.http.get<string>(url + additional, httpOptions) + .pipe( + tap(_ => ''), + catchError(this.handleError<any>('makeTranslateRequest')), + ); + } +} diff --git a/src/app/services/util/device-info.service.spec.ts b/src/app/services/util/device-info.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..22755a345d35b4c26838db25ee2a994ce3294f6f --- /dev/null +++ b/src/app/services/util/device-info.service.spec.ts @@ -0,0 +1,17 @@ +/*import { TestBed } from '@angular/core/testing'; + +import { DeviceInfoService } from './device-info.service'; + +describe('DeviceInfoService', () => { + let service: DeviceInfoService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DeviceInfoService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); + */ diff --git a/src/app/services/util/device-info.service.ts b/src/app/services/util/device-info.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..9bc0fd5685370e04b18b6cd8e18e70fa6f77ff8c --- /dev/null +++ b/src/app/services/util/device-info.service.ts @@ -0,0 +1,67 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class DeviceInfoService { + + private readonly _isSafari; + private readonly _userAgentDeviceType; + private readonly _isMobile = new BehaviorSubject(false); + + constructor() { + const userAgent = navigator.userAgent; + if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)) { + // Check if IOS device + if (/iPhone|iPad|iPod/.test(userAgent)) { + this._isSafari = true; + } + this._userAgentDeviceType = 'mobile'; + } else { + // Check if Mac + if (/Macintosh|MacIntel|MacPPC|Mac68k/.test(userAgent)) { + // Check if Safari browser + if (userAgent.indexOf('Safari') !== -1 && userAgent.indexOf('Chrome') === -1) { + this._isSafari = true; + } + } + this._userAgentDeviceType = 'desktop'; + } + this._isSafari = this._isSafari || false; + if (window.matchMedia) { + const match = window.matchMedia('only screen and (max-device-width: 480px) and (orientation: portrait), ' + + 'only screen and (max-device-height: 480px) and (orientation: landscape)'); + this._isMobile.next(match.matches); + match.addEventListener('change', (e) => { + this._isMobile.next(e.matches); + }); + } else { + this._isMobile.next(this.isUserAgentMobile); + } + } + + get isSafari(): boolean { + return this._isSafari; + } + + get isUserAgentMobile(): boolean { + return this._userAgentDeviceType === 'mobile'; + } + + get isUserAgentDesktop(): boolean { + return this._userAgentDeviceType === 'desktop'; + } + + get userAgentDeviceType(): string { + return this._userAgentDeviceType; + } + + get isCurrentlyMobile(): boolean { + return this._isMobile.value; + } + + public isMobile(): Observable<boolean> { + return this._isMobile.asObservable(); + } +} diff --git a/src/app/services/util/onboarding.service.ts b/src/app/services/util/onboarding.service.ts index d884ebffe67445438de131a3d7ead6e85c7e03e5..f9b8c464e421ab0cb3ef0bad66a7aa983eb100d6 100644 --- a/src/app/services/util/onboarding.service.ts +++ b/src/app/services/util/onboarding.service.ts @@ -12,6 +12,7 @@ import { NotificationService } from './notification.service'; import { RoomService } from '../http/room.service'; import { TranslateService } from '@ngx-translate/core'; import { LanguageService } from './language.service'; +import { DeviceInfoService } from './device-info.service'; @Injectable({ providedIn: 'root' @@ -31,7 +32,8 @@ export class OnboardingService { private notificationService: NotificationService, private roomService: RoomService, private translateService: TranslateService, - private langService: LanguageService) { + private langService: LanguageService, + private deviceInfo: DeviceInfoService) { this.langService.langEmitter.subscribe(lang => { this.translateService.use(lang); }); @@ -79,6 +81,8 @@ export class OnboardingService { if (this._activeTour) { this.cleanup(); return false; + } else if (this.deviceInfo.isSafari) { + return false; } if (ignoreDone) { this.dataStoreService.remove('onboarding_' + tour.name); diff --git a/src/app/utils/create-comment-wrapper.ts b/src/app/utils/create-comment-wrapper.ts index 1408a49eb836e58216a0f531ab6d8b73a1b50c97..93706c1c24af01e487edafb5001e92062636c172 100644 --- a/src/app/utils/create-comment-wrapper.ts +++ b/src/app/utils/create-comment-wrapper.ts @@ -22,8 +22,8 @@ export class CreateCommentWrapper { openCreateDialog(user: User): Observable<Comment> { const dialogRef = this.dialog.open(CreateCommentComponent, { width: '900px', - maxWidth: 'calc( 100% - 50px )', - maxHeight: 'calc( 100vh - 50px )', + maxWidth: '100%', + maxHeight: 'calc( 100vh - 20px )', autoFocus: false, }); dialogRef.componentInstance.user = user; diff --git a/src/assets/i18n/creator/de.json b/src/assets/i18n/creator/de.json index 5e543d7ca965230626f95bc5706b4a919446aee8..ef9be59cd22afa5367efbb3f284d8e218fb05b4f 100644 --- a/src/assets/i18n/creator/de.json +++ b/src/assets/i18n/creator/de.json @@ -125,6 +125,7 @@ "answer": "Frage kommentieren", "save-answer": "Speichern", "comment-answered": "Antwort wurde gespeichert.", + "continue": "Weiter", "edit-answer": "Bearbeiten", "delete-answer": "Löschen", "really-delete-answer": "Willst du deine Antwort wirklich löschen?", @@ -235,6 +236,12 @@ "undo": "Rückgängig", "yes": "Ja" }, + "deepl": { + "header": "DeepL Unterstützung", + "label": "Wähle einen Text", + "option-normal": "Eingegebener Text", + "option-improved": "Stilistisch verbesserter Text" + }, "home-page": { "create-session": "Neue Sitzung", "created-1": "Die Sitzung »", @@ -468,7 +475,9 @@ "period-since-first-comment":"Zeitraum seit der ersten Frage", "upvote-topic": "Up-Votes für dieses Thema", "downvote-topic": "Down-Votes für dieses Thema", - "blacklist-topic": "Thema auf die »Blacklist« setzen" + "blacklist-topic": "Thema auf die »Blacklist« setzen", + "changes-gone-wrong": "Etwas ist schiefgelaufen!", + "changes-successful": "Änderungen gespeichert." }, "tag-cloud-config": { "general": "Allgemein", diff --git a/src/assets/i18n/creator/en.json b/src/assets/i18n/creator/en.json index 4714d563ec951ca976b7a455ba51d79bfb09326c..1a1be02ce03aa8a2291a080535237a29e5424424 100644 --- a/src/assets/i18n/creator/en.json +++ b/src/assets/i18n/creator/en.json @@ -125,6 +125,7 @@ "answer": "Comment this question", "save-answer": "Save", "comment-answered": "Answer has been sent.", + "continue": "Continue", "edit-answer": "Edit", "delete-answer": "Delete", "really-delete-answer": "Do you really want to delete this answer?", @@ -236,6 +237,12 @@ "undo": "Undo", "yes": "Yes" }, + "deepl": { + "header": "DeepL Support", + "label": "Choose a text", + "option-normal": "Entered text", + "option-improved": "Stylistically improved text" + }, "home-page": { "create-session": "New session", "created-1": "Session »", @@ -377,7 +384,9 @@ "period-since-first-comment":"Period since first comment", "upvote-topic": "Upvotes for this topic", "downvote-topic": "Downvotes for this topic", - "blacklist-topic": "Add topic to blacklist" + "blacklist-topic": "Add topic to blacklist", + "changes-gone-wrong": "Something went wrong!", + "changes-successful": "Successfully updated." }, "tag-cloud-popup": { "few-seconds": "few seconds", diff --git a/src/assets/i18n/participant/de.json b/src/assets/i18n/participant/de.json index a0f87e838e32d5bc8b8e8700b282fa526dcc5a31..d034aa1e28e93ae734ae8def57635f9f4a5af79b 100644 --- a/src/assets/i18n/participant/de.json +++ b/src/assets/i18n/participant/de.json @@ -119,6 +119,7 @@ "abort": "Abbrechen", "ask-question-description": "Gib hier deine Frage ein!", "save-answer": "Speichern", + "continue": "Weiter", "delete-answer": "Löschen", "cancel": "Abbrechen", "cancel-description": "Abbrechen", @@ -163,6 +164,12 @@ "upvote": "positiv", "downvote": "negativ" }, + "deepl": { + "header": "DeepL Unterstützung", + "label": "Wähle einen Text", + "option-normal": "Eingegebener Text", + "option-improved": "Stilistisch verbesserter Text" + }, "home-page": { "exactly-8": "Ein Raum-Code hat genau 8 Ziffern.", "no-room-found": "Es wurde kein Raum mit diesem Raum-Code gefunden.", @@ -272,7 +279,9 @@ "period-since-first-comment":"Zeitraum seit der ersten Frage", "upvote-topic": "Up-Votes für dieses Thema", "downvote-topic": "Down-Votes für dieses Thema", - "blacklist-topic": "Thema auf die »Blacklist« setzen" + "blacklist-topic": "Thema auf die »Blacklist« setzen", + "changes-gone-wrong": "Etwas ist schiefgelaufen!", + "changes-successful": "Änderungen gespeichert." }, "tag-cloud-popup": { "few-seconds": "wenige Sekunden", diff --git a/src/assets/i18n/participant/en.json b/src/assets/i18n/participant/en.json index be4675cb8429bd2579225736fcd1a5895101193c..613bb84bd1f2f616b307a4f2dddb696d98ece70c 100644 --- a/src/assets/i18n/participant/en.json +++ b/src/assets/i18n/participant/en.json @@ -129,6 +129,7 @@ "abort": "Cancel", "ask-question-description": "Enter your question …", "save-answer": "Save", + "continue": "Continue", "delete-answer": "Delete", "cancel": "Cancel", "cancel-description": "Cancel", @@ -172,6 +173,12 @@ "upvote": "upvotes", "downvote": "downvotes" }, + "deepl": { + "header": "DeepL Support", + "label": "Choose a text", + "option-normal": "Entered text", + "option-improved": "Stylistically improved text" + }, "home-page": { "exactly-8": "A key is a combination of 8 digits.", "no-room-found": "No session found with this key", @@ -278,7 +285,9 @@ "period-since-first-comment":"Period since first comment", "upvote-topic": "Upvotes for this topic", "downvote-topic": "Downvotes for this topic", - "blacklist-topic": "Add topic to blacklist" + "blacklist-topic": "Add topic to blacklist", + "changes-gone-wrong": "Something went wrong!", + "changes-successful": "Successfully updated." }, "tag-cloud-popup": { "few-seconds": "few seconds",