diff --git a/prisma/migrations/20260710200857_comment_replies_spoiler/migration.sql b/prisma/migrations/20260710200857_comment_replies_spoiler/migration.sql new file mode 100644 index 0000000..f3b2dfc --- /dev/null +++ b/prisma/migrations/20260710200857_comment_replies_spoiler/migration.sql @@ -0,0 +1,9 @@ +-- AlterTable +ALTER TABLE "Comment" ADD COLUMN "isSpoiler" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "parentId" TEXT; + +-- CreateIndex +CREATE INDEX "Comment_parentId_idx" ON "Comment"("parentId"); + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Comment"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 34891db..b96c4fb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -188,7 +188,9 @@ model Comment { id String @id @default(uuid(7)) content String type CommentType + isSpoiler Boolean @default(false) userId String + parentId String? animeId String? mangaId String? tvShowId String? @@ -200,6 +202,8 @@ model Comment { updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) + parent Comment? @relation("CommentReplies", fields: [parentId], references: [id], onDelete: Cascade) + replies Comment[] @relation("CommentReplies") anime Anime? @relation(fields: [animeId], references: [id], onDelete: Cascade) manga Manga? @relation(fields: [mangaId], references: [id], onDelete: Cascade) tvShow TvShow? @relation(fields: [tvShowId], references: [id], onDelete: Cascade) @@ -218,6 +222,7 @@ model Comment { @@index([gameId]) @@index([bookId]) @@index([profileId]) + @@index([parentId]) } enum ReactionType { diff --git a/src/modules/comment/dto/create-comment.dto.ts b/src/modules/comment/dto/create-comment.dto.ts index 257d172..cca480b 100644 --- a/src/modules/comment/dto/create-comment.dto.ts +++ b/src/modules/comment/dto/create-comment.dto.ts @@ -1,9 +1,11 @@ import { ApiProperty } from "@nestjs/swagger"; import { CommentType } from "@prisma/generated/enums"; import { + IsBoolean, IsEnum, IsNotEmpty, IsOptional, + IsUUID, MaxLength, registerDecorator, ValidationArguments, @@ -48,6 +50,21 @@ export class CreateCommentDto { @ApiProperty({ type: "string", maxLength: 500 }) readonly content: string; + @IsOptional() + @IsBoolean() + @ApiProperty({ type: "boolean", required: false, default: false }) + readonly isSpoiler?: boolean; + + @IsOptional() + @IsUUID() + @ApiProperty({ + type: "string", + format: "uuid", + required: false, + description: "Parent comment id when replying", + }) + readonly parentId?: string; + @IsOptional() @CommentRequiredForType(CommentType.Anime) @ApiProperty({ diff --git a/src/modules/comment/service/comment.service.ts b/src/modules/comment/service/comment.service.ts index 66d98b5..e190fa2 100644 --- a/src/modules/comment/service/comment.service.ts +++ b/src/modules/comment/service/comment.service.ts @@ -53,24 +53,50 @@ export class CommentService { } async getComments({ page, itemsPerPage, ...getCommentsDto }: GetCommentsDto) { + const userSelect = { + id: true, + name: true, + username: true, + profile: { + select: { + id: true, + avatarUrl: true, + }, + }, + } as const; + + const reactionsInclude = { + orderBy: { createdAt: "desc" }, + select: { + id: true, + emoji: true, + createdAt: true, + user: { + select: { + id: true, + username: true, + }, + }, + }, + } as const; + const pagination = await this.databaseService.offsetPagination({ model: "comment", - where: { ...getCommentsDto }, + where: { ...getCommentsDto, parentId: null }, page, itemsPerPage, orderBy: { createdAt: "desc" }, include: { user: { - select: { - id: true, - name: true, - username: true, - profile: { - select: { - id: true, - avatarUrl: true, - }, + select: userSelect, + }, + replies: { + orderBy: { createdAt: "asc" }, + include: { + user: { + select: userSelect, }, + reactions: reactionsInclude, }, }, anime: { @@ -121,20 +147,7 @@ export class CommentService { imageUrl: true, }, }, - reactions: { - take: 3, - orderBy: { createdAt: "desc" }, - select: { - id: true, - emoji: true, - createdAt: true, - user: { - select: { - username: true, - }, - }, - }, - }, + reactions: reactionsInclude, }, });