Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/FUNDING.yml

This file was deleted.

3 changes: 0 additions & 3 deletions .netlify/state.json

This file was deleted.

1 change: 1 addition & 0 deletions client/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REACT_APP_API_URL=http://localhost:5000
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Avoid hardcoding the API URL in code; rely on REACT_APP_API_URL everywhere.

Since this file now defines REACT_APP_API_URL, ensure the client uses process.env.REACT_APP_API_URL instead of hardcoded "http://localhost:5000" (the AI summary notes an ENDPOINT constant in App.js). This keeps environments decoupled and avoids drift. Also consider adding .env.development and .env.production variants for clean overrides.

Reference: CRA env var conventions and caveats. (create-react-app.dev)

Run to find hardcoded endpoints:


🏁 Script executed:

#!/bin/bash
# Find hardcoded localhost endpoints in the client
rg -nP --type=js --type=jsx --type=ts --type=tsx -C2 'https?://localhost:5000|process\.env\.REACT_APP_API_URL' client/

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Search for hardcoded localhost endpoints in client source files
rg -n -C2 'https?://localhost:5000' client/ --glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx'

# Search for usage of the environment variable
rg -n -C2 'process\.env\.REACT_APP_API_URL' client/ --glob '*.js' --glob '*.jsx' --glob '*.ts' --glob '*.tsx'

Length of output: 716


Remove hardcoded API endpoints and switch to environment variables

The client currently hardcodes the API URL in two places:

  • client/src/App.js (line 7)
  • client/src/components/Chat/Chat.js (line 12)

Replace

const ENDPOINT = 'http://localhost:5000';

with

const ENDPOINT = process.env.REACT_APP_API_URL;

(you may optionally include a fallback, e.g.

const ENDPOINT = process.env.REACT_APP_API_URL || 'http://localhost:5000';

)

Additionally, create environment-specific files—.env.development and .env.production—to define REACT_APP_API_URL per environment, following the Create React App conventions: https://create-react-app.dev/docs/adding-custom-environment-variables. This ensures each deployment uses the correct API endpoint and prevents configuration drift.

🤖 Prompt for AI Agents
In client/.env around line 1 and in files client/src/App.js (line 7) and
client/src/components/Chat/Chat.js (line 12), remove the hardcoded ENDPOINT and
replace it with reading from the environment (process.env.REACT_APP_API_URL with
an optional fallback to the localhost URL); add environment-specific files
.env.development and .env.production at the project root defining
REACT_APP_API_URL for each environment per Create React App conventions, update
the two JS files to use that env var, and restart the dev/build process so the
new variables are picked up.

17,516 changes: 10,271 additions & 7,245 deletions client/package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"socket.io-client": "^2.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"start": "react-scripts --openssl-legacy-provider start",
"build": "react-scripts --openssl-legacy-provider build",
"test": "react-scripts --openssl-legacy-provider test",
"eject": "react-scripts eject"
},
"eslintConfig": {
Expand Down
1 change: 1 addition & 0 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Chat from './components/Chat/Chat';
import Join from './components/Join/Join';

import { BrowserRouter as Router, Route } from "react-router-dom";
const ENDPOINT = 'https://project-chat-application-tbfm.onrender.com';

const App = () => {
return (
Expand Down
7 changes: 7 additions & 0 deletions client/src/components/Chat/Chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
height: 100vh;
background-color: #1A1A1D;
}
.typing {
font-style: italic;
color: gray;
margin: 5px 10px;
font-size: 14px;
}


.container {
display: flex;
Expand Down
81 changes: 51 additions & 30 deletions client/src/components/Chat/Chat.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,89 @@
import React, { useState, useEffect } from "react";
import queryString from 'query-string';
import queryString from "query-string";
import io from "socket.io-client";

import TextContainer from '../TextContainer/TextContainer';
import Messages from '../Messages/Messages';
import InfoBar from '../InfoBar/InfoBar';
import Input from '../Input/Input';
import TextContainer from "../TextContainer/TextContainer";
import Messages from "../Messages/Messages";
import InfoBar from "../InfoBar/InfoBar";
import Input from "../Input/Input";

import './Chat.css';

const ENDPOINT = 'https://project-chat-application.herokuapp.com/';
import "./Chat.css";

const ENDPOINT = "http://localhost:5000";
let socket;

const Chat = ({ location }) => {
const [name, setName] = useState('');
const [room, setRoom] = useState('');
const [users, setUsers] = useState('');
const [message, setMessage] = useState('');
const [name, setName] = useState("");
const [room, setRoom] = useState("");
const [users, setUsers] = useState([]);
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const [typing, setTyping] = useState("");

useEffect(() => {
const { name, room } = queryString.parse(location.search);

socket = io(ENDPOINT);

setRoom(room);
setName(name)
setName(name);

socket.emit('join', { name, room }, (error) => {
if(error) {
socket.emit("join", { name, room }, (error) => {
if (error) {
alert(error);
}
});
}, [ENDPOINT, location.search]);
}, [location.search]);
Comment on lines 23 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Disconnect the socket on cleanup to prevent ghost users and leaked connections.

When the component unmounts or the room changes (location.search), the socket remains connected. The server won’t receive a disconnect, leaving “stuck” users in roomData and continuing to broadcast typing/messages to an unmounted UI.

   useEffect(() => {
     const { name, room } = queryString.parse(location.search);
     socket = io(ENDPOINT);

     setRoom(room);
     setName(name);

     socket.emit("join", { name, room }, (error) => {
       if (error) {
         alert(error);
       }
     });
-  }, [location.search]);
+    return () => {
+      try {
+        socket?.emit("stopTyping");
+      } catch (_) {}
+      // Ensure the server gets a proper disconnect and the client frees resources.
+      socket?.disconnect();
+    };
+  }, [location.search]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
const { name, room } = queryString.parse(location.search);
socket = io(ENDPOINT);
setRoom(room);
setName(name)
setName(name);
socket.emit('join', { name, room }, (error) => {
if(error) {
socket.emit("join", { name, room }, (error) => {
if (error) {
alert(error);
}
});
}, [ENDPOINT, location.search]);
}, [location.search]);
useEffect(() => {
const { name, room } = queryString.parse(location.search);
socket = io(ENDPOINT);
setRoom(room);
setName(name);
socket.emit("join", { name, room }, (error) => {
if (error) {
alert(error);
}
});
return () => {
try {
socket?.emit("stopTyping");
} catch (_) {}
// Ensure the server gets a proper disconnect and the client frees resources.
socket?.disconnect();
};
}, [location.search]);
🤖 Prompt for AI Agents
In client/src/components/Chat/Chat.js around lines 23 to 35, the socket is
created on mount/when location.search changes but never cleaned up; add a
cleanup function returned from the useEffect that removes listeners and fully
disconnects the socket (e.g., socket.off() for handlers you registered and
socket.disconnect() or socket.emit('disconnect') followed by socket.close()),
and also clear any module-level socket reference if present to avoid reused
stale sockets when the component remounts or room changes.


useEffect(() => {
socket.on('message', message => {
setMessages(messages => [ ...messages, message ]);
socket.on("message", (message) => {
setMessages((msgs) => [...msgs, message]);
});

socket.on("roomData", ({ users }) => {
setUsers(users);
});
}, []);

// ✅ Handle typing events
socket.on("showTyping", (msg) => {
setTyping(msg);
});

return () => {
socket.off("message");
socket.off("roomData");
socket.off("showTyping");
};
}, []);
Comment on lines 37 to +56
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Re-subscribe listeners on room change; avoid stale socket references.

This effect uses an empty dependency array, so it won’t re-run if a new socket is created on a room change. That leaves the new socket without listeners, breaking message updates and typing indicators. Also, pass explicit handler refs to off().

-  useEffect(() => {
-    socket.on("message", (message) => {
-      setMessages((msgs) => [...msgs, message]);
-    });
-
-    socket.on("roomData", ({ users }) => {
-      setUsers(users);
-    });
-
-    // ✅ Handle typing events
-    socket.on("showTyping", (msg) => {
-      setTyping(msg);
-    });
-
-    return () => {
-      socket.off("message");
-      socket.off("roomData");
-      socket.off("showTyping");
-    };
-  }, []);
+  useEffect(() => {
+    if (!socket) return;
+
+    const onMessage = (message) => setMessages((msgs) => [...msgs, message]);
+    const onRoomData = ({ users }) => setUsers(users);
+    const onShowTyping = (msg) => setTyping(msg);
+
+    socket.on("message", onMessage);
+    socket.on("roomData", onRoomData);
+    socket.on("showTyping", onShowTyping);
+
+    return () => {
+      socket.off("message", onMessage);
+      socket.off("roomData", onRoomData);
+      socket.off("showTyping", onShowTyping);
+    };
+  }, [location.search]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
socket.on('message', message => {
setMessages(messages => [ ...messages, message ]);
socket.on("message", (message) => {
setMessages((msgs) => [...msgs, message]);
});
socket.on("roomData", ({ users }) => {
setUsers(users);
});
}, []);
// ✅ Handle typing events
socket.on("showTyping", (msg) => {
setTyping(msg);
});
return () => {
socket.off("message");
socket.off("roomData");
socket.off("showTyping");
};
}, []);
useEffect(() => {
if (!socket) return;
const onMessage = (message) =>
setMessages((msgs) => [...msgs, message]);
const onRoomData = ({ users }) =>
setUsers(users);
const onShowTyping = (msg) =>
setTyping(msg);
socket.on("message", onMessage);
socket.on("roomData", onRoomData);
socket.on("showTyping", onShowTyping);
return () => {
socket.off("message", onMessage);
socket.off("roomData", onRoomData);
socket.off("showTyping", onShowTyping);
};
}, [location.search]);


const sendMessage = (event) => {
event.preventDefault();

if(message) {
socket.emit('sendMessage', message, () => setMessage(''));
if (message) {
socket.emit("sendMessage", message, () => setMessage(""));
socket.emit("stopTyping"); // stop typing after sending
}
}
};
Comment on lines 58 to +65
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against undefined socket and trim messages.

On fast interactions, sendMessage can run before the socket is ready; also avoid sending whitespace messages.

   const sendMessage = (event) => {
     event.preventDefault();

-    if (message) {
-      socket.emit("sendMessage", message, () => setMessage(""));
-      socket.emit("stopTyping"); // stop typing after sending
-    }
+    if (!socket) return;
+    const text = message.trim();
+    if (text) {
+      socket.emit("sendMessage", text, () => setMessage(""));
+      socket.emit("stopTyping"); // stop typing after sending
+    }
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sendMessage = (event) => {
event.preventDefault();
if(message) {
socket.emit('sendMessage', message, () => setMessage(''));
if (message) {
socket.emit("sendMessage", message, () => setMessage(""));
socket.emit("stopTyping"); // stop typing after sending
}
}
};
const sendMessage = (event) => {
event.preventDefault();
if (!socket) return;
const text = message.trim();
if (text) {
socket.emit("sendMessage", text, () => setMessage(""));
socket.emit("stopTyping"); // stop typing after sending
}
};
🤖 Prompt for AI Agents
In client/src/components/Chat/Chat.js around lines 58 to 65, sendMessage can be
called before socket is initialized and may send whitespace-only messages;
update the handler to trim the message first and only proceed if the trimmed
message is non-empty, and guard all socket.emit calls with a check that socket
is defined (or connected) so emits (sendMessage and stopTyping) are only invoked
when socket exists; keep event.preventDefault() as-is and clear the input via
setMessage("") after a successful emit.


return (
<div className="outerContainer">
<div className="container">
<InfoBar room={room} />
<Messages messages={messages} name={name} />
<Input message={message} setMessage={setMessage} sendMessage={sendMessage} />
<InfoBar room={room} />
<Messages messages={messages} name={name} />

{/* 👇 Typing indicator */}
{typing && <p className="typing">{typing}</p>}

<Input
message={message}
setMessage={setMessage}
sendMessage={sendMessage}
socket={socket}
/>
Comment on lines +76 to +81
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Render Input only when socket is ready to avoid runtime errors in Input.

Input emits typing on change; if socket is not yet initialized, it can throw. Render-gate on socket readiness.

-        <Input
-          message={message}
-          setMessage={setMessage}
-          sendMessage={sendMessage}
-          socket={socket}
-        />
+        {socket && (
+          <Input
+            message={message}
+            setMessage={setMessage}
+            sendMessage={sendMessage}
+            socket={socket}
+          />
+        )}

Optional: Alternatively, add a null-check inside Input’s handleChange before emitting.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Input
message={message}
setMessage={setMessage}
sendMessage={sendMessage}
socket={socket}
/>
{socket && (
<Input
message={message}
setMessage={setMessage}
sendMessage={sendMessage}
socket={socket}
/>
)}
🤖 Prompt for AI Agents
In client/src/components/Chat/Chat.js around lines 76 to 81, the Input component
can call socket.emit during typing before socket is initialized causing runtime
errors; fix by rendering-gating Input only when socket is ready (e.g., render
Input only if socket != null and socket.connected is true or socket.readyState
indicates open) so it won't mount without a valid socket, or alternatively add a
defensive null-check in Input’s change handler before calling socket.emit
(return early if socket is null/undefined).

</div>
<TextContainer users={users}/>

<TextContainer users={users} />
</div>
);
}
};

export default Chat;
59 changes: 40 additions & 19 deletions client/src/components/Input/Input.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
import React from 'react';

import './Input.css';

const Input = ({ setMessage, sendMessage, message }) => (
<form className="form">
<input
className="input"
type="text"
placeholder="Type a message..."
value={message}
onChange={({ target: { value } }) => setMessage(value)}
onKeyPress={event => event.key === 'Enter' ? sendMessage(event) : null}
/>
<button className="sendButton" onClick={e => sendMessage(e)}>Send</button>
</form>
)

export default Input;
import React, { useEffect, useRef } from "react";

import "./Input.css";

const Input = ({ message, setMessage, sendMessage, socket }) => {
const typingTimeoutRef = useRef(null);

const handleChange = (e) => {
setMessage(e.target.value);

// Emit typing event
socket.emit("typing");

// Clear old timeout
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);

// After 1.5s of no typing, send stopTyping
typingTimeoutRef.current = setTimeout(() => {
socket.emit("stopTyping");
}, 1500);
};
Comment on lines +8 to +21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard socket emits and add a cleanup for the typing timer.

Avoid exceptions when socket is null/not connected, and clear the timer on unmount to prevent orphaned timeouts.

 const handleChange = (e) => {
   setMessage(e.target.value);

   // Emit typing event
-  socket.emit("typing");
+  if (socket) socket.emit("typing");

   // Clear old timeout
   if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);

   // After 1.5s of no typing, send stopTyping
   typingTimeoutRef.current = setTimeout(() => {
-    socket.emit("stopTyping");
+    if (socket) socket.emit("stopTyping");
   }, 1500);
 };

+// Optional: clear timer on unmount
+// React 16.9 supports this pattern
+// useEffect(() => () => { if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current); }, []);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleChange = (e) => {
setMessage(e.target.value);
// Emit typing event
socket.emit("typing");
// Clear old timeout
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
// After 1.5s of no typing, send stopTyping
typingTimeoutRef.current = setTimeout(() => {
socket.emit("stopTyping");
}, 1500);
};
const handleChange = (e) => {
setMessage(e.target.value);
// Emit typing event
if (socket) socket.emit("typing");
// Clear old timeout
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
// After 1.5s of no typing, send stopTyping
typingTimeoutRef.current = setTimeout(() => {
if (socket) socket.emit("stopTyping");
}, 1500);
};
// Optional: clear timer on unmount
// React 16.9 supports this pattern
// useEffect(() => () => {
// if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
// }, []);
🤖 Prompt for AI Agents
In client/src/components/Input/Input.js around lines 8 to 21, the code emits
"typing" and "stopTyping" without checking socket and sets a timeout that isn't
cleared on unmount; update handleChange to guard socket emits (e.g., if (socket
&& socket.emit) socket.emit(...)) to avoid exceptions when socket is
null/disconnected, and add a useEffect cleanup that clears
typingTimeoutRef.current (if present) on unmount to prevent orphaned timeouts;
also ensure you clear any existing timeout before setting a new one as currently
implemented.


return (
<form className="form" onSubmit={sendMessage}>
<input
className="input"
type="text"
placeholder="Type a message..."
value={message}
onChange={handleChange}
onKeyPress={(e) => (e.key === "Enter" ? sendMessage(e) : null)}
/>
Comment on lines +24 to +32
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix double-submit: Enter triggers both onKeyPress and form onSubmit.

As written, pressing Enter will call sendMessage twice (once from onKeyPress and once from form submit), which can duplicate messages. Drop onKeyPress and rely on the form’s onSubmit.

   <input
     className="input"
     type="text"
     placeholder="Type a message..."
     value={message}
     onChange={handleChange}
-    onKeyPress={(e) => (e.key === "Enter" ? sendMessage(e) : null)}
   />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<form className="form" onSubmit={sendMessage}>
<input
className="input"
type="text"
placeholder="Type a message..."
value={message}
onChange={handleChange}
onKeyPress={(e) => (e.key === "Enter" ? sendMessage(e) : null)}
/>
<form className="form" onSubmit={sendMessage}>
<input
className="input"
type="text"
placeholder="Type a message..."
value={message}
onChange={handleChange}
/>
🤖 Prompt for AI Agents
In client/src/components/Input/Input.js around lines 24 to 32, the input has an
onKeyPress that calls sendMessage which combined with the form's onSubmit causes
double submission; remove the onKeyPress prop from the input so Enter only
triggers the form onSubmit, and verify sendMessage called by onSubmit still
prevents default and handles the input state (no other duplicate call sites
remain).

<button className="sendButton" type="submit">
Send
</button>
</form>
);
};

export default Input;
63 changes: 52 additions & 11 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,89 @@ const socketio = require('socket.io');
const cors = require('cors');

const { addUser, removeUser, getUser, getUsersInRoom } = require('./users');

const router = require('./router');

const app = express();
const server = http.createServer(app);
const io = socketio(server);
const path = require("path");


app.use(cors());

app.use(router);

io.on('connect', (socket) => {
// User joins a room
socket.on('join', ({ name, room }, callback) => {
const { error, user } = addUser({ id: socket.id, name, room });

if(error) return callback(error);
if (error) return callback(error);

socket.join(user.room);

socket.emit('message', { user: 'admin', text: `${user.name}, welcome to room ${user.room}.`});
socket.broadcast.to(user.room).emit('message', { user: 'admin', text: `${user.name} has joined!` });
// ✅ Save user info on socket for typing events
socket.data = { name: user.name, room: user.room };

socket.emit('message', {
user: 'admin',
text: `${user.name}, welcome to room ${user.room}.`
});

socket.broadcast.to(user.room).emit('message', {
user: 'admin',
text: `${user.name} has joined!`
});

io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room) });
io.to(user.room).emit('roomData', {
room: user.room,
users: getUsersInRoom(user.room)
});

callback();
});

// ✅ User typing
socket.on('typing', () => {
const { name, room } = socket.data;
socket.to(room).emit('showTyping', `${name} is typing...`);
});

// ✅ User stopped typing
socket.on('stopTyping', () => {
const { room } = socket.data;
socket.to(room).emit('showTyping', '');
});
Comment on lines +49 to +59
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Harden typing/stopTyping against pre-join races.

If a client emits typing before join finishes, socket.data may be undefined, causing a crash. Guard and no-op when room isn’t available.

 socket.on('typing', () => {
-  const { name, room } = socket.data;
-  socket.to(room).emit('showTyping', `${name} is typing...`);
+  const data = socket.data || {};
+  if (!data.room) return;
+  socket.to(data.room).emit('showTyping', `${data.name} is typing...`);
 });

 socket.on('stopTyping', () => {
-  const { room } = socket.data;
-  socket.to(room).emit('showTyping', '');
+  const data = socket.data || {};
+  if (!data.room) return;
+  socket.to(data.room).emit('showTyping', '');
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// ✅ User typing
socket.on('typing', () => {
const { name, room } = socket.data;
socket.to(room).emit('showTyping', `${name} is typing...`);
});
// ✅ User stopped typing
socket.on('stopTyping', () => {
const { room } = socket.data;
socket.to(room).emit('showTyping', '');
});
// ✅ User typing
socket.on('typing', () => {
const data = socket.data || {};
if (!data.room) return;
socket.to(data.room).emit('showTyping', `${data.name} is typing...`);
});
// ✅ User stopped typing
socket.on('stopTyping', () => {
const data = socket.data || {};
if (!data.room) return;
socket.to(data.room).emit('showTyping', '');
});
🤖 Prompt for AI Agents
In server/index.js around lines 46 to 56, the typing and stopTyping handlers
assume socket.data (and its room/name) always exist which can crash if a client
emits before join completes; guard access by checking socket.data and the room
(and name for typing) and return early (no-op) when room is missing; for typing
also ensure name is present before emitting, and avoid emitting if guards fail
so pre-join races are safe.


// User sends message
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);

io.to(user.room).emit('message', { user: user.name, text: message });
io.to(user.room).emit('message', {
user: user.name,
text: message
});

callback();
});
Comment on lines +61 to 71
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard sendMessage against missing user; ack errors explicitly.

If getUser(socket.id) returns undefined, io.to(user.room) will throw. Return an error via callback instead.

 socket.on('sendMessage', (message, callback) => {
   const user = getUser(socket.id);

-  io.to(user.room).emit('message', {
-    user: user.name,
-    text: message
-  });
+  if (!user) return callback && callback('User not found');
+  io.to(user.room).emit('message', { user: user.name, text: message });
   callback();
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// User sends message
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);
io.to(user.room).emit('message', { user: user.name, text: message });
io.to(user.room).emit('message', {
user: user.name,
text: message
});
callback();
});
// User sends message
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);
if (!user) return callback && callback('User not found');
io.to(user.room).emit('message', { user: user.name, text: message });
callback();
});
🤖 Prompt for AI Agents
In server/index.js around lines 58 to 68, the sendMessage handler assumes
getUser(socket.id) always returns a user and will throw if undefined; add a
guard that checks if user is present, and if not call the provided callback with
an error (e.g., callback({ error: 'User not found' })) and return early; if the
user exists proceed to emit the message to user.room and then call callback with
success (or no args).


// User disconnects
socket.on('disconnect', () => {
const user = removeUser(socket.id);

if(user) {
io.to(user.room).emit('message', { user: 'Admin', text: `${user.name} has left.` });
io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room)});
if (user) {
io.to(user.room).emit('message', {
user: 'Admin',
text: `${user.name} has left.`
});
io.to(user.room).emit('roomData', {
room: user.room,
users: getUsersInRoom(user.room)
});
}
})
});
});

server.listen(process.env.PORT || 5000, () => console.log(`Server has started.`));
server.listen(process.env.PORT || 5000, () =>
console.log(`Server has started.`)
);
Loading