From 7e0e7baef3b07ecb62e110a9a39ab998b6f12f8f Mon Sep 17 00:00:00 2001 From: ash1shkumar Date: Sat, 6 Jun 2026 01:29:12 +0530 Subject: [PATCH] refactor: centralize notification reconciliation workflow --- .../app/components/NotificationsPanel.tsx | 366 +++++++++++------- 1 file changed, 224 insertions(+), 142 deletions(-) diff --git a/frontend/app/components/NotificationsPanel.tsx b/frontend/app/components/NotificationsPanel.tsx index 59f4a6a..f086976 100644 --- a/frontend/app/components/NotificationsPanel.tsx +++ b/frontend/app/components/NotificationsPanel.tsx @@ -59,38 +59,79 @@ function markAllNotificationsRead( })); } +function reconcileNotifications( + notifications: Notification[], + updater: ( + notifications: Notification[] + ) => Notification[] +) { + return updater(notifications); +} + +function getUnreadCount( + notifications: Notification[] +) { + return notifications.reduce( + (count, notification) => + notification.unread + ? count + 1 + : count, + 0 + ); +} + export default function NotificationsPanel() { const [open, setOpen] = useState(false); const router = useRouter(); const [notifications, setNotifications] = - useState(initialNotifications); - - const panelRef = useRef(null); + useState( + initialNotifications + ); + + const panelRef = + useRef( + null + ); useEffect(() => { - function handleOutsideClick(event: MouseEvent) { + function handleOutsideClick( + event: MouseEvent + ) { if ( panelRef.current && - !panelRef.current.contains(event.target as Node) + !panelRef.current.contains( + event.target as Node + ) ) { setOpen(false); } } - function handleEscape(event: KeyboardEvent) { + + function handleEscape( + event: KeyboardEvent + ) { if (event.key === "Escape") { setOpen(false); } } - document.addEventListener("mousedown", handleOutsideClick); - window.addEventListener("keydown", handleEscape); + document.addEventListener( + "mousedown", + handleOutsideClick + ); + + window.addEventListener( + "keydown", + handleEscape + ); return () => { document.removeEventListener( "mousedown", handleOutsideClick ); + window.removeEventListener( "keydown", handleEscape @@ -98,158 +139,199 @@ export default function NotificationsPanel() { }; }, []); - const unreadCount = notifications.reduce( - (count, notification) => - notification.unread ? count + 1 : count, - 0 - ); + const unreadCount = + getUnreadCount( + notifications + ); function markAllAsRead() { - setNotifications(markAllNotificationsRead); + setNotifications( + (current) => + reconcileNotifications( + current, + markAllNotificationsRead + ) + ); } return ( -
- - - {open && ( -
+ setOpen( + (prev) => !prev + ) + } className=" - absolute right-0 top-14 z-50 - w-[380px] - overflow-hidden rounded-2xl - border border-white/20 - bg-white - backdrop-blur-xl - shadow-2xl - animate-in fade-in zoom-in-95 + relative rounded-full p-2 + text-on-surface-variant + hover:bg-surface-container-low + transition-all duration-200 + active:scale-90 " + type="button" > -
-
-

- Notifications -

- -

- Recent activity and updates -

-
+ + notifications + - -
- -
- {notifications.length === 0 ? ( -
- - notifications_off - + {unreadCount} + + )} + -

- No notifications yet -

+ {open && ( +
+
+
+

+ Notifications +

-

- You're all caught up. +

+ Recent activity and updates

- ) : ( - notifications.map((notification) => ( - +
+ +
+ {notifications.length === + 0 ? ( +
+ + notifications_off + + +

+ No notifications yet +

+ +

+ You're all caught + up. +

+
+ ) : ( + notifications.map( + ( + notification + ) => ( + - )) - )} + onClick={() => { + setNotifications( + ( + current + ) => + reconcileNotifications( + current, + ( + notifications + ) => + markNotificationRead( + notifications, + notification.id + ) + ) + ); + + setOpen( + false + ); + + router.push( + notification.href + ); + }} + className={` + flex w-full items-start gap-3 + border-b border-slate-200 + px-5 py-4 text-left + transition-all duration-200 + hover:bg-slate-100 + ${ + notification.unread + ? "bg-slate-50" + : "bg-white" + } + `} + > +
+ +
+
+

+ { + notification.title + } +

+ + + { + notification.time + } + +
+ +

+ { + notification.description + } +

+
+ + ) + ) + )} +
-
- )} -
-); + )} +
+ ); } \ No newline at end of file