// RelayAPI.js - Next.js Wrapper for the WebSocket Server import { useEffect, useState, useCallback, useRef } from "react"; const useWebSocket = (userId) => { const [socket, setSocket] = useState(null); const [messages, setMessages] = useState([]); const [connectedChannels, setConnectedChannels] = useState(new Set()); const reconnectInterval = useRef(null); const connect = useCallback(() => { const ws = new WebSocket(`${process.env.NEXT_PUBLIC_RELAY_URL}/ws?user_id=${userId}`); ws.onopen = () => { console.debug("WebSocket connected."); clearInterval(reconnectInterval.current); }; ws.onmessage = (event) => { try { const data = JSON.parse(event.data); console.debug("Received message:", data); setMessages((prev) => [...prev, data]); console.debug(messages); } catch (error) { console.debug("Failed to parse WebSocket message:", event.data); } }; ws.onclose = () => { console.warn("WebSocket closed. Reconnecting..."); reconnectInterval.current = setInterval(() => connect(), 5000); }; ws.onerror = (error) => { console.debug("WebSocket error:", error); ws.close(); }; setSocket(ws); }, [userId]); useEffect(() => { if (!userId) return; connect(); return () => { clearInterval(reconnectInterval.current); setConnectedChannels(new Set()); if (socket) socket.close(); }; }, [connect, userId]); const sendMessage = useCallback( (action, channel, message, targetUserId = null) => { if (!socket || socket.readyState !== WebSocket.OPEN) { console.warn("WebSocket is not connected."); return; } const payload = { action, channel, message, sender: userId }; if (targetUserId) payload.target_user_id = targetUserId; try { socket.send(JSON.stringify(payload)); console.debug("Sent message:", payload); } catch (error) { console.debug("Failed to send WebSocket message:", error); } }, [socket, userId] ); const joinChannel = useCallback( (channel) => { if (connectedChannels.has(channel)) return; sendMessage("join", channel); setConnectedChannels((prev) => new Set([...prev, channel])); }, [sendMessage, connectedChannels] ); const leaveChannel = useCallback( (channel) => { if (!connectedChannels.has(channel)) return; sendMessage("leave", channel); setConnectedChannels((prev) => { const updated = new Set(prev); updated.delete(channel); return updated; }); }, [sendMessage, connectedChannels] ); const broadcast = useCallback( (channel, message) => { sendMessage("broadcast", channel, message); }, [sendMessage] ); const sendToUser = useCallback( (targetUserId, message) => { sendMessage("send_to_user", null, message, targetUserId); }, [sendMessage] ); return { messages, joinChannel, leaveChannel, broadcast, sendToUser }; }; export const RelayAPIProvider = ({ userId=null, children }) => { if (userId === null) { userId = localStorage.getItem("u_id"); console.log("inside context") } const contextValue = useWebSocket(userId); return ( {children} ); }; import React, { useContext } from "react"; const RelayAPIContext = React.createContext(null); export const useRelayAPI = () => { const context = useContext(RelayAPIContext); if (!context) { throw new Error("useRelayAPI must be used within a RelayAPIProvider"); } return context; };