diff --git a/src/App.tsx b/src/App.tsx index c390fd8..25624bc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,7 @@ import { Route, BrowserRouter as Router, Routes } from "react-router-dom" import ErrorBoundary from "./components/ErrorBoundary" import Footer from "./components/Footer" -import Header from "./components/Header" +import Header, { RefreshToast } from "./components/Header" import { useTheme } from "./hooks/use-theme" import { InjectContext } from "./lib/inject" import { fetchSetting } from "./lib/nezha-api" @@ -93,6 +93,7 @@ const App: React.FC = () => { >
+ } /> } /> diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 1456dee..a0253f4 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -5,13 +5,14 @@ import { useWebSocketContext } from "@/hooks/use-websocket-context" import { fetchLoginUser, fetchSetting } from "@/lib/nezha-api" import { cn } from "@/lib/utils" import { useQuery } from "@tanstack/react-query" +import { AnimatePresence, m } from "framer-motion" import { DateTime } from "luxon" import { useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" import { LanguageSwitcher } from "./LanguageSwitcher" -import { Loader } from "./loading/Loader" +import { Loader, LoadingSpinner } from "./loading/Loader" import { Button } from "./ui/button" function Header() { @@ -138,32 +139,75 @@ function Links() { ) } +export function RefreshToast() { + const { t } = useTranslation() + const navigate = useNavigate() + + const { needReconnect } = useWebSocketContext() + + if (!needReconnect) { + return null + } + + if (needReconnect) { + sessionStorage.removeItem("needRefresh") + setTimeout(() => { + navigate(0) + }, 1000) + } + + return ( +
+ + +
+ +

{t("refreshing")}...

+
+
+
+
+ ) +} + function DashboardLink() { const { t } = useTranslation() - const { reconnect } = useWebSocketContext() - const initRef = useRef(false) - const { data: userData } = useQuery({ + const { setNeedReconnect } = useWebSocketContext() + const previousLoginState = useRef(null) + const { + data: userData, + isFetched, + isLoadingError, + isError, + } = useQuery({ queryKey: ["login-user"], queryFn: () => fetchLoginUser(), - refetchOnMount: true, + refetchOnMount: false, refetchOnWindowFocus: true, refetchIntervalInBackground: true, - refetchInterval: 1000 * 60 * 1, - retry: false, + refetchInterval: 1000 * 5, + retry: 0, }) - let isLogin = !!userData?.data?.id + const isLogin = isError ? false : userData ? !!userData?.data?.id && !!document.cookie : false - if (!document.cookie) { - isLogin = false + if (isLoadingError) { + previousLoginState.current = isLogin } useEffect(() => { - // 当登录状态变化时重新连接 WebSocket - if (initRef.current) { - reconnect() - } else { - initRef.current = true + if (isFetched || isError) { + // 只有当登录状态发生变化时才设置needReconnect + if (previousLoginState.current !== null && previousLoginState.current !== isLogin) { + setNeedReconnect(true) + } + previousLoginState.current = isLogin } }, [isLogin]) diff --git a/src/components/loading/Loader.tsx b/src/components/loading/Loader.tsx index 261c65c..1aa0777 100644 --- a/src/components/loading/Loader.tsx +++ b/src/components/loading/Loader.tsx @@ -11,3 +11,22 @@ export const Loader = ({ visible }: { visible: boolean }) => { ) } + +export const LoadingSpinner = () => { + return ( + + + + ) +} diff --git a/src/context/websocket-context.ts b/src/context/websocket-context.ts index 682a50f..6cc2fab 100644 --- a/src/context/websocket-context.ts +++ b/src/context/websocket-context.ts @@ -5,6 +5,8 @@ export interface WebSocketContextType { connected: boolean messageHistory: { data: string }[] reconnect: () => void + needReconnect: boolean + setNeedReconnect: (needReconnect: boolean) => void } export const WebSocketContext = createContext({ @@ -12,4 +14,6 @@ export const WebSocketContext = createContext({ connected: false, messageHistory: [], reconnect: () => {}, + needReconnect: false, + setNeedReconnect: () => {}, }) diff --git a/src/context/websocket-context.tsx b/src/context/websocket-context.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/context/websocket-provider.tsx b/src/context/websocket-provider.tsx index 7e818ba..85e13e5 100644 --- a/src/context/websocket-provider.tsx +++ b/src/context/websocket-provider.tsx @@ -11,6 +11,7 @@ export const WebSocketProvider: React.FC = ({ url, child const [lastMessage, setLastMessage] = useState<{ data: string } | null>(null) const [messageHistory, setMessageHistory] = useState<{ data: string }[]>([]) // 新增历史消息状态 const [connected, setConnected] = useState(false) + const [needReconnect, setNeedReconnect] = useState(false) const ws = useRef(null) const reconnectTimeout = useRef(null) const maxReconnectAttempts = 30 @@ -99,7 +100,7 @@ export const WebSocketProvider: React.FC = ({ url, child cleanup() setTimeout(() => { connect() - }, 100) + }, 1000) } useEffect(() => { @@ -113,8 +114,10 @@ export const WebSocketProvider: React.FC = ({ url, child const contextValue: WebSocketContextType = { lastMessage, connected, - messageHistory, // 添加到 context value - reconnect, // 添加到 context value + messageHistory, + reconnect, + needReconnect, + setNeedReconnect, } return {children} diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 2afdaf6..a8c7075 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -6,6 +6,7 @@ "online": "Online", "offline": "Offline", "whereTheTimeIs": "Where the time is", + "refreshing": "Refreshing", "info": { "websocketConnecting": "WebSocket connecting", "websocketConnected": "WebSocket connected", diff --git a/src/locales/zh-CN/translation.json b/src/locales/zh-CN/translation.json index 4fff743..97131e2 100644 --- a/src/locales/zh-CN/translation.json +++ b/src/locales/zh-CN/translation.json @@ -6,6 +6,7 @@ "online": "在线", "offline": "离线", "whereTheTimeIs": "当前时间", + "refreshing": "刷新中", "info": { "websocketConnecting": "WebSocket 连接中", "websocketConnected": "WebSocket 连接成功", diff --git a/src/locales/zh-TW/translation.json b/src/locales/zh-TW/translation.json index 396dcac..950909a 100644 --- a/src/locales/zh-TW/translation.json +++ b/src/locales/zh-TW/translation.json @@ -6,6 +6,7 @@ "online": "在線", "offline": "離線", "whereTheTimeIs": "目前時間", + "refreshing": "刷新中", "info": { "websocketConnecting": "WebSocket 連接中", "websocketConnected": "WebSocket 連接成功", diff --git a/src/pages/Server.tsx b/src/pages/Server.tsx index dfa0b80..6189467 100644 --- a/src/pages/Server.tsx +++ b/src/pages/Server.tsx @@ -19,7 +19,6 @@ import { ArrowDownIcon, ArrowUpIcon, ArrowsUpDownIcon, ChartBarSquareIcon, MapIc import { useQuery } from "@tanstack/react-query" import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" -import { toast } from "sonner" export default function Servers() { const { t } = useTranslation() @@ -56,14 +55,6 @@ export default function Servers() { const groupTabs = ["All", ...(groupData?.data?.map((item: ServerGroup) => item.group.name) || [])] - useEffect(() => { - const hasShownToast = sessionStorage.getItem("websocket-connected-toast") - if (connected && !hasShownToast) { - toast.success(t("info.websocketConnected")) - sessionStorage.setItem("websocket-connected-toast", "true") - } - }, [connected]) - if (!connected && !lastMessage) { return (