import { useEffect, useRef, useState, useCallback } from "react"; import { pollNewPosts } from "../../api/client"; /** * Hook for real-time post streaming using polling (socket.io compatible) * * Usage: * const { newPosts, clearNewPosts } = useRealtimeStream({ * enabled: true, * lat: 40.7128, * lon: -74.0060, * radius_km: 500, * category: "NEWS", * pollInterval: 3000 // poll every 3 seconds * }); */ export function useRealtimeStream({ enabled = true, lat, lon, radius_km = 500, category = null, pollInterval = 3000, // 3 seconds default onNewPost = null, // callback when new posts arrive }) { const [newPosts, setNewPosts] = useState([]); const [isStreaming, setIsStreaming] = useState(false); const maxSeenIdRef = useRef(0); const intervalRef = useRef(null); const poll = useCallback(async () => { if (!lat || !lon) return; try { const result = await pollNewPosts({ since_id: maxSeenIdRef.current, lat, lon, radius_km, category, limit: 20, }); if (result.posts && result.posts.length > 0) { setNewPosts((prev) => [...prev, ...result.posts]); // Update max seen ID if (result.max_id > maxSeenIdRef.current) { maxSeenIdRef.current = result.max_id; } // Call callback if provided if (onNewPost) { result.posts.forEach(onNewPost); } } } catch (err) { console.warn("Realtime stream poll error:", err); } }, [lat, lon, radius_km, category, onNewPost]); useEffect(() => { if (!enabled || !lat || !lon) { setIsStreaming(false); if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } return; } setIsStreaming(true); // Start polling intervalRef.current = setInterval(poll, pollInterval); // Initial poll poll(); return () => { if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } setIsStreaming(false); }; }, [enabled, lat, lon, radius_km, category, pollInterval, poll]); const clearNewPosts = useCallback(() => { setNewPosts([]); }, []); return { newPosts, clearNewPosts, isStreaming, }; }