import { TRPCError } from "@trpc/server"; export const commentAuthorSelect = { id: true, name: true, email: true, image: true, } as const; export const commentWithAuthorInclude = { author: { select: commentAuthorSelect }, } as const; export const commentThreadInclude = { author: { select: commentAuthorSelect }, replies: { include: { author: { select: commentAuthorSelect }, }, orderBy: { createdAt: "asc" as const }, }, } as const; export function parseCommentMentions(body: string): string[] { const regex = /@\[([^\]]+)\]\(([^)]+)\)/g; const ids = new Set(); let match: RegExpExecArray | null; while ((match = regex.exec(body)) !== null) { ids.add(match[2]!); } return Array.from(ids); } export function buildCommentCreateData(input: { entityType: string; entityId: string; parentId?: string | undefined; authorId: string; body: string; mentions: string[]; }) { return { entityType: input.entityType, entityId: input.entityId, ...(input.parentId !== undefined ? { parentId: input.parentId } : {}), authorId: input.authorId, body: input.body, mentions: input.mentions, }; } export function commentBelongsToEntity(input: { comment: { entityType: string; entityId: string; }; entityType: string; entityId: string; }): boolean { return ( input.comment.entityType === input.entityType && input.comment.entityId === input.entityId ); } export function assertCommentManageableByActor(input: { authorId: string; actorUserId: string; isAdmin: boolean; action: "resolve" | "delete"; }) { if (input.authorId === input.actorUserId || input.isAdmin) { return; } throw new TRPCError({ code: "FORBIDDEN", message: input.action === "resolve" ? "Only the comment author or an admin can resolve comments" : "Only the comment author or an admin can delete comments", }); } function truncateCommentNotificationBody(body: string): string { return body.length > 120 ? `${body.slice(0, 120)}...` : body; } export function buildCommentMentionNotifications(input: { mentionedUserIds: string[]; authorName: string; body: string; entityId: string; entityType: string; senderId: string; link: string; }) { const truncatedBody = truncateCommentNotificationBody(input.body); return input.mentionedUserIds.map((userId) => ({ userId, type: "COMMENT_MENTION" as const, title: `${input.authorName} mentioned you in a comment`, body: truncatedBody, entityId: input.entityId, entityType: input.entityType, senderId: input.senderId, link: input.link, channel: "in_app" as const, })); }