+
+
{t("title")}
+ {unreadCount > 0 && (
+
+ )}
+
+
+ {loading ? (
+
{t("loading")}
+ ) : error && items.length === 0 ? (
+
+
{t("loadFailed")}
+
+
+ ) : items.length === 0 ? (
+
{t("empty")}
+ ) : (
+
+ {items.map((n) => (
+
{ if (!n.read) void markRead(n.id); }}
+ className={cn(
+ "flex items-start gap-3 p-4 hover:bg-accent transition-colors",
+ !n.read && "bg-accent/40"
+ )}
+ >
+
+
+ {n.actorName.slice(0, 2).toUpperCase()}
+
+
+
+ {t(n.type, { name: n.actorName })}
+
+
{timeAgo(n.createdAt, tSocial)}
+
+ {!n.read &&
}
+
+ ))}
+
+ )}
+
+ {error && items.length > 0 && (
+
{t("loadFailed")}
+ )}
+
+ {(hasMore || (error && items.length > 0)) && (
+
+
+
+ )}
+
+ );
+}
diff --git a/apps/web/components/social/notification-bell.tsx b/apps/web/components/social/notification-bell.tsx
index 06fcfec..7f3af7b 100644
--- a/apps/web/components/social/notification-bell.tsx
+++ b/apps/web/components/social/notification-bell.tsx
@@ -13,10 +13,11 @@ import {
} from "@/components/ui/dropdown-menu";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
+import { notificationHref, type NotificationType } from "@/lib/notification-links";
type Notification = {
id: string;
- type: "follow" | "comment" | "reply" | "reaction" | "rating" | "mention";
+ type: NotificationType;
recipeId: string | null;
commentId: string | null;
read: boolean;
@@ -26,12 +27,6 @@ type Notification = {
actorUsername: string | null;
};
-function notificationHref(n: Notification): string {
- if (n.type === "follow") return n.actorUsername ? `/u/${n.actorUsername}` : "#";
- if (n.recipeId) return `/recipes/${n.recipeId}`;
- return "#";
-}
-
export function NotificationBell() {
const t = useTranslations("notifications");
const [notifications, setNotifications] = useState