import React, { useEffect, useRef, useState } from 'react' export default function NotificationDropdown({ initialUnreadCount = 0, notificationsUrl = '/api/notifications' }) { const [open, setOpen] = useState(false) const [loading, setLoading] = useState(false) const [items, setItems] = useState([]) const [unreadCount, setUnreadCount] = useState(Number(initialUnreadCount || 0)) const rootRef = useRef(null) const csrfToken = typeof document !== 'undefined' ? document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') : null useEffect(() => { if (!open) return undefined const onDocumentClick = (event) => { if (rootRef.current && !rootRef.current.contains(event.target)) { setOpen(false) } } document.addEventListener('mousedown', onDocumentClick) return () => document.removeEventListener('mousedown', onDocumentClick) }, [open]) useEffect(() => { if (!open || items.length > 0) return setLoading(true) fetch(notificationsUrl, { headers: { Accept: 'application/json' }, credentials: 'same-origin', }) .then(async (response) => { if (!response.ok) throw new Error('Failed to load notifications') return response.json() }) .then((payload) => { setItems(Array.isArray(payload?.data) ? payload.data : []) setUnreadCount(Number(payload?.unread_count || 0)) }) .catch(() => {}) .finally(() => setLoading(false)) }, [items.length, notificationsUrl, open]) const markAllRead = async () => { try { await fetch('/api/notifications/read-all', { method: 'POST', headers: { Accept: 'application/json', 'X-CSRF-TOKEN': csrfToken || '', }, credentials: 'same-origin', }) setItems((current) => current.map((item) => ({ ...item, read: true }))) setUnreadCount(0) } catch { // Keep current state on failure. } } const markSingleRead = async (id) => { await fetch(`/api/notifications/${id}/read`, { method: 'POST', headers: { Accept: 'application/json', 'X-CSRF-TOKEN': csrfToken || '', }, credentials: 'same-origin', }) setItems((current) => current.map((item) => item.id === id ? { ...item, read: true } : item)) setUnreadCount((current) => Math.max(0, current - 1)) } const handleNotificationClick = async (event, item) => { if (!item?.id || item.read) return const href = event.currentTarget.getAttribute('href') if (!href) return event.preventDefault() try { await markSingleRead(item.id) } catch { // Continue to the destination even if marking as read fails. } window.location.assign(href) } return (
Recent follows, likes, comments, and unlocks.
{item.message}
{item.time_ago || ''}