| |
|
|
| 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 ( |
| <RelayAPIContext.Provider value={contextValue}>{children}</RelayAPIContext.Provider> |
| ); |
| }; |
|
|
| 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; |
| }; |
|
|