diff --git a/src/components/layout/NotificationCenter.tsx b/src/components/layout/NotificationCenter.tsx index 49f08b1..4252b39 100644 --- a/src/components/layout/NotificationCenter.tsx +++ b/src/components/layout/NotificationCenter.tsx @@ -4,7 +4,7 @@ import { requestPermission, sendNotification, } from "@tauri-apps/plugin-notification"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { listNotifications, @@ -39,8 +39,12 @@ async function showSystemNotification(notification: OrchaiNotification) { export default function NotificationCenter() { const navigate = useNavigate(); const { projectId } = useParams(); + const containerRef = useRef(null); const [open, setOpen] = useState(false); const [notifications, setNotifications] = useState([]); + const [filter, setFilter] = useState<"all" | "unread" | "errors" | "fixes">( + "all" + ); async function loadNotifications() { if (!projectId) { @@ -89,11 +93,53 @@ export default function NotificationCenter() { }; }, [projectId]); + useEffect(() => { + if (!open) { + return; + } + + function handleOutsideClick(event: MouseEvent) { + const target = event.target as Node | null; + if (!containerRef.current || !target) { + return; + } + if (!containerRef.current.contains(target)) { + setOpen(false); + } + } + + function handleEscape(event: KeyboardEvent) { + if (event.key === "Escape") { + setOpen(false); + } + } + + document.addEventListener("mousedown", handleOutsideClick); + document.addEventListener("keydown", handleEscape); + return () => { + document.removeEventListener("mousedown", handleOutsideClick); + document.removeEventListener("keydown", handleEscape); + }; + }, [open]); + const unreadCount = useMemo( () => notifications.filter((n) => !n.read).length, [notifications] ); + const filteredNotifications = useMemo(() => { + switch (filter) { + case "unread": + return notifications.filter((n) => !n.read); + case "errors": + return notifications.filter((n) => n.notification_type === "Error"); + case "fixes": + return notifications.filter((n) => n.notification_type === "FixReady"); + default: + return notifications; + } + }, [filter, notifications]); + async function handleOpenNotification(notification: OrchaiNotification) { if (!notification.read) { try { @@ -130,7 +176,7 @@ export default function NotificationCenter() { } return ( -
+
+
+ {[ + { id: "all", label: "All" }, + { id: "unread", label: "Unread" }, + { id: "errors", label: "Errors" }, + { id: "fixes", label: "Fixes" }, + ].map((item) => ( + + ))} +
+
- {notifications.length === 0 ? ( + {filteredNotifications.length === 0 ? (
No notifications.
) : ( - notifications.map((notification) => ( + filteredNotifications.map((notification) => (