198 lines
5.6 KiB
JavaScript
198 lines
5.6 KiB
JavaScript
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: (
|
|
<Notification
|
|
notification={notif}
|
|
onDelete={deleteNotification}
|
|
showCard={false}
|
|
showDelete={false}
|
|
showExtraInfo={false}
|
|
inlineIcon={true}
|
|
/>
|
|
),
|
|
icon: null,
|
|
duration: 3,
|
|
key: notif._id
|
|
})
|
|
}
|
|
const unregister = registerNotificationListener(handleNotification)
|
|
return unregister
|
|
}, [authenticated, registerNotificationListener, api, deleteNotification])
|
|
|
|
return (
|
|
<NotificationContext.Provider
|
|
value={{
|
|
notificationCenterVisible,
|
|
toggleNotificationCenter,
|
|
notifications,
|
|
fetchNotifications,
|
|
markNotificationAsRead,
|
|
markAllNotificationsAsRead,
|
|
deleteNotification,
|
|
deleteAllNotifications,
|
|
unreadCount,
|
|
notificationsLoading
|
|
}}
|
|
>
|
|
{contextHolder}
|
|
{children}
|
|
<Drawer
|
|
title='Notifications'
|
|
placement='right'
|
|
width={isMobile ? '100%' : 460}
|
|
onClose={() => setNotificationCenterVisible(false)}
|
|
open={notificationCenterVisible}
|
|
>
|
|
<NotificationCenter
|
|
visible={notificationCenterVisible}
|
|
onClose={() => setNotificationCenterVisible(false)}
|
|
/>
|
|
</Drawer>
|
|
</NotificationContext.Provider>
|
|
)
|
|
}
|
|
|
|
NotificationProvider.propTypes = {
|
|
children: PropTypes.node.isRequired
|
|
}
|
|
|
|
export { NotificationContext, NotificationProvider }
|