feat: sync login status with dashboard

This commit is contained in:
hamster1963 2024-12-25 14:28:35 +08:00
parent 97da6b43c1
commit 1c3684faaf
3 changed files with 67 additions and 9 deletions

View File

@ -140,14 +140,32 @@ function Links() {
function DashboardLink() { function DashboardLink() {
const { t } = useTranslation() const { t } = useTranslation()
const { reconnect } = useWebSocketContext()
const initRef = useRef(false)
const { data: userData } = useQuery({ const { data: userData } = useQuery({
queryKey: ["login-user"], queryKey: ["login-user"],
queryFn: () => fetchLoginUser(), queryFn: () => fetchLoginUser(),
refetchOnMount: true, refetchOnMount: true,
refetchOnWindowFocus: true, refetchOnWindowFocus: true,
refetchIntervalInBackground: true,
refetchInterval: 1000 * 60 * 1, refetchInterval: 1000 * 60 * 1,
}) })
let isLogin = !!userData?.data?.id
if (!document.cookie) {
isLogin = false
}
useEffect(() => {
// 当登录状态变化时重新连接 WebSocket
if (initRef.current) {
reconnect()
} else {
initRef.current = true
}
}, [isLogin])
return ( return (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<a <a
@ -156,8 +174,8 @@ function DashboardLink() {
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center text-nowrap gap-1 text-sm font-medium opacity-50 transition-opacity hover:opacity-100" className="flex items-center text-nowrap gap-1 text-sm font-medium opacity-50 transition-opacity hover:opacity-100"
> >
{!userData?.data?.id && t("login")} {!isLogin && t("login")}
{userData?.data?.id && t("dashboard")} {isLogin && t("dashboard")}
</a> </a>
</div> </div>
) )

View File

@ -4,10 +4,12 @@ export interface WebSocketContextType {
lastMessage: { data: string } | null lastMessage: { data: string } | null
connected: boolean connected: boolean
messageHistory: { data: string }[] messageHistory: { data: string }[]
reconnect: () => void
} }
export const WebSocketContext = createContext<WebSocketContextType>({ export const WebSocketContext = createContext<WebSocketContextType>({
lastMessage: null, lastMessage: null,
connected: false, connected: false,
messageHistory: [], messageHistory: [],
reconnect: () => {},
}) })

View File

@ -15,8 +15,37 @@ export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, child
const reconnectTimeout = useRef<NodeJS.Timeout>(null) const reconnectTimeout = useRef<NodeJS.Timeout>(null)
const maxReconnectAttempts = 30 const maxReconnectAttempts = 30
const reconnectAttempts = useRef(0) const reconnectAttempts = useRef(0)
const isConnecting = useRef(false)
const cleanup = () => {
if (ws.current) {
// 移除所有事件监听器
ws.current.onopen = null
ws.current.onclose = null
ws.current.onmessage = null
ws.current.onerror = null
if (ws.current.readyState === WebSocket.OPEN || ws.current.readyState === WebSocket.CONNECTING) {
ws.current.close()
}
ws.current = null
}
if (reconnectTimeout.current) {
clearTimeout(reconnectTimeout.current)
reconnectTimeout.current = null
}
setConnected(false)
}
const connect = () => { const connect = () => {
if (isConnecting.current) {
console.log("Connection already in progress")
return
}
cleanup()
isConnecting.current = true
try { try {
const wsUrl = new URL(url, window.location.origin) const wsUrl = new URL(url, window.location.origin)
wsUrl.protocol = wsUrl.protocol.replace("http", "ws") wsUrl.protocol = wsUrl.protocol.replace("http", "ws")
@ -27,13 +56,15 @@ export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, child
console.log("WebSocket connected") console.log("WebSocket connected")
setConnected(true) setConnected(true)
reconnectAttempts.current = 0 reconnectAttempts.current = 0
isConnecting.current = false
} }
ws.current.onclose = () => { ws.current.onclose = () => {
console.log("WebSocket disconnected") console.log("WebSocket disconnected")
setConnected(false) setConnected(false)
ws.current = null
isConnecting.current = false
// 重连逻辑
if (reconnectAttempts.current < maxReconnectAttempts) { if (reconnectAttempts.current < maxReconnectAttempts) {
reconnectTimeout.current = setTimeout(() => { reconnectTimeout.current = setTimeout(() => {
reconnectAttempts.current++ reconnectAttempts.current++
@ -54,22 +85,28 @@ export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, child
ws.current.onerror = (error) => { ws.current.onerror = (error) => {
console.error("WebSocket error:", error) console.error("WebSocket error:", error)
isConnecting.current = false
} }
} catch (error) { } catch (error) {
console.error("WebSocket connection error:", error) console.error("WebSocket connection error:", error)
isConnecting.current = false
} }
} }
const reconnect = () => {
reconnectAttempts.current = 0
// 等待一个小延时确保清理完成
cleanup()
setTimeout(() => {
connect()
}, 100)
}
useEffect(() => { useEffect(() => {
connect() connect()
return () => { return () => {
if (ws.current) { cleanup()
ws.current.close()
}
if (reconnectTimeout.current) {
clearTimeout(reconnectTimeout.current)
}
} }
}, [url]) }, [url])
@ -77,6 +114,7 @@ export const WebSocketProvider: React.FC<WebSocketProviderProps> = ({ url, child
lastMessage, lastMessage,
connected, connected,
messageHistory, // 添加到 context value messageHistory, // 添加到 context value
reconnect, // 添加到 context value
} }
return <WebSocketContext.Provider value={contextValue}>{children}</WebSocketContext.Provider> return <WebSocketContext.Provider value={contextValue}>{children}</WebSocketContext.Provider>