mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
style: improve the ui of the toast for notifications
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import type { NotificationListResult } from "@edr/types";
|
||||
import type { NotificationDto, NotificationListResult } from "@edr/types";
|
||||
import {
|
||||
NotificationBell,
|
||||
NotificationDrawer,
|
||||
NotificationToast,
|
||||
type NotificationItemData,
|
||||
} from "@edr/ui-common";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import {
|
||||
@@ -19,22 +21,25 @@ import {
|
||||
} from "./useNotifications";
|
||||
import { useNotificationSocket } from "./useNotificationSocket";
|
||||
|
||||
/** Map a server notification into the shared presentational item shape. */
|
||||
function toItem(n: NotificationDto): NotificationItemData {
|
||||
return {
|
||||
id: n.id,
|
||||
type: n.type,
|
||||
title: n.title,
|
||||
body: n.body,
|
||||
createdAt: n.createdAt,
|
||||
isRead: n.isRead,
|
||||
link: n.link,
|
||||
data: n.data,
|
||||
};
|
||||
}
|
||||
|
||||
/** Flatten an infinite query's pages into the drawer's item shape. */
|
||||
function toItems(
|
||||
data: { pages: NotificationListResult[] } | undefined,
|
||||
): NotificationItemData[] {
|
||||
return (data?.pages ?? [])
|
||||
.flatMap((p) => p.items)
|
||||
.map((n) => ({
|
||||
id: n.id,
|
||||
type: n.type,
|
||||
title: n.title,
|
||||
body: n.body,
|
||||
createdAt: n.createdAt,
|
||||
isRead: n.isRead,
|
||||
link: n.link,
|
||||
data: n.data,
|
||||
}));
|
||||
return (data?.pages ?? []).flatMap((p) => p.items).map(toItem);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +60,6 @@ export default function NotificationBellContainer({
|
||||
const unread = useUnreadCount(enabled);
|
||||
const markRead = useMarkRead();
|
||||
const markAllRead = useMarkAllRead();
|
||||
useNotificationSocket(enabled);
|
||||
|
||||
const unreadItems = toItems(unreadQ.data);
|
||||
const readItems = toItems(readQ.data);
|
||||
@@ -68,6 +72,26 @@ export default function NotificationBellContainer({
|
||||
if (href) navigate(href);
|
||||
};
|
||||
|
||||
// Live push → rich toast that reuses the same registry + click action.
|
||||
useNotificationSocket(enabled, (n) => {
|
||||
const item = toItem(n);
|
||||
toast.custom(
|
||||
(t) => (
|
||||
<NotificationToast
|
||||
item={item}
|
||||
visual={resolveNotificationVisual(item)}
|
||||
visible={t.visible}
|
||||
onClick={() => {
|
||||
toast.dismiss(t.id);
|
||||
handleItemClick(item);
|
||||
}}
|
||||
onDismiss={() => toast.dismiss(t.id)}
|
||||
/>
|
||||
),
|
||||
{ duration: 6000 },
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<NotificationBell
|
||||
|
||||
@@ -4,8 +4,7 @@ import {
|
||||
type NotificationDto,
|
||||
} from "@edr/types";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
import { API_BASE_URL } from "@/constants/apiConfig";
|
||||
@@ -19,10 +18,16 @@ const SOCKET_ORIGIN = String(API_BASE_URL ?? "").replace(/\/api\/?$/, "");
|
||||
|
||||
/**
|
||||
* Subscribes to live notification pushes for the signed-in staff user. New
|
||||
* items invalidate the list + toast; unread-count pushes update the badge.
|
||||
* items invalidate the cached lists + fire `onNew` (the host shows a rich
|
||||
* toast); unread-count pushes update the badge.
|
||||
*/
|
||||
export function useNotificationSocket(enabled: boolean) {
|
||||
export function useNotificationSocket(
|
||||
enabled: boolean,
|
||||
onNew?: (notification: NotificationDto) => void,
|
||||
) {
|
||||
const qc = useQueryClient();
|
||||
const onNewRef = useRef(onNew);
|
||||
onNewRef.current = onNew;
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
@@ -37,7 +42,8 @@ export function useNotificationSocket(enabled: boolean) {
|
||||
|
||||
socket.on(NOTIFICATION_WS_EVENTS.NEW, (n: NotificationDto) => {
|
||||
qc.invalidateQueries({ queryKey: NOTIFICATIONS_KEY });
|
||||
toast(n.title);
|
||||
qc.invalidateQueries({ queryKey: UNREAD_KEY });
|
||||
onNewRef.current?.(n);
|
||||
});
|
||||
|
||||
socket.on(NOTIFICATION_WS_EVENTS.UNREAD_COUNT, (count: number) => {
|
||||
|
||||
Reference in New Issue
Block a user