feat: replies to comments
A comment can answer another one (replyTo), one level deep: answering a reply joins the same thread, and it must be on the same ticket. Whoever wrote the comment gets a new REPLIED notification, and the activity says "respondió a un comentario". A comment deleted while it has replies stays, without its text (deleted: true), so the thread still reads; it goes with its last reply and does not count in commentCount. The ticket page shows threads with their replies indented and a "Responder" box; REST, GraphQL and the MCP (add_comment, get_issue) take and show replyTo. Existing comments are unchanged. Docs, Word and PDF updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -955,6 +955,17 @@ export class CommentBodyDto {
|
||||
body!: string;
|
||||
}
|
||||
|
||||
export class AddCommentDto extends CommentBodyDto {
|
||||
@ApiPropertyOptional({
|
||||
format: 'uuid',
|
||||
description:
|
||||
'Comment it answers (one level: answering a reply joins its thread)',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsUUID()
|
||||
replyTo?: string;
|
||||
}
|
||||
|
||||
// ---- Releases ---------------------------------------------------------------
|
||||
|
||||
export class CreateReleaseDto {
|
||||
|
||||
@@ -466,7 +466,21 @@ export class CommentDto {
|
||||
@ApiProperty({ format: 'uuid' }) id!: string;
|
||||
@ApiProperty({ format: 'uuid' }) issueId!: string;
|
||||
@ApiProperty({ type: UserRefDto }) author!: UserRefDto;
|
||||
@ApiProperty() body!: string;
|
||||
@ApiProperty({
|
||||
description: 'Empty on a deleted comment kept for its replies',
|
||||
})
|
||||
body!: string;
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
nullable: true,
|
||||
format: 'uuid',
|
||||
description: 'The top-level comment it answers; null for a top-level one',
|
||||
})
|
||||
replyTo!: string | null;
|
||||
@ApiProperty({
|
||||
description: 'Deleted, but kept (without its text) while it has replies',
|
||||
})
|
||||
deleted!: boolean;
|
||||
@ApiProperty({ format: 'date-time' }) createdAt!: string;
|
||||
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
|
||||
editedAt!: string | null;
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
BulkUpdateIssuesDto,
|
||||
ChangeStatusDto,
|
||||
ChecklistItemBodyDto,
|
||||
CommentBodyDto,
|
||||
AddCommentDto,
|
||||
CreateIssueDto,
|
||||
IssueSearchQueryDto,
|
||||
LinkIssueDto,
|
||||
@@ -529,18 +529,21 @@ export class IssuesController {
|
||||
|
||||
@Post('issues/:issue/comments')
|
||||
@ISSUE
|
||||
@ApiOperation({ summary: 'Comment on the ticket' })
|
||||
@ApiOperation({
|
||||
summary: 'Comment on the ticket, or answer a comment (replyTo)',
|
||||
})
|
||||
@ApiCreatedResponse({ type: CommandResultDto })
|
||||
@ApiCommandErrors()
|
||||
async addComment(
|
||||
@CurrentUser() me: User,
|
||||
@Param('issue') issue: string,
|
||||
@Body() body: CommentBodyDto,
|
||||
@Body() body: AddCommentDto,
|
||||
) {
|
||||
return this.comments.add(
|
||||
me.id,
|
||||
await this.refs.issueId(me.id, issue),
|
||||
body.body,
|
||||
body.replyTo ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user