import React, { createContext, useContext, useEffect, useState } from 'react'; import { io } from 'socket.io-client'; import { useAuth } from './AuthContext'; import toast from 'react-hot-toast'; const SocketContext = createContext(); export const SocketProvider = ({ children }) => { const [socket, setSocket] = useState(null); const [connected, setConnected] = useState(false); const [recentDetections, setRecentDetections] = useState([]); const [deviceStatus, setDeviceStatus] = useState({}); const [movementAlerts, setMovementAlerts] = useState([]); const [droneTracking, setDroneTracking] = useState(new Map()); const { isAuthenticated } = useAuth(); useEffect(() => { if (isAuthenticated) { // Initialize socket connection const newSocket = io(process.env.NODE_ENV === 'production' ? window.location.origin : 'http://localhost:3001', { path: process.env.NODE_ENV === 'production' ? '/drones/socket.io/' : '/socket.io/' }); newSocket.on('connect', () => { console.log('Connected to server'); setConnected(true); // Join dashboard room for general updates newSocket.emit('join_dashboard'); toast.success('Connected to real-time updates'); }); newSocket.on('disconnect', () => { console.log('Disconnected from server'); setConnected(false); toast.error('Disconnected from server'); }); newSocket.on('connect_error', (error) => { console.error('Connection error:', error); setConnected(false); toast.error('Failed to connect to server'); }); // Listen for drone detections newSocket.on('drone_detection', (detection) => { console.log('New drone detection:', detection); setRecentDetections(prev => [detection, ...prev.slice(0, 49)]); // Keep last 50 // Update drone tracking if (detection.movement_analysis) { const trackingKey = `${detection.drone_id}_${detection.device_id}`; setDroneTracking(prev => { const newTracking = new Map(prev); newTracking.set(trackingKey, { droneId: detection.drone_id, deviceId: detection.device_id, lastDetection: detection, analysis: detection.movement_analysis, timestamp: Date.now() }); return newTracking; }); } // Show toast notification toast.error( `Drone ${detection.drone_id} detected by ${detection.device?.name || `Device ${detection.device_id}`}`, { duration: 5000, icon: '🚨', } ); }); // Listen for drone movement alerts newSocket.on('drone_movement_alert', (alertData) => { console.log('Drone movement alert:', alertData); setMovementAlerts(prev => [alertData, ...prev.slice(0, 19)]); // Keep last 20 alerts // Show priority-based notifications const alertIcon = alertData.analysis.alertLevel >= 3 ? '🚨' : alertData.analysis.alertLevel >= 2 ? '⚠️' : '📍'; const alertColor = alertData.analysis.alertLevel >= 3 ? 'error' : alertData.analysis.alertLevel >= 2 ? 'warning' : 'info'; toast[alertColor === 'error' ? 'error' : alertColor === 'warning' ? 'error' : 'info']( alertData.analysis.description, { duration: alertData.analysis.alertLevel >= 2 ? 10000 : 6000, icon: alertIcon, style: { background: alertData.analysis.alertLevel >= 3 ? '#fee2e2' : alertData.analysis.alertLevel >= 2 ? '#fef3c7' : '#e0f2fe' } } ); }); // Listen for device heartbeats newSocket.on('device_heartbeat', (heartbeat) => { console.log('Device heartbeat:', heartbeat); setDeviceStatus(prev => ({ ...prev, [heartbeat.device_id]: { ...heartbeat, last_seen: new Date() } })); }); // Listen for device updates newSocket.on('device_updated', (device) => { console.log('Device updated:', device); toast.success(`Device ${device.name || device.id} updated`); }); setSocket(newSocket); return () => { newSocket.disconnect(); }; } else { // Disconnect if not authenticated if (socket) { socket.disconnect(); setSocket(null); setConnected(false); } } }, [isAuthenticated]); const joinDeviceRoom = (deviceId) => { if (socket) { socket.emit('join_device_room', deviceId); } }; const leaveDeviceRoom = (deviceId) => { if (socket) { socket.emit('leave_device_room', deviceId); } }; const clearRecentDetections = () => { setRecentDetections([]); }; const clearMovementAlerts = () => { setMovementAlerts([]); }; const getDroneTracking = (droneId, deviceId) => { const trackingKey = `${droneId}_${deviceId}`; return droneTracking.get(trackingKey); }; const value = { socket, connected, recentDetections, deviceStatus, movementAlerts, droneTracking, joinDeviceRoom, leaveDeviceRoom, clearRecentDetections, clearMovementAlerts, getDroneTracking }; return ( {children} ); }; export const useSocket = () => { const context = useContext(SocketContext); if (!context) { throw new Error('useSocket must be used within a SocketProvider'); } return context; };