Files
twotalesanimation 7a7088f2ad feat(shot-status): flag shots with new comments until marked read
A per-user ShotCommentRead marker records how far each person has read a
shot's comments. Comments and replies from others after the marker put an
orange "!" beside the shot code; its popover lists them and only "Mark
read" clears it. A chip filters the board to flagged shots and marks all
read. The migration marks existing comments as read so the board starts
clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 09:06:25 +02:00

43 lines
1.5 KiB
TypeScript

"use server";
import { auth } from "@/auth";
import { revalidatePath } from "next/cache";
import { z } from "zod";
import {
getUnreadCommentsForShot,
getUnreadCommentSummaries,
markShotCommentsRead as markRead,
} from "@/lib/comment-reads";
export async function listUnreadShotComments(shotId: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
return getUnreadCommentsForShot(session.user.id, z.string().min(1).parse(shotId));
}
const markSchema = z.object({
shotId: z.string().min(1),
upTo: z.string().datetime(),
});
export async function markShotCommentsRead(shotId: string, upTo: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
const parsed = markSchema.parse({ shotId, upTo });
await markRead(session.user.id, parsed.shotId, new Date(parsed.upTo));
revalidatePath("/shot-status");
return { success: true };
}
/** Mark every flagged shot in a project read, up to what each one had when checked. */
export async function markProjectCommentsRead(projectId: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
const summaries = await getUnreadCommentSummaries(session.user.id, z.string().min(1).parse(projectId));
for (const [shotId, s] of Object.entries(summaries)) {
await markRead(session.user.id, shotId, new Date(s.latestAt));
}
revalidatePath("/shot-status");
return { success: true, count: Object.keys(summaries).length };
}