Initial commit
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
"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 };
|
||||
}
|
||||
Reference in New Issue
Block a user