feat: add support for web api notifications

Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
Heikki Hellgren
2024-01-25 22:04:51 +02:00
parent a3d42153d1
commit c3a22686dd
4 changed files with 77 additions and 18 deletions
@@ -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',
});
}
@@ -38,21 +38,29 @@ const useStyles = makeStyles(_theme => ({
export const NotificationsPage = () => {
const [type, setType] = useState<NotificationType>('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 = () => {
<NotificationsTable
notifications={value}
type={type}
loading={loading}
onUpdate={onUpdate}
/>
</Grid>
@@ -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 (
<SidebarItem
@@ -38,7 +38,6 @@ import { notificationsApiRef } from '../../api';
import { useApi } from '@backstage/core-plugin-api';
import Inbox from '@material-ui/icons/Inbox';
import CloseIcon from '@material-ui/icons/Close';
import { Skeleton } from '@material-ui/lab';
// @ts-ignore
import RelativeTime from 'react-relative-time';
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
@@ -79,10 +78,9 @@ const useStyles = makeStyles(theme => ({
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<string[]>([]);
@@ -111,10 +109,6 @@ export const NotificationsTable = (props: {
);
};
if (loading) {
return <Skeleton variant="rect" height={200} />;
}
return (
<Table size="small" className={styles.table}>
<TableHead>