import { useEffect } from 'react' import { useLocation } from 'react-router-dom' import { useQueryClient } from '@tanstack/react-query' import { useAuthStore } from '@/app/store/auth' import { toast } from '@/app/store/toast' import type { Customer, Message } from '@/types' import { getWebSocketTicket } from '@/services/api/auth' export function GlobalRealtime() { const token = useAuthStore((state) => state.token) const location = useLocation() const queryClient = useQueryClient() useEffect(() => { if (!token) return const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' let socket: WebSocket | undefined let reconnectTimer: ReturnType | undefined let stopped = false let retryDelay = 500 const connect = async () => { if (stopped) return try { const { ticket } = await getWebSocketTicket() if (stopped) return socket = new WebSocket(`${protocol}//${window.location.host}/api/ws?ticket=${encodeURIComponent(ticket)}`) } catch { reconnectTimer = setTimeout(() => { void connect() }, retryDelay) retryDelay = Math.min(retryDelay * 2, 10_000) return } socket.onopen = () => { retryDelay = 500 } socket.onmessage = (raw) => { let event: { type?: string; message?: Message; customer?: Customer; conversationId?: string } try { event = JSON.parse(String(raw.data)) } catch { return } if (event.type !== 'message.created' || event.message?.sender !== 'customer') return void queryClient.invalidateQueries({ queryKey: ['notifications'] }) void queryClient.invalidateQueries({ queryKey: ['conversations'] }) if (!location.pathname.startsWith('/inbox')) { const preview = event.message.translatedText || event.message.originalText || `[${event.message.type}]` toast.info(`${event.customer?.name || event.message.senderName || 'Telegram 客户'} 发来新消息`, preview.slice(0, 120)) } } socket.onerror = () => socket?.close() socket.onclose = () => { if (stopped) return reconnectTimer = setTimeout(() => { void connect() }, retryDelay) retryDelay = Math.min(retryDelay * 2, 10_000) } } void connect() return () => { stopped = true if (reconnectTimer) clearTimeout(reconnectTimer) socket?.close() } }, [token, location.pathname, queryClient]) return null }