Files
message-inbox-web-frontend-sdk/apps/web/components/overlays/NotificationsPanel.tsx
T
2026-07-17 21:48:37 +05:30

59 lines
3.2 KiB
TypeScript

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useNotifications } from "@lynkd/messaging-inbox-sdk";
import { useUi } from "@/lib/ui-state";
import { relativeTime } from "@/lib/format";
import { AtSign, Heart, LifeBuoy, Bell, MessageSquare, X, CheckCheck } from "lucide-react";
const kindIcon: Record<string, typeof Bell> = {
MENTION: AtSign, REACTION: Heart, SUPPORT_UPDATE: LifeBuoy, THREAD_REPLY: MessageSquare, SYSTEM: Bell,
};
export function NotificationsPanel() {
const { notifOpen, setNotifOpen } = useUi();
const { notifications } = useNotifications();
const router = useRouter();
const [readIds, setReadIds] = useState<Set<string>>(new Set());
if (!notifOpen) return null;
const isRead = (id: string, base?: boolean) => base || readIds.has(id);
const markAll = () => setReadIds(new Set(notifications.map((n) => n.id)));
return (
<div className="fixed inset-0 z-40" onClick={() => setNotifOpen(false)}>
<div className="absolute right-3 top-[58px] w-[min(360px,calc(100vw-1.5rem))] overflow-hidden rounded-card border border-border bg-elevated shadow-pop animate-slide-up" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-between border-b border-border px-4 py-3">
<span className="text-[15px] font-bold">Notifications</span>
<div className="flex items-center gap-1">
<button onClick={markAll} title="Mark all as read" className="flex items-center gap-1 rounded-control px-2 py-1 text-[11.5px] font-semibold text-muted hover:bg-hover hover:text-ink"><CheckCheck size={14} /> Mark all read</button>
<button onClick={() => setNotifOpen(false)} className="text-muted hover:text-ink"><X size={16} /></button>
</div>
</div>
<div className="max-h-[60vh] overflow-y-auto">
{notifications.map((n) => {
const Icon = kindIcon[n.kind] ?? Bell;
const read = isRead(n.id, n.read);
return (
<button
key={n.id}
onClick={() => { setReadIds((s) => new Set(s).add(n.id)); setNotifOpen(false); if (n.channelId) router.push(`/c/${n.channelId}${n.messageId ? `?msg=${n.messageId}` : ""}`); else router.push("/inbox"); }}
className="flex w-full items-start gap-3 border-b border-border px-4 py-3 text-left hover:bg-hover"
>
<span className="mt-0.5 grid h-8 w-8 shrink-0 place-items-center rounded-control bg-accent-soft text-accent"><Icon size={15} /></span>
<span className="min-w-0 flex-1">
<span className="text-[13px]"><b className="text-ink">{n.title}</b> <span className="text-muted">{n.body}</span></span>
<span className="mt-0.5 block text-[11px] text-dim">{relativeTime(n.ts)}</span>
</span>
{!read && <span className="mt-1 h-2 w-2 shrink-0 rounded-full bg-accent" />}
</button>
);
})}
</div>
<button onClick={() => { setNotifOpen(false); router.push("/inbox"); }} className="w-full py-2.5 text-center text-[13px] font-semibold text-accent hover:bg-hover">Open Inbox </button>
</div>
</div>
);
}