| | |
| |
|
| | 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; |
| | }; |
| |
|