Files
ALwrity/frontend/src/hooks/useRealTimeData.ts
2025-08-19 21:48:33 +05:30

233 lines
6.1 KiB
TypeScript

import { useState, useEffect, useCallback, useRef } from 'react';
interface RealTimeDataOptions {
strategyId: number;
reconnectInterval?: number;
maxReconnectAttempts?: number;
onConnect?: () => void;
onDisconnect?: () => void;
onError?: (error: Event) => void;
onMessage?: (data: any) => void;
}
interface RealTimeDataState {
data: any;
isConnected: boolean;
isConnecting: boolean;
error: string | null;
reconnectAttempts: number;
}
export const useRealTimeData = (options: RealTimeDataOptions) => {
const {
strategyId,
reconnectInterval = 5000,
maxReconnectAttempts = 5,
onConnect,
onDisconnect,
onError,
onMessage
} = options;
const [state, setState] = useState<RealTimeDataState>({
data: null,
isConnected: false,
isConnecting: false,
error: null,
reconnectAttempts: 0
});
const wsRef = useRef<WebSocket | null>(null);
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const reconnectAttemptsRef = useRef(0);
const connect = useCallback(() => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
return;
}
setState(prev => ({ ...prev, isConnecting: true, error: null }));
try {
// For development, use a mock WebSocket connection
// In production, this would be the actual WebSocket URL
const wsUrl = process.env.NODE_ENV === 'development'
? `ws://localhost:8000/ws/strategy/${strategyId}/live`
: `wss://api.alwrity.com/ws/strategy/${strategyId}/live`;
const ws = new WebSocket(wsUrl);
wsRef.current = ws;
ws.onopen = () => {
setState(prev => ({
...prev,
isConnected: true,
isConnecting: false,
error: null,
reconnectAttempts: 0
}));
reconnectAttemptsRef.current = 0;
onConnect?.();
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
setState(prev => ({ ...prev, data }));
onMessage?.(data);
} catch (error) {
console.error('Error parsing WebSocket message:', error);
}
};
ws.onclose = (event) => {
setState(prev => ({
...prev,
isConnected: false,
isConnecting: false
}));
onDisconnect?.();
// Attempt to reconnect if not a clean close
if (event.code !== 1000 && reconnectAttemptsRef.current < maxReconnectAttempts) {
reconnectAttemptsRef.current += 1;
setState(prev => ({
...prev,
reconnectAttempts: reconnectAttemptsRef.current
}));
reconnectTimeoutRef.current = setTimeout(() => {
connect();
}, reconnectInterval);
}
};
ws.onerror = (error) => {
setState(prev => ({
...prev,
error: 'WebSocket connection error',
isConnecting: false
}));
onError?.(error);
};
} catch (error) {
setState(prev => ({
...prev,
error: 'Failed to create WebSocket connection',
isConnecting: false
}));
}
}, [strategyId, reconnectInterval, maxReconnectAttempts, onConnect, onDisconnect, onError, onMessage]);
const disconnect = useCallback(() => {
if (reconnectTimeoutRef.current) {
clearTimeout(reconnectTimeoutRef.current);
reconnectTimeoutRef.current = null;
}
if (wsRef.current) {
wsRef.current.close(1000); // Clean close
wsRef.current = null;
}
setState(prev => ({
...prev,
isConnected: false,
isConnecting: false,
reconnectAttempts: 0
}));
}, []);
const sendMessage = useCallback((message: any) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(message));
} else {
console.warn('WebSocket is not connected');
}
}, []);
// Connect on mount and when strategyId changes
useEffect(() => {
connect();
return () => {
disconnect();
};
}, [connect, disconnect]);
// Cleanup on unmount
useEffect(() => {
return () => {
disconnect();
};
}, [disconnect]);
return {
...state,
connect,
disconnect,
sendMessage
};
};
// Mock real-time data for development
export const useMockRealTimeData = (strategyId: number) => {
const [data, setData] = useState<any>(null);
useEffect(() => {
// Simulate real-time data updates
const interval = setInterval(() => {
const mockData = {
timestamp: new Date().toISOString(),
strategyId,
metrics: {
traffic_growth: Math.random() * 20 + 5, // 5-25%
engagement_rate: Math.random() * 10 + 5, // 5-15%
conversion_rate: Math.random() * 3 + 1, // 1-4%
content_quality_score: Math.random() * 20 + 80, // 80-100%
strategy_adoption_rate: Math.random() * 20 + 80, // 80-100%
roi_ratio: Math.random() * 2 + 2, // 2-4x
competitive_position_rank: Math.floor(Math.random() * 5) + 1, // 1-5
audience_growth_percentage: Math.random() * 15 + 8, // 8-23%
confidence_score: Math.random() * 20 + 80 // 80-100%
},
alerts: Math.random() > 0.8 ? [
{
type: 'warning',
message: 'Engagement rate dropped below target',
timestamp: new Date().toISOString()
}
] : [],
trends: {
daily: Array.from({ length: 7 }, (_, i) => ({
date: new Date(Date.now() - (6 - i) * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
traffic_growth: Math.random() * 20 + 5,
engagement_rate: Math.random() * 10 + 5,
conversion_rate: Math.random() * 3 + 1,
content_quality_score: Math.random() * 20 + 80
}))
}
};
setData(mockData);
}, 5000); // Update every 5 seconds
return () => clearInterval(interval);
}, [strategyId]);
return {
data,
isConnected: true,
isConnecting: false,
error: null,
reconnectAttempts: 0,
connect: () => {},
disconnect: () => {},
sendMessage: () => {}
};
};
// Export both hooks
export default useRealTimeData;