import {
createContext,
useState,
useContext,
useCallback,
useEffect
} from 'react'
import { useLocation } from 'react-router-dom'
import { notification, Drawer } from 'antd'
import PropTypes from 'prop-types'
import { AuthContext } from './AuthContext'
import { ApiServerContext } from './ApiServerContext'
import NotificationCenter from '../common/NotificationCenter'
import Notification from '../common/Notification'
import { useMediaQuery } from 'react-responsive'
const NotificationContext = createContext()
const NotificationProvider = ({ children }) => {
const [api, contextHolder] = notification.useNotification()
const location = useLocation()
const { authenticated } = useContext(AuthContext)
const {
showError,
fetchNotificationsApi,
markNotificationAsReadApi,
markAllNotificationsAsReadApi,
deleteNotificationApi,
deleteAllNotificationsApi,
registerNotificationListener
} = useContext(ApiServerContext)
const [notificationCenterVisible, setNotificationCenterVisible] =
useState(false)
const [notifications, setNotifications] = useState([])
const [notificationsLoading, setNotificationsLoading] = useState(false)
const isMobile = useMediaQuery({ maxWidth: 768 })
const fetchNotifications = useCallback(async () => {
if (!authenticated) return []
setNotificationsLoading(true)
try {
const data = await fetchNotificationsApi()
setNotifications(data)
return data
} catch (err) {
console.error(err)
const is404 = err?.response?.status === 404
if (is404) {
setNotifications([])
} else {
showError(err, () => fetchNotifications())
}
return []
} finally {
setNotificationsLoading(false)
}
}, [authenticated, fetchNotificationsApi, showError])
const markNotificationAsRead = useCallback(
async (notificationId) => {
try {
await markNotificationAsReadApi(notificationId)
setNotifications((prev) =>
prev.map((n) => (n._id === notificationId ? { ...n, read: true } : n))
)
} catch (err) {
console.error(err)
showError(err, () => markNotificationAsRead(notificationId))
}
},
[markNotificationAsReadApi, showError]
)
const markAllNotificationsAsRead = useCallback(async () => {
try {
await markAllNotificationsAsReadApi()
setNotifications((prev) => prev.map((n) => ({ ...n, read: true })))
} catch (err) {
console.error(err)
showError(err, () => markAllNotificationsAsRead())
}
}, [markAllNotificationsAsReadApi, showError])
const deleteNotification = useCallback(
async (notificationId) => {
try {
await deleteNotificationApi(notificationId)
setNotifications((prev) => prev.filter((n) => n._id !== notificationId))
} catch (err) {
console.error(err)
showError(err, () => deleteNotification(notificationId))
}
},
[deleteNotificationApi, showError]
)
const deleteAllNotifications = useCallback(async () => {
try {
await deleteAllNotificationsApi()
setNotifications([])
} catch (err) {
console.error(err)
showError(err, () => deleteAllNotifications())
}
}, [deleteAllNotificationsApi, showError])
const toggleNotificationCenter = useCallback(() => {
setNotificationCenterVisible((prev) => !prev)
}, [])
const unreadCount = notifications.filter((n) => !n.read).length
useEffect(() => {
if (authenticated && notificationCenterVisible) {
fetchNotifications()
}
}, [authenticated, notificationCenterVisible, fetchNotifications])
useEffect(() => {
if (authenticated) {
fetchNotifications()
}
}, [authenticated, fetchNotifications])
useEffect(() => {
setNotificationCenterVisible(false)
}, [location.pathname])
useEffect(() => {
if (!authenticated || !registerNotificationListener) return
const handleNotification = (notif) => {
setNotifications((prev) => {
const exists = prev.some((n) => n._id === notif._id)
if (exists) return prev
return [{ ...notif, read: false }, ...prev]
})
const show = api.open
show({
message: (
),
icon: null,
duration: 3,
key: notif._id
})
}
const unregister = registerNotificationListener(handleNotification)
return unregister
}, [authenticated, registerNotificationListener, api, deleteNotification])
return (
{contextHolder}
{children}
setNotificationCenterVisible(false)}
open={notificationCenterVisible}
>
setNotificationCenterVisible(false)}
/>
)
}
NotificationProvider.propTypes = {
children: PropTypes.node.isRequired
}
export { NotificationContext, NotificationProvider }