diff --git a/plugins/notifications-backend/src/service/router.ts b/plugins/notifications-backend/src/service/router.ts index a31f47861f..3611d407a3 100644 --- a/plugins/notifications-backend/src/service/router.ts +++ b/plugins/notifications-backend/src/service/router.ts @@ -180,6 +180,14 @@ export async function createRouter( return; } await store.markRead({ user_ref: user, ids }); + + if (signalService) { + await signalService.publish({ + recipients: [user], + message: { action: 'refresh' }, + channel: 'notifications', + }); + } res.status(200).send({ ids }); }); @@ -191,6 +199,13 @@ export async function createRouter( return; } await store.markUnread({ user_ref: user, ids }); + if (signalService) { + await signalService.publish({ + recipients: [user], + message: { action: 'refresh' }, + channel: 'notifications', + }); + } res.status(200).send({ ids }); }); @@ -243,7 +258,6 @@ export async function createRouter( } const baseNotification = { - id: uuid(), title, description, link, @@ -252,7 +266,7 @@ export async function createRouter( }; for (const user of users) { - let notification = { ...baseNotification, userRef: user }; + let notification = { ...baseNotification, id: uuid(), userRef: user }; for (const processor of processors ?? []) { notification = processor.decorate ? await processor.decorate(notification) @@ -271,7 +285,7 @@ export async function createRouter( if (signalService) { await signalService.publish({ recipients: entityRef === null ? null : users, - message: { action: 'refresh' }, + message: { action: 'refresh', title, description, link }, channel: 'notifications', }); } diff --git a/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx b/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx index 20b106f6f2..41db91b9cf 100644 --- a/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx +++ b/plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx @@ -38,21 +38,29 @@ const useStyles = makeStyles(_theme => ({ export const NotificationsPage = () => { const [type, setType] = useState('unread'); + const [refresh, setRefresh] = React.useState(false); - const { loading, error, value, retry } = useNotificationsApi( + const { error, value, retry } = useNotificationsApi( api => api.getNotifications({ type }), [type], ); + useEffect(() => { + if (refresh) { + retry(); + setRefresh(false); + } + }, [refresh, setRefresh, retry]); + const { lastSignal } = useSignal('notifications'); useEffect(() => { if (lastSignal && lastSignal.action === 'refresh') { - retry(); + setRefresh(true); } - }, [lastSignal, retry]); + }, [lastSignal]); const onUpdate = () => { - retry(); + setRefresh(true); }; const styles = useStyles(); @@ -95,7 +103,6 @@ export const NotificationsPage = () => { diff --git a/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx b/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx index 255105a499..6dfa0ebb75 100644 --- a/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx +++ b/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx @@ -27,14 +27,51 @@ export const NotificationsSidebarItem = () => { api.getStatus(), ); const [unreadCount, setUnreadCount] = React.useState(0); + const [webNotificationPermission, setWebNotificationPermission] = + React.useState('default'); const notificationsRoute = useRouteRef(rootRouteRef); - const { lastSignal } = useSignal('notifications'); + const [webNotifications, setWebNotifications] = React.useState< + Notification[] + >([]); + const [refresh, setRefresh] = React.useState(false); + + useEffect(() => { + if ('Notification' in window && webNotificationPermission === 'default') { + window.Notification.requestPermission().then(permission => { + setWebNotificationPermission(permission); + }); + } + }, [webNotificationPermission]); + + useEffect(() => { + if (refresh) { + retry(); + setRefresh(false); + } + }, [refresh, retry]); + useEffect(() => { if (lastSignal && lastSignal.action === 'refresh') { - retry(); + if ( + webNotificationPermission === 'granted' && + 'title' in lastSignal && + 'description' in lastSignal && + 'link' in lastSignal + ) { + const notification = new Notification(lastSignal.title as string, { + body: lastSignal.description as string, + }); + notification.onclick = function (event) { + event.preventDefault(); + notification.close(); + window.open(lastSignal.link as string, '_blank'); + }; + setWebNotifications(prev => [...prev, notification]); + } + setRefresh(true); } - }, [lastSignal, retry]); + }, [lastSignal, webNotificationPermission]); useEffect(() => { if (!loading && !error && value) { @@ -42,6 +79,13 @@ export const NotificationsSidebarItem = () => { } }, [loading, error, value]); + document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') { + webNotifications.forEach(n => n.close()); + setWebNotifications([]); + } + }); + // TODO: Figure out if the count can be added to hasNotifications return ( ({ export const NotificationsTable = (props: { onUpdate: () => void; type: NotificationType; - loading?: boolean; notifications?: Notification[]; }) => { - const { notifications, type, loading } = props; + const { notifications, type } = props; const navigate = useNavigate(); const styles = useStyles(); const [selected, setSelected] = useState([]); @@ -111,10 +109,6 @@ export const NotificationsTable = (props: { ); }; - if (loading) { - return ; - } - return (