A Place Where Random People Meet And Chat.

feat(chat): Add real time chat UI with WebSockets

Changed files
+141 -4
client
src
pages
+141 -4
client/src/pages/Chat.jsx
··· 1 - function Chat() { 1 + import { useEffect, useState, useRef } from "react"; 2 + 3 + export default function Chat() { 4 + 5 + const [messages, setMessages] = useState([]); 6 + const [terminated, setTerminated] = useState(false); 7 + const [currMessage, setCurrMessage] = useState(""); 8 + const [isConnected, setIsConnected] = useState(false); 9 + const [isSearching, setIsSearching] = useState(false); 10 + const [matched, setMatched] = useState(false); 11 + const [receiver, setReceiver] = useState(localStorage.getItem("receiver")); 12 + const socketRef = useRef(null); 13 + 14 + const handleChange = (e) => setCurrMessage(e.target.value); 15 + 16 + const handleKeyPress = (e) => { 17 + if (e.key === "Enter" && currMessage.trim() !== "" && matched) { 18 + const payload = { 19 + id: localStorage.getItem("userId"), 20 + message: currMessage 21 + }; 22 + socketRef.current.send(JSON.stringify(payload)); 23 + setCurrMessage(""); 24 + } 25 + }; 26 + 27 + const handleClick = () => { 28 + if (isConnected) return; 29 + 30 + const userId = localStorage.getItem("userId"); 31 + if (!userId) { 32 + alert("Please login first"); 33 + return; 34 + } 35 + 36 + setIsSearching(true); 37 + socketRef.current = new WebSocket(`ws://localhost:3000/?userId=${userId}`); 38 + 39 + socketRef.current.onopen = () => { 40 + console.log("Connected to WebSocket"); 41 + setIsConnected(true); 42 + }; 43 + 44 + socketRef.current.onmessage = (e) => { 45 + try { 46 + const data = JSON.parse(e.data); 47 + 48 + if (data.type === "matched") { 49 + setIsSearching(false); 50 + setMatched(true); 51 + setReceiver(data.with); 52 + localStorage.setItem("receiver", data.with); 53 + console.log("Matched with:", data.with); 54 + } else if (data.type === "disconnected") { 55 + setMatched(false); 56 + setReceiver(null); 57 + localStorage.removeItem("receiver"); 58 + setIsSearching(true); 59 + console.log("Partner disconnected, searching for new match..."); 60 + } else if (data.type === "no_match") { 61 + setIsSearching(false); 62 + setMatched(false); 63 + setReceiver(null); 64 + localStorage.removeItem("receiver"); 65 + console.log("No match found"); 66 + } else { 67 + setMessages((prev) => [...prev, data]); 68 + } 69 + } catch (err) { 70 + console.error("Error parsing message:", err); 71 + setMessages((prev) => [...prev, e.data]); 72 + } 73 + }; 74 + 75 + socketRef.current.onclose = () => { 76 + setTerminated(true); 77 + setIsConnected(false); 78 + setIsSearching(false); 79 + }; 80 + 81 + socketRef.current.onerror = (err) => { 82 + console.error("WebSocket Error:", err); 83 + setIsSearching(false); 84 + }; 85 + }; 86 + 87 + useEffect(() => { 88 + return () => { 89 + if (socketRef.current) { 90 + socketRef.current.close(); 91 + } 92 + }; 93 + }, []); 94 + 95 + if (terminated) return <h1>Chat Terminated</h1>; 96 + 2 97 return ( 3 - <h1>Chat</h1> 4 - ) 98 + <div> 99 + {!isConnected ? ( 100 + <button onClick={handleClick}>I want a friend</button> 101 + ) : isSearching ? ( 102 + <div> 103 + <p>Searching for a friend...</p> 104 + <button onClick={() => { 105 + socketRef.current?.close(); 106 + setIsConnected(false); 107 + setIsSearching(false); 108 + }}>Cancel</button> 109 + </div> 110 + ) : matched && receiver ? ( 111 + <div> 112 + <h2>Chat with {receiver}</h2> 113 + <input 114 + type="text" 115 + value={currMessage} 116 + onChange={handleChange} 117 + onKeyDown={handleKeyPress} 118 + placeholder="Type a message..." 119 + /> 120 + <ul> 121 + {messages.map((msg, i) => ( 122 + <li key={i}> 123 + {typeof msg === 'string' ? msg : `${msg.id}: ${msg.message}`} 124 + </li> 125 + ))} 126 + </ul> 127 + </div> 128 + ) : isConnected && !isSearching ? ( 129 + <div> 130 + <p>No match found. Try again later.</p> 131 + <button onClick={() => { 132 + socketRef.current?.close(); 133 + setIsConnected(false); 134 + setIsSearching(false); 135 + setMatched(false); 136 + setReceiver(null); 137 + localStorage.removeItem("receiver"); 138 + }}>Try Again</button> 139 + </div> 140 + ) : null} 141 + </div> 142 + ); 5 143 } 6 144 7 - export default Chat;