All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Removed the useEffectEvent for fetching notifications and integrated the fetching logic directly into the useEffect hooks for improved clarity and performance. - Updated dependency arrays to ensure notifications are fetched correctly based on authentication and connection status, enhancing responsiveness to state changes.
201 lines
5.8 KiB
JavaScript
201 lines
5.8 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,
|
|
connected
|
|
} = 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
|
|
|
|
// Initial load / when we become authenticated and connected
|
|
useEffect(() => {
|
|
if (!authenticated || !connected) return
|
|
fetchNotifications()
|
|
}, [authenticated, connected, fetchNotifications])
|
|
|
|
// Refresh when the notification center opens
|
|
useEffect(() => {
|
|
if (!notificationCenterVisible || !authenticated || !connected) return
|
|
fetchNotifications()
|
|
}, [notificationCenterVisible, authenticated, connected, 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}
|
|
showDetails={false}
|
|
largeTitleSpacing={true}
|
|
/>
|
|
),
|
|
icon: null,
|
|
duration: 5,
|
|
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 }
|