feat: add support for web api notifications
Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
@@ -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>
|
||||
|
||||
+47
-3
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user