import { listen } from "@tauri-apps/api/event"; import { isPermissionGranted, requestPermission, sendNotification, } from "@tauri-apps/plugin-notification"; import { useEffect, useMemo, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { listNotifications, markAllNotificationsRead, markNotificationRead, } from "../../lib/api"; import type { OrchaiNotification } from "../../lib/types"; type NewNotificationEvent = { notification: OrchaiNotification; }; async function showSystemNotification(notification: OrchaiNotification) { try { let permissionGranted = await isPermissionGranted(); if (!permissionGranted) { const permission = await requestPermission(); permissionGranted = permission === "granted"; } if (permissionGranted) { sendNotification({ title: notification.title, body: notification.message, }); } } catch { // Best effort only } } export default function NotificationCenter() { const navigate = useNavigate(); const { projectId } = useParams(); const [open, setOpen] = useState(false); const [notifications, setNotifications] = useState([]); async function loadNotifications() { if (!projectId) { setNotifications([]); return; } try { const items = await listNotifications(projectId, false); setNotifications(items); } catch { // Ignore load errors in layout chrome } } useEffect(() => { loadNotifications(); }, [projectId]); useEffect(() => { let unlisten: (() => void) | null = null; const setup = async () => { unlisten = await listen("new-notification", (event) => { const incoming = event.payload.notification; if (projectId && incoming.project_id !== projectId) { return; } setNotifications((prev) => { const withoutDuplicate = prev.filter((n) => n.id !== incoming.id); return [incoming, ...withoutDuplicate]; }); void showSystemNotification(incoming); }); }; void setup(); return () => { if (unlisten) { unlisten(); } }; }, [projectId]); const unreadCount = useMemo( () => notifications.filter((n) => !n.read).length, [notifications] ); async function handleOpenNotification(notification: OrchaiNotification) { if (!notification.read) { try { await markNotificationRead(notification.id); setNotifications((prev) => prev.map((n) => (n.id === notification.id ? { ...n, read: true } : n)) ); } catch { // ignore } } setOpen(false); if (notification.ticket_id) { navigate(`/tickets/${notification.ticket_id}`); return; } navigate(`/projects/${notification.project_id}`); } async function handleMarkAllRead() { if (!projectId) { return; } try { await markAllNotificationsRead(projectId); setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); } catch { // ignore } } return (
{open && (

Notifications

{notifications.length === 0 ? (
No notifications.
) : ( notifications.map((notification) => ( )) )}
)}
); }