generated from nhcarrigan/template
feat: implement user profiles with achievements and primary badge system #58
@@ -14,12 +14,13 @@ import { SanitizeService } from '../../services/sanitize.service';
|
|||||||
import { SuggestionService } from '../../services/suggestion.service';
|
import { SuggestionService } from '../../services/suggestion.service';
|
||||||
import { PaginationComponent } from '../shared/pagination.component';
|
import { PaginationComponent } from '../shared/pagination.component';
|
||||||
import { LikeButtonComponent } from '../shared/like-button.component';
|
import { LikeButtonComponent } from '../shared/like-button.component';
|
||||||
|
import { CommentDisplayComponent } from '../comment-display/comment-display.component';
|
||||||
import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-art-gallery',
|
selector: 'app-art-gallery',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent],
|
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent, CommentDisplayComponent],
|
||||||
template: `
|
template: `
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
@@ -468,56 +469,11 @@ import { Art, CreateArtDto, UpdateArtDto, Comment, SuggestionEntity, Link } from
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (commentsLoading()[art.id]) {
|
<app-comment-display
|
||||||
<div class="comments-loading">Loading comments...</div>
|
[comments]="getCommentsSignal(art.id)"
|
||||||
} @else {
|
(onEdit)="handleCommentEdit(art.id, $event)"
|
||||||
@for (comment of comments()[art.id] || []; track comment.id) {
|
(onDelete)="deleteComment(art.id, $event)"
|
||||||
<div class="comment">
|
/>
|
||||||
<div class="comment-header">
|
|
||||||
@if (comment.user.avatar) {
|
|
||||||
<img [src]="comment.user.avatar" [alt]="comment.user.username" class="comment-avatar">
|
|
||||||
}
|
|
||||||
<span class="comment-author">{{ comment.user.username }}</span>
|
|
||||||
@if (comment.user.inDiscord) {
|
|
||||||
<span class="discord-badge">Discord</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isVip) {
|
|
||||||
<span class="vip-badge">VIP</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isMod) {
|
|
||||||
<span class="mod-badge">Mod</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isStaff) {
|
|
||||||
<span class="staff-badge">Staff</span>
|
|
||||||
}
|
|
||||||
<span class="comment-date">{{ formatDate(comment.createdAt) }}</span>
|
|
||||||
@if (canEditComment(comment)) {
|
|
||||||
<button (click)="startEditComment(art.id, comment)" class="btn btn-secondary btn-xs">Edit</button>
|
|
||||||
}
|
|
||||||
@if (canDeleteComment(comment)) {
|
|
||||||
<button (click)="deleteComment(art.id, comment.id)" class="btn btn-danger btn-xs">Delete</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@if (editingCommentId() === comment.id) {
|
|
||||||
<div class="comment-edit-form">
|
|
||||||
<textarea
|
|
||||||
[(ngModel)]="editCommentContent"
|
|
||||||
name="editComment"
|
|
||||||
rows="3"
|
|
||||||
></textarea>
|
|
||||||
<div class="comment-edit-actions">
|
|
||||||
<button (click)="saveCommentEdit(art.id, comment.id)" class="btn btn-primary btn-xs">Save</button>
|
|
||||||
<button (click)="cancelCommentEdit()" class="btn btn-secondary btn-xs">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="comment-content" [innerHTML]="sanitizeService.sanitizeHtml(comment.content)"></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
} @empty {
|
|
||||||
<div class="no-comments">No comments yet. Be the first to comment!</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1615,4 +1571,21 @@ export class ArtGalleryComponent implements OnInit {
|
|||||||
toggleFilters() {
|
toggleFilters() {
|
||||||
this.showFilters.update(v => !v);
|
this.showFilters.update(v => !v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCommentEdit(artId: string, event: { commentId: string; content: string }) {
|
||||||
|
this.commentsService.updateCommentOnArt(artId, event.commentId, event.content).subscribe({
|
||||||
|
next: (updatedComment) => {
|
||||||
|
this.comments.set({
|
||||||
|
...this.comments(),
|
||||||
|
[artId]: (this.comments()[artId] || []).map(c =>
|
||||||
|
c.id === event.commentId ? updatedComment : c
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommentsSignal(artId: string) {
|
||||||
|
return signal(this.comments()[artId] || []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ import { SanitizeService } from '../../services/sanitize.service';
|
|||||||
import { SuggestionService } from '../../services/suggestion.service';
|
import { SuggestionService } from '../../services/suggestion.service';
|
||||||
import { PaginationComponent } from '../shared/pagination.component';
|
import { PaginationComponent } from '../shared/pagination.component';
|
||||||
import { LikeButtonComponent } from '../shared/like-button.component';
|
import { LikeButtonComponent } from '../shared/like-button.component';
|
||||||
|
import { CommentDisplayComponent } from '../comment-display/comment-display.component';
|
||||||
import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-books-list',
|
selector: 'app-books-list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent],
|
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent, CommentDisplayComponent],
|
||||||
template: `
|
template: `
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
@@ -655,52 +656,11 @@ import { Book, BookStatus, CreateBookDto, UpdateBookDto, Comment, SuggestionEnti
|
|||||||
@if (commentsLoading()[book.id]) {
|
@if (commentsLoading()[book.id]) {
|
||||||
<div class="comments-loading">Loading comments...</div>
|
<div class="comments-loading">Loading comments...</div>
|
||||||
} @else {
|
} @else {
|
||||||
@for (comment of comments()[book.id] || []; track comment.id) {
|
<app-comment-display
|
||||||
<div class="comment">
|
[comments]="getCommentsSignal(book.id)"
|
||||||
<div class="comment-header">
|
(onEdit)="handleCommentEdit(book.id, $event)"
|
||||||
@if (comment.user.avatar) {
|
(onDelete)="deleteComment(book.id, $event)"
|
||||||
<img [src]="comment.user.avatar" [alt]="comment.user.username" class="comment-avatar">
|
/>
|
||||||
}
|
|
||||||
<span class="comment-author">{{ comment.user.username }}</span>
|
|
||||||
@if (comment.user.inDiscord) {
|
|
||||||
<span class="discord-badge">Discord</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isVip) {
|
|
||||||
<span class="vip-badge">VIP</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isMod) {
|
|
||||||
<span class="mod-badge">Mod</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isStaff) {
|
|
||||||
<span class="staff-badge">Staff</span>
|
|
||||||
}
|
|
||||||
<span class="comment-date">{{ formatDate(comment.createdAt) }}</span>
|
|
||||||
@if (canEditComment(comment)) {
|
|
||||||
<button (click)="startEditComment(book.id, comment)" class="btn btn-secondary btn-xs">Edit</button>
|
|
||||||
}
|
|
||||||
@if (canDeleteComment(comment)) {
|
|
||||||
<button (click)="deleteComment(book.id, comment.id)" class="btn btn-danger btn-xs">Delete</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@if (editingCommentId() === comment.id) {
|
|
||||||
<div class="comment-edit-form">
|
|
||||||
<textarea
|
|
||||||
[(ngModel)]="editCommentContent"
|
|
||||||
name="editComment"
|
|
||||||
rows="3"
|
|
||||||
></textarea>
|
|
||||||
<div class="comment-edit-actions">
|
|
||||||
<button (click)="saveCommentEdit(book.id, comment.id)" class="btn btn-primary btn-xs">Save</button>
|
|
||||||
<button (click)="cancelCommentEdit()" class="btn btn-secondary btn-xs">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="comment-content" [innerHTML]="sanitizeService.sanitizeHtml(comment.content)"></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
} @empty {
|
|
||||||
<div class="no-comments">No comments yet. Be the first to comment!</div>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -1982,4 +1942,21 @@ export class BooksListComponent implements OnInit {
|
|||||||
alert('Failed to submit suggestion. Please try again.');
|
alert('Failed to submit suggestion. Please try again.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCommentEdit(bookId: string, event: { commentId: string; content: string }) {
|
||||||
|
this.commentsService.updateCommentOnBook(bookId, event.commentId, event.content).subscribe({
|
||||||
|
next: (updatedComment) => {
|
||||||
|
this.comments.set({
|
||||||
|
...this.comments(),
|
||||||
|
[bookId]: (this.comments()[bookId] || []).map(c =>
|
||||||
|
c.id === event.commentId ? updatedComment : c
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommentsSignal(bookId: string) {
|
||||||
|
return signal(this.comments()[bookId] || []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,12 +14,13 @@ import { SanitizeService } from '../../services/sanitize.service';
|
|||||||
import { SuggestionService } from '../../services/suggestion.service';
|
import { SuggestionService } from '../../services/suggestion.service';
|
||||||
import { PaginationComponent } from '../shared/pagination.component';
|
import { PaginationComponent } from '../shared/pagination.component';
|
||||||
import { LikeButtonComponent } from '../shared/like-button.component';
|
import { LikeButtonComponent } from '../shared/like-button.component';
|
||||||
|
import { CommentDisplayComponent } from '../comment-display/comment-display.component';
|
||||||
import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-manga-list',
|
selector: 'app-manga-list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent],
|
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent, CommentDisplayComponent],
|
||||||
template: `
|
template: `
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
@@ -610,56 +611,11 @@ import { Manga, MangaStatus, CreateMangaDto, UpdateMangaDto, Comment, Suggestion
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (commentsLoading()[manga.id]) {
|
<app-comment-display
|
||||||
<div class="comments-loading">Loading comments...</div>
|
[comments]="getCommentsSignal(manga.id)"
|
||||||
} @else {
|
(onEdit)="handleCommentEdit(manga.id, $event)"
|
||||||
@for (comment of comments()[manga.id] || []; track comment.id) {
|
(onDelete)="deleteComment(manga.id, $event)"
|
||||||
<div class="comment">
|
/>
|
||||||
<div class="comment-header">
|
|
||||||
@if (comment.user.avatar) {
|
|
||||||
<img [src]="comment.user.avatar" [alt]="comment.user.username" class="comment-avatar">
|
|
||||||
}
|
|
||||||
<span class="comment-author">{{ comment.user.username }}</span>
|
|
||||||
@if (comment.user.inDiscord) {
|
|
||||||
<span class="discord-badge">Discord</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isVip) {
|
|
||||||
<span class="vip-badge">VIP</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isMod) {
|
|
||||||
<span class="mod-badge">Mod</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isStaff) {
|
|
||||||
<span class="staff-badge">Staff</span>
|
|
||||||
}
|
|
||||||
<span class="comment-date">{{ formatDate(comment.createdAt) }}</span>
|
|
||||||
@if (canEditComment(comment)) {
|
|
||||||
<button (click)="startEditComment(manga.id, comment)" class="btn btn-secondary btn-xs">Edit</button>
|
|
||||||
}
|
|
||||||
@if (canDeleteComment(comment)) {
|
|
||||||
<button (click)="deleteComment(manga.id, comment.id)" class="btn btn-danger btn-xs">Delete</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@if (editingCommentId() === comment.id) {
|
|
||||||
<div class="comment-edit-form">
|
|
||||||
<textarea
|
|
||||||
[(ngModel)]="editCommentContent"
|
|
||||||
name="editComment"
|
|
||||||
rows="3"
|
|
||||||
></textarea>
|
|
||||||
<div class="comment-edit-actions">
|
|
||||||
<button (click)="saveCommentEdit(manga.id, comment.id)" class="btn btn-primary btn-xs">Save</button>
|
|
||||||
<button (click)="cancelCommentEdit()" class="btn btn-secondary btn-xs">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="comment-content" [innerHTML]="sanitizeService.sanitizeHtml(comment.content)"></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
} @empty {
|
|
||||||
<div class="no-comments">No comments yet. Be the first to comment!</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1780,4 +1736,21 @@ export class MangaListComponent implements OnInit {
|
|||||||
alert('Failed to submit suggestion. Please try again.');
|
alert('Failed to submit suggestion. Please try again.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCommentEdit(mangaId: string, event: { commentId: string; content: string }) {
|
||||||
|
this.commentsService.updateCommentOnManga(mangaId, event.commentId, event.content).subscribe({
|
||||||
|
next: (updatedComment) => {
|
||||||
|
this.comments.set({
|
||||||
|
...this.comments(),
|
||||||
|
[mangaId]: (this.comments()[mangaId] || []).map(c =>
|
||||||
|
c.id === event.commentId ? updatedComment : c
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommentsSignal(mangaId: string) {
|
||||||
|
return signal(this.comments()[mangaId] || []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ import { SanitizeService } from '../../services/sanitize.service';
|
|||||||
import { SuggestionService } from '../../services/suggestion.service';
|
import { SuggestionService } from '../../services/suggestion.service';
|
||||||
import { PaginationComponent } from '../shared/pagination.component';
|
import { PaginationComponent } from '../shared/pagination.component';
|
||||||
import { LikeButtonComponent } from '../shared/like-button.component';
|
import { LikeButtonComponent } from '../shared/like-button.component';
|
||||||
|
import { CommentDisplayComponent } from '../comment-display/comment-display.component';
|
||||||
import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-music-list',
|
selector: 'app-music-list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent],
|
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent, CommentDisplayComponent],
|
||||||
template: `
|
template: `
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
@@ -686,56 +687,11 @@ import { Music, MusicStatus, MusicType, CreateMusicDto, UpdateMusicDto, Comment,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (commentsLoading()[music.id]) {
|
<app-comment-display
|
||||||
<div class="comments-loading">Loading comments...</div>
|
[comments]="getCommentsSignal(music.id)"
|
||||||
} @else {
|
(onEdit)="handleCommentEdit(music.id, $event)"
|
||||||
@for (comment of comments()[music.id] || []; track comment.id) {
|
(onDelete)="deleteComment(music.id, $event)"
|
||||||
<div class="comment">
|
/>
|
||||||
<div class="comment-header">
|
|
||||||
@if (comment.user.avatar) {
|
|
||||||
<img [src]="comment.user.avatar" [alt]="comment.user.username" class="comment-avatar">
|
|
||||||
}
|
|
||||||
<span class="comment-author">{{ comment.user.username }}</span>
|
|
||||||
@if (comment.user.inDiscord) {
|
|
||||||
<span class="discord-badge">Discord</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isVip) {
|
|
||||||
<span class="vip-badge">VIP</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isMod) {
|
|
||||||
<span class="mod-badge">Mod</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isStaff) {
|
|
||||||
<span class="staff-badge">Staff</span>
|
|
||||||
}
|
|
||||||
<span class="comment-date">{{ formatDate(comment.createdAt) }}</span>
|
|
||||||
@if (canEditComment(comment)) {
|
|
||||||
<button (click)="startEditComment(music.id, comment)" class="btn btn-secondary btn-xs">Edit</button>
|
|
||||||
}
|
|
||||||
@if (canDeleteComment(comment)) {
|
|
||||||
<button (click)="deleteComment(music.id, comment.id)" class="btn btn-danger btn-xs">Delete</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@if (editingCommentId() === comment.id) {
|
|
||||||
<div class="comment-edit-form">
|
|
||||||
<textarea
|
|
||||||
[(ngModel)]="editCommentContent"
|
|
||||||
name="editComment"
|
|
||||||
rows="3"
|
|
||||||
></textarea>
|
|
||||||
<div class="comment-edit-actions">
|
|
||||||
<button (click)="saveCommentEdit(music.id, comment.id)" class="btn btn-primary btn-xs">Save</button>
|
|
||||||
<button (click)="cancelCommentEdit()" class="btn btn-secondary btn-xs">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="comment-content" [innerHTML]="sanitizeService.sanitizeHtml(comment.content)"></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
} @empty {
|
|
||||||
<div class="no-comments">No comments yet. Be the first to comment!</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -2014,4 +1970,21 @@ export class MusicListComponent implements OnInit {
|
|||||||
alert('Failed to submit suggestion. Please try again.');
|
alert('Failed to submit suggestion. Please try again.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCommentEdit(musicId: string, event: { commentId: string; content: string }) {
|
||||||
|
this.commentsService.updateCommentOnMusic(musicId, event.commentId, event.content).subscribe({
|
||||||
|
next: (updatedComment) => {
|
||||||
|
this.comments.set({
|
||||||
|
...this.comments(),
|
||||||
|
[musicId]: (this.comments()[musicId] || []).map(c =>
|
||||||
|
c.id === event.commentId ? updatedComment : c
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommentsSignal(musicId: string) {
|
||||||
|
return signal(this.comments()[musicId] || []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,12 +14,13 @@ import { SanitizeService } from '../../services/sanitize.service';
|
|||||||
import { SuggestionService } from '../../services/suggestion.service';
|
import { SuggestionService } from '../../services/suggestion.service';
|
||||||
import { PaginationComponent } from '../shared/pagination.component';
|
import { PaginationComponent } from '../shared/pagination.component';
|
||||||
import { LikeButtonComponent } from '../shared/like-button.component';
|
import { LikeButtonComponent } from '../shared/like-button.component';
|
||||||
|
import { CommentDisplayComponent } from '../comment-display/comment-display.component';
|
||||||
import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, SuggestionEntity, Link } from '@library/shared-types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-shows-list',
|
selector: 'app-shows-list',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent],
|
imports: [CommonModule, FormsModule, PaginationComponent, LikeButtonComponent, CommentDisplayComponent],
|
||||||
template: `
|
template: `
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header-section">
|
<div class="header-section">
|
||||||
@@ -604,56 +605,11 @@ import { Show, ShowStatus, ShowType, CreateShowDto, UpdateShowDto, Comment, Sugg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (commentsLoading()[show.id]) {
|
<app-comment-display
|
||||||
<div class="comments-loading">Loading comments...</div>
|
[comments]="getCommentsSignal(show.id)"
|
||||||
} @else {
|
(onEdit)="handleCommentEdit(show.id, $event)"
|
||||||
@for (comment of comments()[show.id] || []; track comment.id) {
|
(onDelete)="deleteComment(show.id, $event)"
|
||||||
<div class="comment">
|
/>
|
||||||
<div class="comment-header">
|
|
||||||
@if (comment.user.avatar) {
|
|
||||||
<img [src]="comment.user.avatar" [alt]="comment.user.username" class="comment-avatar">
|
|
||||||
}
|
|
||||||
<span class="comment-author">{{ comment.user.username }}</span>
|
|
||||||
@if (comment.user.inDiscord) {
|
|
||||||
<span class="discord-badge">Discord</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isVip) {
|
|
||||||
<span class="vip-badge">VIP</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isMod) {
|
|
||||||
<span class="mod-badge">Mod</span>
|
|
||||||
}
|
|
||||||
@if (comment.user.isStaff) {
|
|
||||||
<span class="staff-badge">Staff</span>
|
|
||||||
}
|
|
||||||
<span class="comment-date">{{ formatDate(comment.createdAt) }}</span>
|
|
||||||
@if (canEditComment(comment)) {
|
|
||||||
<button (click)="startEditComment(show.id, comment)" class="btn btn-secondary btn-xs">Edit</button>
|
|
||||||
}
|
|
||||||
@if (canDeleteComment(comment)) {
|
|
||||||
<button (click)="deleteComment(show.id, comment.id)" class="btn btn-danger btn-xs">Delete</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@if (editingCommentId() === comment.id) {
|
|
||||||
<div class="comment-edit-form">
|
|
||||||
<textarea
|
|
||||||
[(ngModel)]="editCommentContent"
|
|
||||||
name="editComment"
|
|
||||||
rows="3"
|
|
||||||
></textarea>
|
|
||||||
<div class="comment-edit-actions">
|
|
||||||
<button (click)="saveCommentEdit(show.id, comment.id)" class="btn btn-primary btn-xs">Save</button>
|
|
||||||
<button (click)="cancelCommentEdit()" class="btn btn-secondary btn-xs">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<div class="comment-content" [innerHTML]="sanitizeService.sanitizeHtml(comment.content)"></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
} @empty {
|
|
||||||
<div class="no-comments">No comments yet. Be the first to comment!</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -1783,4 +1739,21 @@ export class ShowsListComponent implements OnInit {
|
|||||||
alert('Failed to submit suggestion. Please try again.');
|
alert('Failed to submit suggestion. Please try again.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCommentEdit(showId: string, event: { commentId: string; content: string }) {
|
||||||
|
this.commentsService.updateCommentOnShow(showId, event.commentId, event.content).subscribe({
|
||||||
|
next: (updatedComment) => {
|
||||||
|
this.comments.set({
|
||||||
|
...this.comments(),
|
||||||
|
[showId]: (this.comments()[showId] || []).map(c =>
|
||||||
|
c.id === event.commentId ? updatedComment : c
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommentsSignal(showId: string) {
|
||||||
|
return signal(this.comments()[showId] || []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user