Files
twotalesanimation 0fbe856dce Initial commit
2026-05-19 22:20:29 +02:00

155 lines
4.6 KiB
TypeScript

"use server";
import { auth } from "@/auth";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import { notifyFeedbackAdded, notifyCommentReply } from "@/lib/notifications";
import { slackNotifyNewFeedback } from "@/lib/slack";
const addCommentSchema = z.object({
versionId: z.string().cuid(),
frameNumber: z.number().int().min(0),
timestamp: z.number().min(0),
text: z.string().min(1).max(5000),
});
export async function addComment(data: z.infer<typeof addCommentSchema>) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
const parsed = addCommentSchema.parse(data);
const comment = await db.comment.create({
data: {
versionId: parsed.versionId,
authorId: session.user.id,
frameNumber: parsed.frameNumber,
timestamp: parsed.timestamp,
text: parsed.text,
},
include: {
author: { select: { id: true, name: true, email: true, image: true, role: true } },
replies: true,
annotations: true,
},
});
// Notify the shot artist if commenter is not the artist
const version = await db.version.findUnique({
where: { id: parsed.versionId },
include: {
shot: { include: { project: true } },
task: { include: { project: true, shot: true } },
},
});
const slackWebhook =
version?.shot?.project?.slackWebhook ??
version?.task?.project?.slackWebhook ??
null;
const shotCode =
version?.shot?.shotCode ??
version?.task?.shot?.shotCode ??
version?.task?.title ??
"Task";
if (version?.shot?.artistId && version.shot.artistId !== session.user.id) {
const user = await db.user.findUnique({ where: { id: session.user.id } });
await notifyFeedbackAdded({
artistId: version.shot.artistId,
shotCode: version.shot.shotCode,
frameNumber: parsed.frameNumber,
versionId: parsed.versionId,
authorName: user?.name ?? session.user.email ?? "Someone",
});
}
// Slack notification
if (slackWebhook) {
const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
const user = await db.user.findUnique({ where: { id: session.user.id } });
await slackNotifyNewFeedback(slackWebhook, {
shotCode,
frameNumber: parsed.frameNumber,
authorName: user?.name ?? "Someone",
commentText: parsed.text,
reviewUrl: `${appUrl}/review/${parsed.versionId}`,
});
}
revalidatePath(`/review/${parsed.versionId}`);
return { success: true, comment };
}
export async function addReply(commentId: string, text: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
if (!text?.trim()) throw new Error("Reply text is required");
const reply = await db.commentReply.create({
data: {
commentId,
authorId: session.user.id,
text: text.trim(),
},
include: {
author: { select: { id: true, name: true, email: true, image: true, role: true } },
},
});
// Notify the original comment author
const comment = await db.comment.findUnique({
where: { id: commentId },
include: {
version: { include: { shot: true, task: { select: { title: true } } } },
},
});
if (comment && comment.authorId && comment.authorId !== session.user.id) {
const user = await db.user.findUnique({ where: { id: session.user.id } });
await notifyCommentReply({
commentAuthorId: comment.authorId,
replierName: user?.name ?? "Someone",
shotCode: comment.version.shot?.shotCode ?? comment.version.task?.title ?? "Task",
frameNumber: comment.frameNumber,
versionId: comment.versionId,
});
}
revalidatePath(`/review/${comment?.versionId}`);
return { success: true, reply };
}
export async function resolveComment(commentId: string, resolved: boolean) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
const comment = await db.comment.update({
where: { id: commentId },
data: { isResolved: resolved },
});
revalidatePath(`/review/${comment.versionId}`);
return { success: true };
}
export async function deleteComment(commentId: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
const comment = await db.comment.findUnique({ where: { id: commentId } });
if (!comment) throw new Error("Comment not found");
// Only the author or admin can delete
if (comment.authorId !== session.user.id && session.user.role !== "ADMIN") {
throw new Error("Unauthorized");
}
await db.comment.delete({ where: { id: commentId } });
revalidatePath(`/review/${comment.versionId}`);
return { success: true };
}