refactor: 前端应用骨架迁移至 zustand 并统一路由权限壳
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Badge, Popover, Button, List, Typography, Empty } from 'antd';
|
||||
import { BellOutlined } from '@ant-design/icons';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router';
|
||||
import dayjs from 'dayjs';
|
||||
import { useInterval } from 'usehooks-ts';
|
||||
import api from '../api';
|
||||
import { formatNotificationText, notificationTypeLabels } from '../utils/notification-display';
|
||||
import { useUserStore } from '../store/user/userStore';
|
||||
@@ -17,25 +19,19 @@ interface NotificationItem {
|
||||
}
|
||||
|
||||
function timeAgo(dateStr: string): string {
|
||||
const diff = Date.now() - new Date(dateStr).getTime();
|
||||
const mins = Math.floor(diff / 60000);
|
||||
if (mins < 1) return '刚刚';
|
||||
if (mins < 60) return `${mins}分钟前`;
|
||||
const hours = Math.floor(mins / 60);
|
||||
if (hours < 24) return `${hours}小时前`;
|
||||
const days = Math.floor(hours / 24);
|
||||
return `${days}天前`;
|
||||
return dayjs(dateStr).fromNow();
|
||||
}
|
||||
|
||||
const NotificationBell: React.FC = () => {
|
||||
const [unreadCount, setUnreadCount] = useState(0);
|
||||
const [notifications, setNotifications] = useState<NotificationItem[]>([]);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [sseDown, setSseDown] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const fetchNotifications = async () => {
|
||||
try {
|
||||
const data = (await api.get('/notifications?limit=20')) as unknown as NotificationItem[];
|
||||
const data = await api.get<NotificationItem[]>('/notifications?limit=20');
|
||||
setNotifications(data);
|
||||
} catch {
|
||||
/* ignore */
|
||||
@@ -44,7 +40,7 @@ const NotificationBell: React.FC = () => {
|
||||
|
||||
const fetchUnread = async () => {
|
||||
try {
|
||||
const data = (await api.get('/notifications/unread-count')) as unknown as { count: number };
|
||||
const data = await api.get<{ count: number }>('/notifications/unread-count');
|
||||
setUnreadCount(data.count);
|
||||
} catch {
|
||||
/* ignore */
|
||||
@@ -52,7 +48,9 @@ const NotificationBell: React.FC = () => {
|
||||
};
|
||||
const openRef = useRef(open);
|
||||
openRef.current = open;
|
||||
const retryRef = useRef<number | null>(null);
|
||||
useInterval(() => {
|
||||
void fetchUnread();
|
||||
}, sseDown ? 60_000 : null);
|
||||
|
||||
// SSE connection — decoupled from popover open state
|
||||
useEffect(() => {
|
||||
@@ -71,13 +69,11 @@ const NotificationBell: React.FC = () => {
|
||||
};
|
||||
es.onerror = () => {
|
||||
es.close();
|
||||
if (retryRef.current !== null) clearInterval(retryRef.current);
|
||||
retryRef.current = window.setInterval(fetchUnread, 60_000);
|
||||
setSseDown(true);
|
||||
};
|
||||
return () => {
|
||||
es.close();
|
||||
clearInterval(retryRef.current ?? undefined);
|
||||
retryRef.current = null;
|
||||
setSseDown(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user