diff --git a/.changeset/ten-penguins-roll.md b/.changeset/ten-penguins-roll.md new file mode 100644 index 0000000000..60e0ae773a --- /dev/null +++ b/.changeset/ten-penguins-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Allow setting notification as read when opening snackbar or web notification link diff --git a/plugins/notifications/api-report.md b/plugins/notifications/api-report.md index 9240bdb1c2..2547204c7b 100644 --- a/plugins/notifications/api-report.md +++ b/plugins/notifications/api-report.md @@ -103,6 +103,7 @@ export const NotificationsSidebarItem: (props?: { titleCounterEnabled?: boolean; snackbarEnabled?: boolean; snackbarAutoHideDuration?: number | null; + markAsReadOnLinkOpen?: boolean; className?: string; icon?: IconComponent; text?: string; @@ -182,7 +183,10 @@ export function useTitleCounter(): { }; // @public (undocumented) -export function useWebNotifications(enabled: boolean): { +export function useWebNotifications( + enabled: boolean, + markAsReadOnLinkOpen: boolean, +): { sendWebNotification: (options: { id: string; title: string; diff --git a/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx b/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx index 7b55c63747..6e1f7c7d56 100644 --- a/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx +++ b/plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx @@ -84,6 +84,7 @@ export const NotificationsSidebarItem = (props?: { titleCounterEnabled?: boolean; snackbarEnabled?: boolean; snackbarAutoHideDuration?: number | null; + markAsReadOnLinkOpen?: boolean; className?: string; icon?: IconComponent; text?: string; @@ -95,6 +96,7 @@ export const NotificationsSidebarItem = (props?: { titleCounterEnabled = true, snackbarEnabled = true, snackbarAutoHideDuration = 10000, + markAsReadOnLinkOpen = false, icon = NotificationsIcon, text = 'Notifications', ...restProps @@ -103,6 +105,7 @@ export const NotificationsSidebarItem = (props?: { titleCounterEnabled: true, snackbarEnabled: true, snackbarAutoHideDuration: 10000, + markAsReadOnLinkOpen: false, }; const { loading, error, value, retry } = useNotificationsApi(api => @@ -114,7 +117,10 @@ export const NotificationsSidebarItem = (props?: { const notificationsRoute = useRouteRef(rootRouteRef); // TODO: Do we want to add long polling in case signals are not available const { lastSignal } = useSignal('notifications'); - const { sendWebNotification } = useWebNotifications(webNotificationsEnabled); + const { sendWebNotification } = useWebNotifications( + webNotificationsEnabled, + markAsReadOnLinkOpen, + ); const [refresh, setRefresh] = React.useState(false); const { setNotificationCount } = useTitleCounter(); @@ -126,6 +132,19 @@ export const NotificationsSidebarItem = (props?: { component={Link} to={notification.payload.link ?? notificationsRoute()} onClick={() => { + if (markAsReadOnLinkOpen) { + notificationsApi + .updateNotifications({ + ids: [notification.id], + read: true, + }) + .catch(() => { + alertApi.post({ + message: 'Failed to mark notification as read', + severity: 'error', + }); + }); + } closeSnackbar(snackBarId); }} > @@ -156,7 +175,7 @@ export const NotificationsSidebarItem = (props?: { return { action }; }, - [notificationsRoute, notificationsApi, alertApi], + [notificationsRoute, markAsReadOnLinkOpen, notificationsApi, alertApi], ); useEffect(() => { diff --git a/plugins/notifications/src/hooks/useWebNotifications.ts b/plugins/notifications/src/hooks/useWebNotifications.ts index 728d992130..92a0a9b806 100644 --- a/plugins/notifications/src/hooks/useWebNotifications.ts +++ b/plugins/notifications/src/hooks/useWebNotifications.ts @@ -15,14 +15,19 @@ */ import { useCallback, useEffect, useState } from 'react'; import { rootRouteRef } from '../routes'; -import { useRouteRef } from '@backstage/core-plugin-api'; +import { useApi, useRouteRef } from '@backstage/core-plugin-api'; import { useNavigate } from 'react-router-dom'; +import { notificationsApiRef } from '../api'; /** @public */ -export function useWebNotifications(enabled: boolean) { +export function useWebNotifications( + enabled: boolean, + markAsReadOnLinkOpen: boolean, +) { const [webNotificationPermission, setWebNotificationPermission] = useState('default'); const notificationsRoute = useRouteRef(rootRouteRef); + const notificationsApi = useApi(notificationsApiRef); const navigate = useNavigate(); useEffect(() => { @@ -57,6 +62,12 @@ export function useWebNotifications(enabled: boolean) { event.preventDefault(); if (options.link) { window.open(options.link, '_blank'); + if (markAsReadOnLinkOpen) { + notificationsApi.updateNotifications({ + ids: [options.id], + read: true, + }); + } } else { navigate(notificationsRoute()); } @@ -65,7 +76,13 @@ export function useWebNotifications(enabled: boolean) { return notification; }, - [webNotificationPermission, navigate, notificationsRoute], + [ + webNotificationPermission, + markAsReadOnLinkOpen, + notificationsApi, + navigate, + notificationsRoute, + ], ); return { sendWebNotification };