import React, { createContext, useContext, useEffect, useState } from 'react'; import { useWebSocket, WebSocketMessage } from '../hooks/useWebSocket'; interface WebSocketContextType { isConnected: boolean; connectionState: 'connecting' | 'connected' | 'disconnected' | 'error'; sendMessage: (type: string, data: any) => void; lastMessage: WebSocketMessage | null; subscribe: (messageType: string, handler: (data: any) => void) => () => void; reconnect: () => void; } const WebSocketContext = createContext(null); interface WebSocketProviderProps { children: React.ReactNode; url?: string; } export const WebSocketProvider: React.FC = ({ children, url = import.meta.env.VITE_WS_BASE_URL || 'wss://whoosh.home.deepblack.cloud' }) => { const [subscriptions, setSubscriptions] = useState void>>>(new Map()); const { isConnected, connectionState, sendMessage, lastMessage, reconnect } = useWebSocket({ url, reconnectAttempts: 5, reconnectDelay: 3000, onMessage: (message) => { // Handle incoming messages and notify subscribers const handlers = subscriptions.get(message.type); if (handlers) { handlers.forEach(handler => { try { handler(message.data); } catch (error) { console.error('Error in WebSocket message handler:', error); } }); } }, onConnect: () => { console.log('WebSocket connected to WHOOSH backend'); // Subscribe to general system events sendMessage('subscribe', { events: ['agent_status_changed', 'execution_started', 'execution_completed', 'metrics_updated'] }); }, onDisconnect: () => { console.log('WebSocket disconnected from WHOOSH backend'); }, onError: (error) => { console.error('WebSocket error:', error); } }); const subscribe = (messageType: string, handler: (data: any) => void) => { setSubscriptions(prev => { const newSubscriptions = new Map(prev); if (!newSubscriptions.has(messageType)) { newSubscriptions.set(messageType, new Set()); } newSubscriptions.get(messageType)!.add(handler); return newSubscriptions; }); // Return unsubscribe function return () => { setSubscriptions(prev => { const newSubscriptions = new Map(prev); const handlers = newSubscriptions.get(messageType); if (handlers) { handlers.delete(handler); if (handlers.size === 0) { newSubscriptions.delete(messageType); } } return newSubscriptions; }); }; }; const contextValue: WebSocketContextType = { isConnected, connectionState, sendMessage, lastMessage, subscribe, reconnect }; return ( {children} ); }; export const useWebSocketContext = (): WebSocketContextType => { const context = useContext(WebSocketContext); if (!context) { throw new Error('useWebSocketContext must be used within a WebSocketProvider'); } return context; }; // Convenience hooks for common real-time updates export const useAgentUpdates = (onAgentUpdate: (agentData: any) => void) => { const { subscribe } = useWebSocketContext(); useEffect(() => { const unsubscribe = subscribe('agent_status_changed', onAgentUpdate); return unsubscribe; }, [subscribe, onAgentUpdate]); }; export const useExecutionUpdates = (onExecutionUpdate: (executionData: any) => void) => { const { subscribe } = useWebSocketContext(); useEffect(() => { const unsubscribeStart = subscribe('execution_started', onExecutionUpdate); const unsubscribeComplete = subscribe('execution_completed', onExecutionUpdate); const unsubscribeFailed = subscribe('execution_failed', onExecutionUpdate); return () => { unsubscribeStart(); unsubscribeComplete(); unsubscribeFailed(); }; }, [subscribe, onExecutionUpdate]); }; export const useMetricsUpdates = (onMetricsUpdate: (metricsData: any) => void) => { const { subscribe } = useWebSocketContext(); useEffect(() => { const unsubscribe = subscribe('metrics_updated', onMetricsUpdate); return unsubscribe; }, [subscribe, onMetricsUpdate]); };