feat: integrate Socket.IO for real-time communication; add socket connection management and token refresh handling
This commit is contained in:
126
src/contexts/SocketContext.tsx
Normal file
126
src/contexts/SocketContext.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react'
|
||||
import { io, Socket } from 'socket.io-client'
|
||||
import { useAuth } from './AuthContext'
|
||||
import { authEvents, AUTH_EVENTS } from '../lib/events'
|
||||
|
||||
interface SocketContextType {
|
||||
socket: Socket | null
|
||||
isConnected: boolean
|
||||
connectionError: string | null
|
||||
isReconnecting: boolean
|
||||
}
|
||||
|
||||
const SocketContext = createContext<SocketContextType | undefined>(undefined)
|
||||
|
||||
interface SocketProviderProps {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export function SocketProvider({ children }: SocketProviderProps) {
|
||||
const { user, loading } = useAuth()
|
||||
const [socket, setSocket] = useState<Socket | null>(null)
|
||||
const [isConnected, setIsConnected] = useState(false)
|
||||
const [connectionError, setConnectionError] = useState<string | null>(null)
|
||||
const [isReconnecting, setIsReconnecting] = useState(false)
|
||||
|
||||
const createSocket = useCallback(() => {
|
||||
if (!user) return null
|
||||
|
||||
const newSocket = io('http://localhost:8000', {
|
||||
withCredentials: true,
|
||||
transports: ['polling', 'websocket'],
|
||||
timeout: 20000,
|
||||
forceNew: true,
|
||||
autoConnect: true,
|
||||
})
|
||||
|
||||
newSocket.on('connect', () => {
|
||||
setIsConnected(true)
|
||||
setConnectionError(null)
|
||||
setIsReconnecting(false)
|
||||
})
|
||||
|
||||
newSocket.on('disconnect', () => {
|
||||
setIsConnected(false)
|
||||
})
|
||||
|
||||
newSocket.on('connect_error', (error) => {
|
||||
setConnectionError(`Connection failed: ${error.message}`)
|
||||
setIsConnected(false)
|
||||
setIsReconnecting(false)
|
||||
})
|
||||
|
||||
return newSocket
|
||||
}, [user])
|
||||
|
||||
// Handle token refresh - reconnect socket with new token
|
||||
const handleTokenRefresh = useCallback(() => {
|
||||
if (!user || !socket) return
|
||||
|
||||
setIsReconnecting(true)
|
||||
|
||||
// Disconnect current socket
|
||||
socket.disconnect()
|
||||
|
||||
// Create new socket with fresh token
|
||||
const newSocket = createSocket()
|
||||
if (newSocket) {
|
||||
setSocket(newSocket)
|
||||
}
|
||||
}, [user, socket, createSocket])
|
||||
|
||||
// Listen for token refresh events
|
||||
useEffect(() => {
|
||||
authEvents.on(AUTH_EVENTS.TOKEN_REFRESHED, handleTokenRefresh)
|
||||
|
||||
return () => {
|
||||
authEvents.off(AUTH_EVENTS.TOKEN_REFRESHED, handleTokenRefresh)
|
||||
}
|
||||
}, [handleTokenRefresh])
|
||||
|
||||
// Initial socket connection
|
||||
useEffect(() => {
|
||||
if (loading) return
|
||||
|
||||
if (!user) {
|
||||
if (socket) {
|
||||
socket.disconnect()
|
||||
setSocket(null)
|
||||
setIsConnected(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const newSocket = createSocket()
|
||||
if (newSocket) {
|
||||
setSocket(newSocket)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (newSocket) {
|
||||
newSocket.disconnect()
|
||||
}
|
||||
}
|
||||
}, [loading, user, createSocket])
|
||||
|
||||
const value: SocketContextType = {
|
||||
socket,
|
||||
isConnected,
|
||||
connectionError,
|
||||
isReconnecting,
|
||||
}
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={value}>
|
||||
{children}
|
||||
</SocketContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useSocket() {
|
||||
const context = useContext(SocketContext)
|
||||
if (context === undefined) {
|
||||
throw new Error('useSocket must be used within a SocketProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
Reference in New Issue
Block a user