Files
CapaKraken/packages/api/src/router/comment-support.ts
T

113 lines
2.6 KiB
TypeScript

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<string>();
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,
}));
}