- user-notifications collection (per-user inbox, owner-scoped read) - notifyUser() fire-and-forget helper + notification label mapping - NotificationsBell in the site header with 30s polling, unread badge, mark-read on click, and "Mark all read" - markNotificationsRead / markAllNotificationsRead server actions
81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
"use server";
|
|
|
|
import config from "@payload-config";
|
|
import { getPayload } from "payload";
|
|
|
|
export interface NotificationActionResult {
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
export async function markNotificationsRead(ids: number[]): Promise<NotificationActionResult> {
|
|
try {
|
|
const payloadConfig = await config;
|
|
const payload = await getPayload({ config: payloadConfig });
|
|
const { headers } = await import("next/headers");
|
|
const hdrs = await headers();
|
|
const { user } = await payload.auth({ headers: hdrs, canSetHeaders: false });
|
|
|
|
if (!user) return { success: false, error: "You must be logged in." };
|
|
|
|
const cleanIds = [...new Set(ids.filter((id) => Number.isFinite(id) && id > 0))];
|
|
if (cleanIds.length === 0) return { success: true };
|
|
|
|
// Query with access control so users can only touch their own notifications.
|
|
const found = await payload.find({
|
|
collection: "user-notifications",
|
|
where: { id: { in: cleanIds } },
|
|
limit: Math.min(cleanIds.length, 100),
|
|
depth: 0,
|
|
});
|
|
|
|
for (const doc of found.docs) {
|
|
if (!doc.read) {
|
|
await payload.update({
|
|
collection: "user-notifications",
|
|
id: doc.id,
|
|
data: { read: true },
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
});
|
|
}
|
|
}
|
|
|
|
return { success: true };
|
|
} catch {
|
|
return { success: false, error: "Failed to update notifications." };
|
|
}
|
|
}
|
|
|
|
export async function markAllNotificationsRead(): Promise<NotificationActionResult> {
|
|
try {
|
|
const payloadConfig = await config;
|
|
const payload = await getPayload({ config: payloadConfig });
|
|
const { headers } = await import("next/headers");
|
|
const hdrs = await headers();
|
|
const { user } = await payload.auth({ headers: hdrs, canSetHeaders: false });
|
|
|
|
if (!user) return { success: false, error: "You must be logged in." };
|
|
|
|
const unread = await payload.find({
|
|
collection: "user-notifications",
|
|
where: { read: { equals: false } },
|
|
limit: 100,
|
|
depth: 0,
|
|
});
|
|
|
|
for (const doc of unread.docs) {
|
|
await payload.update({
|
|
collection: "user-notifications",
|
|
id: doc.id,
|
|
data: { read: true },
|
|
overrideAccess: true,
|
|
depth: 0,
|
|
});
|
|
}
|
|
|
|
return { success: true };
|
|
} catch {
|
|
return { success: false, error: "Failed to update notifications." };
|
|
}
|
|
}
|