├── public ├── p.jpg ├── Chat Wallpaper.jpg ├── index.html ├── script.js └── styles.css ├── package.json ├── README.md └── index.js /public/p.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softenrj/Real-Time-Chat-Room/HEAD/public/p.jpg -------------------------------------------------------------------------------- /public/Chat Wallpaper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softenrj/Real-Time-Chat-Room/HEAD/public/Chat Wallpaper.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "p5(p2p-message)", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "express": "^4.19.2", 15 | "socket.io": "^4.7.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | P2P chatting app 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 |
17 | 18 |
19 |
20 | image 21 |
22 | Create new
23 | No user
24 |
    25 | offline 26 |
    27 | 28 |
    29 | 31 | 32 |
    33 |
    34 | 35 | 36 |
    37 |
    38 | 39 |
    40 | 41 | 42 |
    43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # P2P Chatting App 2 | 3 | ## Overview 4 | 5 | The P2P Chatting App is a real-time chat application allowing users to join chat rooms and communicate with each other. Built using Socket.io and Express.js, this app demonstrates real-time communication and room management functionalities. 6 | 7 | ## Features 8 | 9 | - **Real-time Messaging:** Send and receive messages instantly within a room. 10 | - **User Presence:** See who is online and who has joined or left the room. 11 | - **Typing Indicator:** Display when users are typing a message. 12 | - **Responsive Design:** Works on both desktop and mobile devices. 13 | 14 | ## Technologies Used 15 | 16 | - **Frontend:** 17 | - HTML 18 | - CSS 19 | - JavaScript 20 | - Socket.io client 21 | 22 | - **Backend:** 23 | - Node.js 24 | - Express.js 25 | - Socket.io server 26 | 27 | ## Installation 28 | 29 | 1. **Clone the Repository** 30 | 31 | ```bash 32 | git clone https://github.com/softenrj/P2P-chatting-app.git 33 | 34 | 2. **Navigate to the Project Directory** 35 | 36 | ```bash 37 | cd P2P-chatting-app 38 | 39 | 3. **Install Dependencies** 40 | Make sure you have Node.js installed. Then run: 41 | 42 | ```bash 43 | npm install 44 | 45 | 4. **Run the Server** 46 | 47 | ```bash 48 | nodemon index.js 49 | 50 | 5. **Open the App** 51 | Open your browser and navigate to http://localhost:3000 to use the chat application. 52 | 53 | ## Usage 54 | 55 | 1. **Join a Room:** 56 | - Enter your username and room name in the fields provided and click "OK" to join a room. 57 | 58 | 2. **Send Messages:** 59 | 60 | - Type your message in the input box and press "Send" or hit Enter. 61 | 62 | 3. **See User Activity:** 63 | 64 | - The chat area will display user. activity, including who is typing. 65 | 66 | ## Contributing 67 | Feel free to fork this repository and submit pull requests. For any issues or feature requests, please open an issue on the GitHub repository. 68 | 69 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { createServer } from "http"; 3 | import { Server } from "socket.io"; 4 | 5 | const app = express(); 6 | const httpServer = createServer(app); 7 | const io = new Server(httpServer, {}); 8 | 9 | app.use(express.static('public')); 10 | 11 | // Object to store users by room 12 | const roomUsers = {}; 13 | 14 | io.on("connection", (socket) => { 15 | console.log(`A user is connected with id: ${socket.id}`); 16 | 17 | 18 | // Handle joining a room 19 | socket.on('join-room', (room, user) => { 20 | socket.join(room); 21 | console.log(`${user} joined room: ${room}`); 22 | 23 | 24 | // Initialize room in roomUsers if not exists 25 | if (!roomUsers[room]) { 26 | roomUsers[room] = {}; 27 | } 28 | roomUsers[room][socket.id] = user; 29 | 30 | io.to(room).emit("userList", Object.values(roomUsers[room])); 31 | io.to(room).emit("userJoin",`${user} joined room`); 32 | }); 33 | 34 | // Handle sending a message 35 | socket.on("send-msg", (message, room, user) => { 36 | // For your eyes only! Uncomment for secret message spy mode 😅 37 | // console.log(`Message received from ${user} in room ${room}: ${message}`); 38 | 39 | // Ninja mode: sending the message to everyone in the room except the sneaky sender 40 | socket.to(room).emit("received", message, user); 41 | }); 42 | 43 | 44 | // Handle typing notification 45 | socket.on("typing", (room, userName) => { 46 | socket.to(room).emit("typing", `${userName} is typing...`); 47 | }); 48 | 49 | socket.on("stop-typing", (room) => { 50 | socket.to(room).emit("stop-typing"); 51 | }); 52 | 53 | // Handle user disconnection 54 | socket.on("disconnect", () => { 55 | console.log(`User disconnected with id: ${socket.id}`); 56 | 57 | for (const [room, users] of Object.entries(roomUsers)) { 58 | if (users[socket.id]) { 59 | const userName = users[socket.id]; 60 | delete users[socket.id]; 61 | 62 | // Notify the room that the user has left 63 | io.to(room).emit("receive", `${userName} has left the room`, 'Admin'); 64 | 65 | // Send the updated user list 66 | io.to(room).emit("userList", Object.values(users)); 67 | } 68 | } 69 | 70 | }); 71 | 72 | }); 73 | 74 | httpServer.listen(3000, () => { 75 | console.log("Server is running on port 3000"); 76 | }); 77 | -------------------------------------------------------------------------------- /public/script.js: -------------------------------------------------------------------------------- 1 | //join room 2 | const userName = document.querySelector(".username"); 3 | const roomName = document.querySelector(".roomname"); 4 | const joinForm = document.querySelector(".join"); 5 | 6 | //info 7 | const roomDisp = document.querySelector(".room"); 8 | const usersName = document.querySelector(".users"); 9 | const activity = document.querySelector(".activity"); 10 | 11 | // chat area 12 | const chatArea = document.querySelector(".chat-display"); 13 | 14 | //send message 15 | const messageSend = document.querySelector(".send-message"); 16 | const msgForm = document.querySelector(".sendmessage"); 17 | 18 | 19 | msgForm.addEventListener("submit",e =>{ 20 | e.preventDefault(); 21 | const message = messageSend.value; 22 | if(message==="") return; 23 | 24 | displayMessage(message,userName.value); 25 | 26 | //clint to server 27 | socket.emit("send-msg",message,roomName.value,userName.value); 28 | messageSend.value =""; 29 | }) 30 | 31 | 32 | function displayMessage(m, anyUser) { 33 | let li = document.createElement("li"); 34 | if (anyUser !== userName.value) { 35 | li.innerHTML = ` 36 |
    37 | ${anyUser} 38 |
    39 |
    40 | ${m} 41 |
    42 | ${new Date().toLocaleTimeString('en-US', { 43 | hour: '2-digit', 44 | minute: '2-digit', 45 | hour12: true 46 | }) 47 | } 48 |
    49 | `; 50 | } else { 51 | li.innerHTML = ` 52 |
    53 | You 54 |
    55 |
    56 | ${m} 57 |
    58 | ${new Date().toLocaleTimeString('en-US', { 59 | hour: '2-digit', 60 | minute: '2-digit', 61 | hour12: true 62 | }) 63 | } 64 |
    65 | `; 66 | } 67 | chatArea.appendChild(li); 68 | chatArea.scrollTop = chatArea.scrollHeight; 69 | } 70 | 71 | function serverMsg(m){ 72 | let p = document.createElement("p"); 73 | p.className="admin"; 74 | p.innerHTML=` 75 | ${m} 76 | `; 77 | chatArea.scrollTop = chatArea.scrollHeight; 78 | chatArea.prepend(p); 79 | } 80 | 81 | 82 | 83 | //socket 84 | 85 | const socket = io(); 86 | 87 | socket.on("connect",()=>{ 88 | serverMsg(`Default You connect to ${socket.id} \n create room or join from above`); 89 | }) 90 | 91 | socket.on("received",(message,anyUser)=>{ 92 | displayMessage(message,anyUser); 93 | }) 94 | 95 | socket.on("userJoin",(m)=>{ 96 | newJoin(m); 97 | }) 98 | 99 | function newJoin(m){ 100 | let p = document.createElement("p"); 101 | p.className="admin"; 102 | p.innerHTML=` 103 | ${m} 104 | `; 105 | chatArea.scrollTop = chatArea.scrollHeight; 106 | chatArea.appendChild(p); 107 | } 108 | 109 | //room 110 | joinForm.addEventListener("submit",(e)=>{ 111 | e.preventDefault(); 112 | const room = roomName.value.trim(); 113 | const user = userName.value.trim(); 114 | 115 | if(room && user){ 116 | socket.emit('join-room',room,user); 117 | } 118 | 119 | roomDisp.innerHTML=roomName.value; 120 | activity.innerHTML="online"; 121 | }) 122 | 123 | //show activity 124 | 125 | let typingTimeout; 126 | 127 | messageSend.addEventListener("input",()=>{ 128 | console.log("hellolllllll"); 129 | socket.emit("typing",roomName.value,userName.value); 130 | clearTimeout(typingTimeout); 131 | typingTimeout = setTimeout(()=>{ 132 | socket.emit("stop-typing",roomName.value); 133 | },1000) 134 | }) 135 | 136 | socket.on("typing", (m) => { 137 | console.log("dsssssss"); 138 | activity.innerHTML = m; 139 | }); 140 | 141 | socket.on("stop-typing", () => { 142 | activity.innerHTML = ''; 143 | }); 144 | 145 | 146 | //user list 147 | socket.on("userList", (users) => { 148 | usersName.innerHTML = ''; 149 | for (let i = 0; i < users.length; i++) { 150 | if (i === 0) { 151 | usersName.innerHTML += users[i]; 152 | } else { 153 | usersName.innerHTML += ", " + users[i]; 154 | } 155 | } 156 | }); 157 | 158 | -------------------------------------------------------------------------------- /public/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Anton&family=Bebas+Neue&family=Comfortaa:wght@300..700&family=Edu+AU+VIC+WA+NT+Hand:wght@400..700&family=Kalnia+Glaze:wght@100..700&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap'); 2 | 3 | body { 4 | height: 100vh; 5 | display: grid; 6 | place-items: center; 7 | 8 | } 9 | 10 | * { 11 | margin: 0; 12 | padding: 0; 13 | } 14 | 15 | .container { 16 | background-image: url("./Chat\ Wallpaper.jpg"); 17 | background-size:cover; 18 | background-position: center; 19 | width: 400px; 20 | border: 3px solid rgba(0, 0, 0, 0.884); 21 | border-radius: 16px; 22 | transition: all 0.3s ease; 23 | 24 | } 25 | 26 | 27 | .join { 28 | width: 98.5%; 29 | background-color: rgb(0, 0, 0); 30 | height: 25px; 31 | border-radius: 12px 12px 0px 0px; 32 | padding-left: 6px; 33 | 34 | } 35 | 36 | .join input { 37 | border: none; 38 | font-size: x-small; 39 | padding: 3.5px; 40 | } 41 | 42 | input:focus { 43 | outline: none; 44 | } 45 | 46 | .join-btn { 47 | background-color: rgb(0, 162, 255); 48 | border: none; 49 | padding: 2px; 50 | color: white; 51 | } 52 | 53 | .head { 54 | background-color: rgb(223, 221, 221); 55 | border-radius: 0px 0px 20px 20px; 56 | display: flex; 57 | padding: 5px; 58 | padding-bottom: 2px; 59 | } 60 | 61 | .head>img { 62 | border-radius: 50%; 63 | height: 60px; 64 | width: 60px; 65 | border: 2px solid white; 66 | } 67 | 68 | .info { 69 | color: rgb(0, 0, 0); 70 | font-weight: bolder; 71 | font-size: 15px; 72 | margin-left: 12px; 73 | margin-top: 5px; 74 | 75 | } 76 | 77 | .room { 78 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 79 | font-weight: 500; 80 | font-style: normal; 81 | font-size: 20px; 82 | } 83 | 84 | .users { 85 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 86 | font-size: small; 87 | font-weight: 400; 88 | margin-left: 2px; 89 | position: relative; 90 | top: -2px; 91 | } 92 | 93 | .activity { 94 | margin-left: 100px; 95 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 96 | font-weight: 400; 97 | font-size: 12px; 98 | } 99 | .chat-display { 100 | overflow-y: auto; 101 | height: 65vh; 102 | display: flex; 103 | flex-direction: column; 104 | } 105 | 106 | .left-chat { 107 | background-color: rgb(109, 187, 109); 108 | color: white; 109 | justify-self: flex-start; 110 | margin-top: 20px; 111 | margin-left: 10px; 112 | word-wrap: break-word; 113 | max-width: 90%; 114 | border-radius: 12px; 115 | padding-bottom: 14px; 116 | } 117 | 118 | .name-l { 119 | font-size: 12px; 120 | margin-left: 5px; 121 | } 122 | 123 | .message { 124 | margin-top: 5px; 125 | margin-left: 15px; 126 | word-wrap: break-word; 127 | margin-right: 15px; 128 | font-family:Verdana, Geneva, Tahoma, sans-serif; 129 | font-optical-sizing: auto; 130 | font-weight: normal; 131 | font-style: normal; 132 | margin-bottom: 5px; 133 | } 134 | 135 | .time-l { 136 | font-weight: lighter; 137 | font-size: 10px; 138 | margin-bottom: 5px; 139 | float: right; 140 | margin-right: 12px; 141 | } 142 | 143 | .right-chat { 144 | background-color: white; 145 | justify-self: flex-end; 146 | margin-top: 20px; 147 | margin-right: 10px; 148 | word-wrap: break-word; 149 | max-width: 90%; 150 | border-radius: 12px; 151 | padding-bottom: 16px; 152 | } 153 | 154 | .name-r { 155 | font-size: 12px; 156 | margin-left: 140px; 157 | } 158 | 159 | .time-r { 160 | font-weight: lighter; 161 | font-size: 10px; 162 | float: right; 163 | position: relative; 164 | top: 4px; 165 | left: -10px; 166 | } 167 | 168 | .sendmessage { 169 | margin-left: 13px; 170 | width: 100%; 171 | margin-bottom: 10px; 172 | } 173 | 174 | .sendmessage .msg>input { 175 | padding: 6px; 176 | border-radius: 6px; 177 | height: 28px; 178 | width: 76%; 179 | border: none; 180 | line-height: 24px; 181 | } 182 | 183 | .sendmessage .msg>button { 184 | height: 40px; 185 | border-radius: 6px; 186 | border: none; 187 | background-color: rgb(0, 81, 255); 188 | color: white; 189 | width: 50px; 190 | margin-left: 5px; 191 | } 192 | 193 | 194 | 195 | ::-webkit-scrollbar { 196 | width: 2px; 197 | height: 2px; 198 | } 199 | 200 | ::-webkit-scrollbar-button { 201 | width: 0px; 202 | height: 0px; 203 | } 204 | 205 | ::-webkit-scrollbar-thumb { 206 | background: #00000000; 207 | border: 0px none #ffffff00; 208 | border-radius: 50px; 209 | } 210 | 211 | ::-webkit-scrollbar-thumb:hover { 212 | background: #00000000; 213 | } 214 | 215 | ::-webkit-scrollbar-thumb:active { 216 | background: #00000000; 217 | } 218 | 219 | ::-webkit-scrollbar-track { 220 | background: #ffffff00; 221 | border: 0px none #ffffff00; 222 | border-radius: 50px; 223 | } 224 | 225 | ::-webkit-scrollbar-track:hover { 226 | background: #66666600; 227 | } 228 | 229 | ::-webkit-scrollbar-track:active { 230 | background: #33333300; 231 | } 232 | 233 | ::-webkit-scrollbar-corner { 234 | background: transparent; 235 | } 236 | 237 | .admin { 238 | background-color: #ffffff6a; 239 | text-align: center; 240 | font-size: small; 241 | margin-top: 5px; 242 | width: 350px; 243 | margin-left: 25px; 244 | border-radius: 5px; 245 | font-weight: 500; 246 | } 247 | 248 | @media only screen and (max-width: 480px) { 249 | body { 250 | height: 93vh; 251 | font-family: "Open Sans", sans-serif; 252 | font-optical-sizing: auto; 253 | font-weight: bold; 254 | font-style: normal; 255 | font-variation-settings: 256 | "wdth" 100; 257 | display: block; 258 | width: 47vb; 259 | 260 | 261 | } 262 | 263 | .container { 264 | background-image: url("./chat\ back.png"); 265 | background-size: cover; 266 | height: 93vh; 267 | width: 100%; 268 | border: none; 269 | border-radius: 0; 270 | padding: 0; 271 | box-sizing: border-box; 272 | overflow: hidden; 273 | } 274 | 275 | .admin { 276 | width: calc(100vw - 40px); 277 | margin-left: 20px; 278 | } 279 | 280 | .join { 281 | width: 98.5%; 282 | background-color: rgb(207, 205, 205); 283 | height: 35px; 284 | padding-left: 6px; 285 | border-radius: 0px 0px 0px 0px; 286 | } 287 | 288 | .join input { 289 | border: none; 290 | font-size: x-small; 291 | padding: 5px; 292 | margin-top: 6px; 293 | } 294 | 295 | 296 | .join-btn { 297 | background-color: rgb(0, 162, 255); 298 | border: none; 299 | padding: 5px; 300 | color: white; 301 | } 302 | 303 | .head { 304 | background-color: rgb(255, 255, 255); 305 | display: flex; 306 | padding: 5px; 307 | } 308 | 309 | .head>img { 310 | border-radius: 50%; 311 | height: 50px; 312 | width: 50px; 313 | border: 2px solid white; 314 | } 315 | 316 | .info { 317 | color: rgb(0, 0, 0); 318 | font-weight: bolder; 319 | font-size: 15px; 320 | margin-left: 15px; 321 | } 322 | 323 | .activity { 324 | margin-left: 92px; 325 | } 326 | 327 | .chat-display { 328 | overflow-y: auto; 329 | height: 72vh; 330 | } 331 | 332 | .sendmessage { 333 | margin-left: 13px; 334 | width: 100%; 335 | } 336 | 337 | .sendmessage .msg>input { 338 | padding: 6px; 339 | border-radius: 6px; 340 | height: 28px; 341 | width: 76%; 342 | border: none; 343 | line-height: 24px; 344 | } 345 | 346 | .right-chat { 347 | background-color: white; 348 | width: 180px; 349 | border-radius: 15px 0px 15px 15px; 350 | -webkit-border-radius: 15px 0px 15px 15px; 351 | -moz-border-radius: 15px 0px 15px 15px; 352 | position: relative; 353 | margin-bottom: 25px; 354 | left: 190px; 355 | } 356 | .name-r { 357 | font-size: 12px; 358 | margin-left: 122px; 359 | } 360 | 361 | 362 | } 363 | --------------------------------------------------------------------------------