Added SSE function and add readAt for notification

This commit is contained in:
2026-02-21 22:34:29 +07:00
parent fa689ea4aa
commit ab745e6a2f
17 changed files with 349 additions and 43 deletions

View File

@@ -1,9 +1,11 @@
import { updateReadedNotification } from '@/service/notify.api';
import { notificationQueries } from '@/service/queries';
import useNotificationStore from '@/store/useNotificationStore';
import { formatTimeAgo } from '@/utils/helper';
import { cn } from '@lib/utils';
import { m } from '@paraglide/messages';
import { BellIcon } from '@phosphor-icons/react';
import { useQuery } from '@tanstack/react-query';
import { useMutation, useQuery } from '@tanstack/react-query';
import { Link } from '@tanstack/react-router';
import { Button } from '@ui/button';
import {
@@ -15,15 +17,42 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@ui/dropdown-menu';
import { useEffect, useState } from 'react';
import { toast } from 'sonner';
import { Item, ItemContent, ItemDescription, ItemTitle } from './ui/item';
const Notification = () => {
const { data: notifications } = useQuery(notificationQueries.topFive());
const [open, _setOpen] = useState(false);
const { hasNew, setHasNew } = useNotificationStore((state) => state);
const { data } = useQuery(notificationQueries.topFive());
if (!notifications) return null;
const { mutate: updateReaded } = useMutation({
mutationFn: () => updateReadedNotification(),
onError: (error: ReturnError) => {
const code = error.code as Parameters<
typeof m.backend_message
>[0]['code'];
toast.error(m.backend_message({ code }), {
richColors: true,
});
},
});
const onOpenNotification = (isOpen: boolean) => {
_setOpen(isOpen);
updateReaded();
};
useEffect(() => {
if (data) {
setHasNew(data.hasNewNotify);
}
}, [data]);
if (!data) return null;
return (
<DropdownMenu>
<DropdownMenu open={open} onOpenChange={onOpenNotification}>
<DropdownMenuTrigger asChild>
<Button
size="icon-lg"
@@ -32,9 +61,9 @@ const Notification = () => {
>
<BellIcon
size={32}
className={cn('origin-top', { 'animate-bell-ring': true })}
className={cn('origin-top', { 'animate-bell-ring': hasNew })}
/>
{true && (
{hasNew && (
<span className="absolute top-1 right-1 rounded-full w-2 h-2 bg-red-600"></span>
)}
<span className="sr-only">{m.ui_label_notifications()}</span>
@@ -46,26 +75,32 @@ const Notification = () => {
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
{notifications.map((notify) => {
return (
<DropdownMenuItem key={notify.id}>
<Item className="p-0">
<ItemContent>
<ItemTitle className="text-sm">
{m.templates_title_notification({
title: notify.title as Parameters<
typeof m.templates_title_notification
>[0]['title'],
})}
</ItemTitle>
<ItemDescription>
{formatTimeAgo(new Date(notify.createdAt))}
</ItemDescription>
</ItemContent>
</Item>
</DropdownMenuItem>
);
})}
{data.list && data.list.length > 0 ? (
data.list.map((notify) => {
return (
<DropdownMenuItem key={notify.id}>
<Item className="p-0">
<ItemContent>
<ItemTitle className="text-sm">
{m.templates_title_notification({
title: notify.title as Parameters<
typeof m.templates_title_notification
>[0]['title'],
})}
</ItemTitle>
<ItemDescription>
{formatTimeAgo(new Date(notify.createdAt))}
</ItemDescription>
</ItemContent>
</Item>
</DropdownMenuItem>
);
})
) : (
<DropdownMenuItem className="py-10 justify-center">
{m.common_no_notify()}
</DropdownMenuItem>
)}
</DropdownMenuGroup>
<DropdownMenuGroup>
<DropdownMenuSeparator />