fixes+improvements

This commit is contained in:
_Bastler 2021-10-03 20:22:58 +02:00
parent 7477527f24
commit a94647d549
10 changed files with 190 additions and 49 deletions

View File

@ -4,7 +4,9 @@
<p>{{entry.text}}</p> <p>{{entry.text}}</p>
<ui-commentform [target]="entry.id" [change]="boundRefresh"></ui-commentform> <ui-commentform [target]="entry.id" [change]="boundReplyCallback"></ui-commentform>
<ng-container *ngIf="entry.metadata.comments">
<ui-comments [target]="entry.id"></ui-comments> <ui-comments [target]="entry.id"></ui-comments>
</ng-container>
</ng-container> </ng-container>

View File

@ -3,6 +3,7 @@ import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { EntriesService } from '../../services/entries.service'; import { EntriesService } from '../../services/entries.service';
import { CommentService } from '../../services/comment.service';
@Component({ @Component({
selector: 'page-entry', selector: 'page-entry',
@ -14,13 +15,15 @@ export class PageEntry implements OnInit {
entry: any; entry: any;
notfound: boolean = false; notfound: boolean = false;
boundRefresh: Function; boundRefresh: Function;
boundReplyCallback: Function;
constructor(private entriesService: EntriesService, constructor(private commentService: CommentService, private entriesService: EntriesService,
private route: ActivatedRoute) { } private route: ActivatedRoute) { }
ngOnInit(): void { ngOnInit(): void {
this.id = +this.route.snapshot.paramMap.get('id'); this.id = +this.route.snapshot.paramMap.get('id');
this.boundRefresh = this.refresh.bind(this); this.boundRefresh = this.refresh.bind(this);
this.boundReplyCallback = this.replyCallback.bind(this);
this.refresh(); this.refresh();
} }
@ -34,4 +37,12 @@ export class PageEntry implements OnInit {
}) })
} }
replyCallback(): void {
this.entry.metadata.reply = false;
this.entry.metadata.comments = 0;
this.commentService.count(this.entry.id).subscribe((data) => {
this.entry.metadata.comments = +data;
});
}
} }

View File

@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class ModerationService {
constructor(private http: HttpClient) {
}
deleteComment(id: number) {
return this.http.delete(environment.apiUrl + "/m/c/" + id);
}
deleteEntry(id: number) {
return this.http.delete(environment.apiUrl + "/m/e/" + id);
}
}

View File

@ -1,16 +1,17 @@
<div mat-line> <div mat-line>
<small> <small>
<a *ngIf="comment.metadata && comment.metadata.vote" href="javascript:" (click)="voteUp(comment.id)"
matTooltip="{{'vote.up' | i18n}}">
<mat-icon inline="true">expand_less</mat-icon>
</a>
<mat-icon *ngIf="!comment.metadata || !comment.metadata.vote" inline="true">&nbsp;</mat-icon>
{{'comment.author' | i18n}}<a routerLink="/u/{{comment.author}}">{{comment.author}}</a>&nbsp;
<a routerLink="/c/{{comment.id}}" matTooltip="{{comment.created | datef:'LLLL'}}">{{comment.created <a routerLink="/c/{{comment.id}}" matTooltip="{{comment.created | datef:'LLLL'}}">{{comment.created
| datef}}</a> | datef}}</a>
{{'comment.author' | i18n}}<a routerLink="/u/{{comment.author}}">{{comment.author}}</a>
&nbsp;
<a *ngIf="comment.metadata && comment.metadata.vote" href="javascript:" (click)="voteUp(comment.id)"
matTooltip="{{'vote.up' | i18n}}">
<mat-icon inline="true">thumb_up</mat-icon>
</a>
<span *ngIf="comment.metadata && comment.metadata.vote">&nbsp;</span>
<a *ngIf="comment.metadata && comment.metadata.downvote" href="javascript:" (click)="voteDown(comment.id)" <a *ngIf="comment.metadata && comment.metadata.downvote" href="javascript:" (click)="voteDown(comment.id)"
matTooltip="{{'vote.down' | i18n}}"> matTooltip="{{'vote.down' | i18n}}">
<mat-icon inline="true">remove</mat-icon> <mat-icon inline="true">thumb_down</mat-icon>
</a> </a>
</small> </small>
</div> </div>
@ -27,6 +28,10 @@
<a *ngIf="comment.metadata.unvote" href="javascript:" (click)="voteDown()"> <a *ngIf="comment.metadata.unvote" href="javascript:" (click)="voteDown()">
{{'comment.unvote' | i18n}} {{'comment.unvote' | i18n}}
</a> </a>
<span *ngIf="moderator">|</span>
<a *ngIf="moderator" href="javascript:" (click)="deleteComment(comment)">
{{'comment.delete' | i18n}}
</a>
</small> </small>
</div> </div>

View File

@ -1,6 +1,11 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { AuthService } from '../../services/auth.service';
import { VoteService } from '../../services/vote.service'; import { VoteService } from '../../services/vote.service';
import { CommentService } from '../../services/comment.service'; import { CommentService } from '../../services/comment.service';
import { ModerationService } from '../../services/moderarion.service';
import { ConfirmDialog } from '../../ui/confirm/confirm.component';
@Component({ @Component({
selector: 'ui-comment', selector: 'ui-comment',
@ -9,14 +14,26 @@ import { CommentService } from '../../services/comment.service';
}) })
export class UiComment implements OnInit { export class UiComment implements OnInit {
moderator: boolean = false;
@Input() comment: any; @Input() comment: any;
@Input() change: Function; @Input() change: Function;
boundReplyCallback: Function; boundReplyCallback: Function;
constructor(private commentService: CommentService, private voteService: VoteService) { } constructor(private authService: AuthService, private commentService: CommentService, private voteService: VoteService,
private moderationService: ModerationService, public dialog: MatDialog) { }
ngOnInit(): void { ngOnInit(): void {
this.authService.auth.subscribe((auth: any) => {
if (auth && auth.authorities) {
for (let role of auth.authorities) {
if (role.authority == 'ROLE_ADMIN' || role.authority == 'ROLE_MOD') {
this.moderator = true;
}
}
}
})
this.commentService.countParent(this.comment.target, this.comment.id).subscribe((data) => { this.commentService.countParent(this.comment.target, this.comment.id).subscribe((data) => {
this.comment.metadata.comments = +data; this.comment.metadata.comments = +data;
}); });
@ -36,7 +53,7 @@ export class UiComment implements OnInit {
}); });
} }
author(author : string) { author(author: string) {
return '<a href="/u/' + author + '">' + author + '</a>'; return '<a href="/u/' + author + '">' + author + '</a>';
} }
@ -47,4 +64,21 @@ export class UiComment implements OnInit {
this.comment.metadata.comments = +data; this.comment.metadata.comments = +data;
}); });
} }
deleteComment(comment: any) {
const dialogRef = this.dialog.open(ConfirmDialog, {
data: {
'label': 'comment.confirmDelete',
'args': [ comment.text, comment.author ]
}
})
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.moderationService.deleteComment(comment.id).subscribe((result: any) => {
this.change && this.change()
})
}
});
}
} }

View File

@ -36,7 +36,7 @@ export class UiCommentForm implements OnInit {
comment.text = this.form.get("text").value; comment.text = this.form.get("text").value;
this.commentService.create(comment).subscribe((data) => { this.commentService.create(comment).subscribe((data) => {
this.form.reset(); this.formDirective.resetForm();
this.change && this.change(); this.change && this.change();
}); });
} }

View File

@ -1,11 +1,10 @@
<div mat-line> <div mat-line>
<span *ngIf="index">{{index}}.&nbsp;</span> <span *ngIf="index">{{index}}.&nbsp;</span>
<a *ngIf="entry.metadata && entry.metadata.vote" href="javascript:" (click)="voteUp(entry.id)"
matTooltip="{{'vote.up' | i18n}}">
<mat-icon inline="true">expand_less</mat-icon>
</a>
<mat-icon *ngIf="!entry.metadata || !entry.metadata.vote" inline="true">&nbsp;</mat-icon>
<mat-icon>{{'entryType.' + entry.entryType + '.icon' | i18n}}</mat-icon>&nbsp; <mat-icon>{{'entryType.' + entry.entryType + '.icon' | i18n}}</mat-icon>&nbsp;
<span *ngIf="entry.metadata && entry.metadata.vote" (click)="voteUp(entry.id)" matTooltip="{{'vote.up' | i18n}}">
<mat-icon inline="true">thumb_up</mat-icon>
</span>
&nbsp;
<a class="title" *ngIf="entry.url" [href]="entry.url" target="_blank">{{entry.title}}</a> <a class="title" *ngIf="entry.url" [href]="entry.url" target="_blank">{{entry.title}}</a>
<a class="title" *ngIf="!entry.url" routerLink="/e/{{entry.id}}">{{entry.title}}</a> <a class="title" *ngIf="!entry.url" routerLink="/e/{{entry.id}}">{{entry.title}}</a>
</div> </div>
@ -21,5 +20,9 @@
<a routerLink="/e/{{entry.id}}"> <a routerLink="/e/{{entry.id}}">
{{'entry.comments' | i18n:(entry.metadata && entry.metadata.comments)}} {{'entry.comments' | i18n:(entry.metadata && entry.metadata.comments)}}
</a> </a>
<span *ngIf="moderator">|</span>
<a *ngIf="moderator" href="javascript:" (click)="deleteEntry(entry)">
{{'entry.delete' | i18n}}
</a>
</small> </small>
</div> </div>

View File

@ -1,3 +1,6 @@
@import '../../../variables.scss';
small a { small a {
color: inherit !important; color: inherit !important;
text-decoration: none; text-decoration: none;
@ -7,3 +10,7 @@ small a:hover {
color: inherit !important; color: inherit !important;
text-decoration: underline; text-decoration: underline;
} }
a.vote {
color: $light-primary-text;
}

View File

@ -1,5 +1,11 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { AuthService } from '../../services/auth.service';
import { VoteService } from '../../services/vote.service'; import { VoteService } from '../../services/vote.service';
import { CommentService } from '../../services/comment.service';
import { ModerationService } from '../../services/moderarion.service';
import { ConfirmDialog } from '../../ui/confirm/confirm.component';
@Component({ @Component({
selector: 'ui-entry', selector: 'ui-entry',
@ -8,14 +14,24 @@ import { VoteService } from '../../services/vote.service';
}) })
export class UiEntry implements OnInit { export class UiEntry implements OnInit {
moderator: boolean = false;
@Input() entry: any; @Input() entry: any;
@Input() index : number; @Input() index: number;
@Input() change : Function; @Input() change: Function;
constructor(private voteService: VoteService) { } constructor(private authService: AuthService, private voteService: VoteService,
private moderationService: ModerationService, public dialog: MatDialog) { }
ngOnInit(): void { ngOnInit(): void {
this.authService.auth.subscribe((auth: any) => {
if (auth && auth.authorities) {
for (let role of auth.authorities) {
if (role.authority == 'ROLE_ADMIN' || role.authority == 'ROLE_MOD') {
this.moderator = true;
}
}
}
})
} }
voteUp() { voteUp() {
@ -29,4 +45,22 @@ export class UiEntry implements OnInit {
this.change && this.change() this.change && this.change()
}); });
} }
deleteEntry(entry: any) {
const dialogRef = this.dialog.open(ConfirmDialog, {
data: {
'label': 'comment.confirmDelete',
'args': [ entry.title, entry.author ]
}
})
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.moderationService.deleteEntry(entry.id).subscribe((result: any) => {
this.entry = null;
this.change && this.change();
})
}
});
}
} }

View File

@ -1,43 +1,67 @@
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<a href="javascript:" mat-icon-button>
<mat-icon (click)="sidenav.toggle()">menu</mat-icon>
</a>
<mat-icon svgIcon="logo"></mat-icon> <mat-icon svgIcon="logo"></mat-icon>
<span> <span>
{{'bstlboard' | i18n}} {{'bstlboard' | i18n}}
</span> </span>
<div *ngIf="auth && auth.authenticated"> <span class="spacer"></span>
<a mat-button routerLink="/">{{'top' | i18n}}</a> <ng-container *ngIf="auth && auth.authenticated">
<a mat-button routerLink="/new">{{'new' | i18n}}</a>
<a routerLink="/submit" mat-raised-button color="accent">{{'submission' | <a routerLink="/submit" mat-raised-button color="accent">{{'submission' |
i18n}}</a> i18n}}</a>
</div> </ng-container>
<span class="spacer"></span>
<ng-container> </mat-toolbar>
<button mat-button [matMenuTriggerFor]="menu">
<mat-icon>settings</mat-icon> <mat-sidenav-container>
<mat-icon>arrow_drop_down</mat-icon> <mat-sidenav #sidenav [mode]="isBiggerScreen() ? 'side' : 'over'" [(opened)]="opened"
</button> (click)="!isBiggerScreen() && opened=false">
<mat-menu #menu="matMenu">
<a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-menu-item> <mat-nav-list>
<a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-list-item>
<mat-icon>login</mat-icon> {{'login' | i18n}} <mat-icon>login</mat-icon> {{'login' | i18n}}
</a> </a>
<a *ngIf="auth && auth.authenticated" routerLink="/settings" routerLinkActive="active" mat-menu-item> <a *ngIf="auth && auth.authenticated" routerLink="/" routerLinkActive="active" mat-list-item>
<mat-icon>trending_up</mat-icon> {{'top' | i18n}}
</a>
<a *ngIf="auth && auth.authenticated" routerLink="/new" routerLinkActive="active" mat-list-item>
<mat-icon>history</mat-icon> {{'new' | i18n}}
</a>
<mat-divider></mat-divider>
<a *ngIf="auth && auth.authenticated" routerLink="/settings" routerLinkActive="active" mat-list-item>
<mat-icon>tune</mat-icon> {{'settings' | i18n}} <mat-icon>tune</mat-icon> {{'settings' | i18n}}
</a> </a>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<a *ngFor="let locale of locales" mat-menu-item (click)="setLocale(locale)">{{'locale.' + locale + '.long' | <a *ngIf="auth && auth.authenticated" (click)="logout()" mat-list-item>
i18n}} <mat-icon inline=true *ngIf="locale == currentLocale">done</mat-icon></a> <mat-icon>exit_to_app</mat-icon> {{'logout' | i18n}}
<a mat-menu-item> </a>
<a *ngIf="!auth || auth && !auth.authenticated" routerLink="/login" routerLinkActive="active" mat-list-item>
<mat-icon>login</mat-icon> {{'login' | i18n}}
</a>
</mat-nav-list>
<span class="spacer"></span>
<mat-nav-list>
<a *ngFor="let locale of locales" mat-list-item (click)="setLocale(locale)">
<mat-icon *ngIf="locale == currentLocale">done</mat-icon>{{'locale.' + locale + '.long' |
i18n}}
</a>
<a mat-list-item>
<mat-slide-toggle (change)="darkThemeChange($event)" [checked]="darkTheme == 'true'"> <mat-slide-toggle (change)="darkThemeChange($event)" [checked]="darkTheme == 'true'">
{{'darkTheme' | i18n}} {{'darkTheme' | i18n}}
</mat-slide-toggle> </mat-slide-toggle>
</a> </a>
<mat-divider *ngIf="auth && auth.authenticated"></mat-divider>
<a *ngIf="auth && auth.authenticated" (click)="logout()" mat-menu-item>
<mat-icon>exit_to_app</mat-icon> {{'logout' | i18n}}
</a>
</mat-menu>
</ng-container>
</mat-toolbar>
<div class="container" fxFlex> </mat-nav-list>
</mat-sidenav>
<!-- Main content -->
<mat-sidenav-content>
<div class="container">
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>
</mat-sidenav-content>
</mat-sidenav-container>