import { CommandRunner } from '@task-stream/api/shared/application'; import { Injectable } from '@nestjs/common'; import { QueryBus } from '@nestjs/cqrs'; import { CommentReadModel, DecisionReadModel, } from '@task-stream/api/tracker/domain'; import { AddCommentCommand } from './commands/add-comment.command'; import { CommandResult } from './commands/command-result'; import { DeleteCommentCommand } from './commands/delete-comment.command'; import { EditCommentCommand } from './commands/edit-comment.command'; import { ReactToCommentCommand } from './commands/react-to-comment.command'; import { MarkDecisionCommand } from './commands/mark-decision.command'; import { Page } from './ports/find-issues.repository'; import { GetCommentsQuery } from './queries/get-comments.query'; import { GetIssueDecisionsQuery, SearchDecisionsQuery, } from './queries/decisions.query'; @Injectable() export class CommentsService { constructor( private readonly commands: CommandRunner, private readonly queryBus: QueryBus, ) {} add( actorId: string, issueId: string, body: string, replyTo: string | null = null, ): Promise { return this.commands.execute( new AddCommentCommand(actorId, issueId, body, replyTo), ); } edit( actorId: string, commentId: string, body: string, ): Promise { return this.commands.execute( new EditCommentCommand(actorId, commentId, body), ); } /** Adds (`on`) or takes back one's reaction (👍…). */ react( actorId: string, commentId: string, emoji: string, on: boolean, ): Promise { return this.commands.execute( new ReactToCommentCommand(actorId, commentId, emoji, on), ); } /** Marks it as a decision of its ticket (`on`) or unmarks it: the team does. */ markDecision( actorId: string, commentId: string, on: boolean, ): Promise { return this.commands.execute( new MarkDecisionCommand(actorId, commentId, on), ); } /** A ticket's decisions, oldest first. */ decisions( viewerId: string | null, issueId: string, ): Promise { return this.queryBus.execute(new GetIssueDecisionsQuery(viewerId, issueId)); } /** Decisions by their text: of a project, or of all mine without one. */ searchDecisions( viewerId: string | null, filter: { projectId?: string; search?: string; limit?: number }, ): Promise { return this.queryBus.execute( new SearchDecisionsQuery(viewerId, { limit: 20, ...filter }), ); } delete(actorId: string, commentId: string): Promise { return this.commands.execute(new DeleteCommentCommand(actorId, commentId)); } list( viewerId: string | null, issueId: string, limit?: number, offset?: number, ): Promise> { return this.queryBus.execute( new GetCommentsQuery(viewerId, issueId, limit, offset), ); } }