diff --git a/client/src/App.jsx b/client/src/App.jsx
index 53be66a..f4d5d71 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -37,7 +37,7 @@ function App() {
// Render a loading state until authentication check is complete
if (!authChecked) {
- return
Loading...
;
+ return Loading...
;
}
return (
diff --git a/client/src/pages/Home/DirectAccess.jsx b/client/src/pages/Home/DirectAccess.jsx
index 056cbe7..76682cd 100644
--- a/client/src/pages/Home/DirectAccess.jsx
+++ b/client/src/pages/Home/DirectAccess.jsx
@@ -7,7 +7,7 @@ import { useContext } from 'react';
import IsSearchingContext from '../../Contexts/IsSearchingContext';
import PropTypes from 'prop-types';
-const DirectAccess = ({ setSelectedUser }) => {
+const DirectAccess = ({ setSelectedUser, selectedUser, setSelectedConnection }) => {
const [placeholderValue, setPlaceholderValue] = useState('Search Here');
const [inputValue, setInputValue] = useState('');
const [searchKey, setSearchKey] = useState(0); // Key to force remount of SearchedProfile component
@@ -45,13 +45,15 @@ const DirectAccess = ({ setSelectedUser }) => {
placeholder={placeholderValue}
/>
- {isSearching ?
@@ -55,6 +77,8 @@ const ChatMenu = ({ selectedUser }) => {
ChatMenu.propTypes = {
selectedUser: PropTypes.object,
+ setSelectedUser: PropTypes.func,
+ selectedConnection: PropTypes.object,
};
export default ChatMenu;
diff --git a/client/src/pages/Home/MessageMenu.jsx b/client/src/pages/Home/MessageMenu.jsx
index f78904a..efbf9cd 100644
--- a/client/src/pages/Home/MessageMenu.jsx
+++ b/client/src/pages/Home/MessageMenu.jsx
@@ -4,11 +4,12 @@ import ChatMenu from './MessageComponents/ChatMenu';
const MessageMenu = () => {
const [selectedUser, setSelectedUser] = useState({});
+ const [selectedConnection, setSelectedConnection] = useState({});
return (
);
};
diff --git a/client/src/pages/Home/index.jsx b/client/src/pages/Home/index.jsx
index 3d50ea2..ad047f2 100644
--- a/client/src/pages/Home/index.jsx
+++ b/client/src/pages/Home/index.jsx
@@ -2,8 +2,19 @@ import LeftBar from './LeftBar';
import '../pages-styles.scss';
import MessageMenu from './MessageMenu';
import { IsSearchingProvider } from '../../Contexts/IsSearchingContext';
+import socket from '../../socket';
+import { useEffect, useContext } from 'react';
+import UserContext from '../../Contexts/userContext';
-const index = () => {
+const Index = () => {
+ const user = useContext(UserContext);
+
+ useEffect(() => {
+ socket.connect();
+ socket.auth = { user };
+ // Remove the event listener on component unmount
+ return () => socket.disconnect();
+ });
return (
@@ -14,4 +25,4 @@ const index = () => {
);
};
-export default index;
+export default Index;
diff --git a/client/src/pages/Home/normalChats.jsx b/client/src/pages/Home/normalChats.jsx
index f31056e..f4e194d 100644
--- a/client/src/pages/Home/normalChats.jsx
+++ b/client/src/pages/Home/normalChats.jsx
@@ -4,7 +4,7 @@ import UserContext from '../../Contexts/userContext';
import PropTypes from 'prop-types';
import socket from '../../socket';
-const NormalChats = ({ setSelectedUser }) => {
+const NormalChats = ({ setSelectedUser, setSelectedConnection }) => {
// State to store the chat connections
const [connectionsArr, setConnectionsArr] = useState([]);
@@ -28,6 +28,15 @@ const NormalChats = ({ setSelectedUser }) => {
return () => socket.off('connect_error');
}, []);
+ useEffect(() => {
+ socket.on('user_status_changed_normal_chats', ({ userId, isOnline }) => {
+ // Update the user status in your connectionsArr state
+ setConnectionsArr((prevConnectionsArr) => prevConnectionsArr.map((connection) => (connection.userOne._id === userId || connection.userTwo._id === userId ? { ...connection, userOne: { ...connection.userOne, isOnline }, userTwo: { ...connection.userTwo, isOnline } } : connection)));
+ });
+ // Remove the event listener on component unmount
+ return () => socket.off('user_status_changed_normal_chats');
+ }, []);
+
// Function to fetch the user's chat connections from the server
const searchConnections = async function () {
const response = await fetch('http://localhost:3000/connection/searchConnections', {
@@ -50,15 +59,11 @@ const NormalChats = ({ setSelectedUser }) => {
}
return text;
}
-
// Function to handle a click on a chat connection
const handleClick = function ({ clickedOnUser, connection }) {
- // Disconnect the socket before updating authentication data
- socket.disconnect();
- socket.auth = { connection, clickedOnUser };
- // Reconnect the socket with updated authentication data
- socket.connect();
// Update the selected user in the parent component
+ setSelectedConnection(connection);
+ socket.emit('connection_selected', { connection });
setSelectedUser(clickedOnUser);
};
@@ -105,10 +110,14 @@ const NormalChats = ({ setSelectedUser }) => {
}}
key={i}
>
-
+
+

+
+
+
{truncateText(tmpArr[i].displayName, 18)}
-
default text
+
Default Placeholder
);
@@ -127,6 +136,8 @@ const NormalChats = ({ setSelectedUser }) => {
// Define the PropTypes for the component
NormalChats.propTypes = {
setSelectedUser: PropTypes.func.isRequired,
+ selectedUser: PropTypes.object,
+ setSelectedConnection: PropTypes.func,
};
export default NormalChats;
diff --git a/client/src/pages/pages-styles.scss b/client/src/pages/pages-styles.scss
index 1e73f84..cf83c13 100644
--- a/client/src/pages/pages-styles.scss
+++ b/client/src/pages/pages-styles.scss
@@ -96,10 +96,39 @@
.chat {
@apply flex p-3 gap-3 border-y-2 border-slate-300 max-h-[5rem] items-center;
-
+
img {
@apply w-[3rem] h-[3rem] rounded-full;
}
+
+ .image_container {
+ position: relative;
+ }
+ .user-picture {
+ @apply w-[2.25rem] h-[2.25rem] rounded-full;
+ }
+ #status_offline {
+ position: absolute;
+ top: 2.1rem;
+ left: 2rem;
+ background-color: #828282;
+ height: 0.8rem;
+ width: 0.8rem;
+ border-radius: 50%;
+ // border-color: #000;
+ // border-width: 0.15rem;
+ }
+ #status_online {
+ position: absolute;
+ top: 2.1rem;
+ left: 2rem;
+ background-color: #5dfcc3;
+ height: 0.8rem;
+ width: 0.8rem;
+ border-radius: 50%;
+ // border-color: #000;
+ // border-width: 0.15rem;
+ }
.contact-name {
@apply text-base font-semibold;
}
@@ -115,9 +144,35 @@
.chat-left {
@apply w-[50%] h-[100%] flex items-center px-5;
+ .image_container {
+ position: relative;
+ }
.user-picture {
@apply w-[2.25rem] h-[2.25rem] rounded-full;
}
+ #status_offline {
+ position: absolute;
+ top: 1.5rem;
+ left: 1.5rem;
+ background-color: #828282;
+ height: 0.7rem;
+ width: 0.7rem;
+ border-radius: 50%;
+ // border-color: #000;
+ // border-width: 0.15rem;
+ }
+ #status_online {
+ position: absolute;
+ top: 1.5rem;
+ left: 1.5rem;
+ background-color: #5dfcc3;
+ height: 0.7rem;
+ width: 0.7rem;
+ border-radius: 50%;
+ // border-color: #000;
+ // border-width: 0.15rem;
+ }
+
.user-info {
@apply px-3;
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..1c15f11
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,13 @@
+{
+ "name": "synapse",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "synapse",
+ "version": "1.0.0",
+ "license": "ISC"
+ }
+ }
+}
diff --git a/server/config/passport.js b/server/config/passport.js
index d57b6e4..430f4c6 100644
--- a/server/config/passport.js
+++ b/server/config/passport.js
@@ -27,6 +27,7 @@ passport.use(
displayName: profile.displayName,
googleId: profile.id,
provider: issuer,
+ isOnline: false,
});
userCreate.save();
diff --git a/server/controllers/userCRUD.js b/server/controllers/userCRUD.js
index 5ca6971..82c0b75 100644
--- a/server/controllers/userCRUD.js
+++ b/server/controllers/userCRUD.js
@@ -50,8 +50,6 @@ exports.findUser = async function findUser(reference) {
* @throws {Error} - If failed to update user.
*/
exports.updateUser = async function updateUser(_id, updateKeys) {
- let user = await findUser({ _id });
-
try {
const updatedUser = await User.findByIdAndUpdate(_id, updateKeys, {
new: true,
@@ -59,8 +57,7 @@ exports.updateUser = async function updateUser(_id, updateKeys) {
if (!updatedUser) {
throw new Error('User not found');
}
- user = updatedUser;
- return user;
+ return updatedUser;
} catch (error) {
throw new Error('Failed to update user');
}
diff --git a/server/index.js b/server/index.js
index 307eaff..f5eb8af 100644
--- a/server/index.js
+++ b/server/index.js
@@ -14,6 +14,8 @@ const authRoutes = require('./routes/authRoutes.js');
const messageRoutes = require('./routes/messageRoutes.js');
const googleRoutes = require('./routes/googleAuth.js');
+const { updateUser } = require('./controllers/userCRUD.js');
+
// Connect to the MongoDB database
connectDatabase();
@@ -94,35 +96,62 @@ app.use('/', googleRoutes);
// Socket.IO middleware function for authentication and authorization.
io.use((socket, next) => {
// Extract authentication data from the handshake object sent by the client.
- const { connection, clickedOnUser } = socket.handshake.auth;
-
- // Check if the 'clickedOnUser' flag exists in the authentication data.
- if (!clickedOnUser) {
- // If the flag is missing, send an error to the client and abort the connection.
- return next(new Error('User Does Not Exist'));
- }
+ const { user } = socket.handshake.auth;
// If the user is authenticated, attach the 'connection' data to the socket for later use.
- socket.connection = connection;
+ socket.curUser = user;
next();
});
+const activeConnections = {};
// Event listener for a new socket connection.
-io.on('connection', (socket) => {
+io.on('connection', async (socket) => {
// Log the ID of the connected socket.
console.log(socket.id);
+ await updateUser(socket.curUser._id, { isOnline: true });
+
+ socket.broadcast.emit('user_status_changed_chat_menu', { userId: socket.curUser._id, isOnline: true });
+ socket.broadcast.emit('user_status_changed_normal_chats', { userId: socket.curUser._id, isOnline: true });
+
+ // Function to leave the previous room and join a new one
+ const updateConnectionRoom = (newConnectionId) => {
+ const oldConnectionId = activeConnections[socket.id];
+ if (oldConnectionId !== newConnectionId) {
+ if (oldConnectionId) {
+ socket.leave(oldConnectionId);
+ // console.log(`Socket ${socket.id} left room ${oldConnectionId}`);
+ }
+ socket.join(newConnectionId);
+ activeConnections[socket.id] = newConnectionId;
+ // console.log(`Socket ${socket.id} joined room ${newConnectionId}`);
+ }
+ };
+
// Join a specific room based on the 'connection._id'.
- socket.join(socket.connection._id);
+
+ socket.on('connection_selected', (data) => {
+ updateConnectionRoom(data.connection._id); // Join the new room
+ });
// Event listener for 'private_message' events from the client.
- socket.on('private_message', async (data) => {
+ socket.on('private_message', async ({ data, selectedConnection }) => {
+ console.log('message sent');
// Emit the 'new_message' event to all sockets in the same room.
- io.to(socket.connection._id).emit('new_message', data);
+ io.to(selectedConnection._id).emit('new_message', data);
});
-
// Event listener for 'disconnect' events from the client.
socket.on('disconnect', () => {
+ const connectionId = activeConnections[socket.id];
+ if (connectionId) {
+ if (connectionId) {
+ socket.leave(connectionId);
+ delete activeConnections[socket.id];
+ }
+ }
+ updateUser(socket.curUser._id, { isOnline: false });
+ socket.broadcast.emit('user_status_changed_chat_menu', { userId: socket.curUser._id, isOnline: false });
+ socket.broadcast.emit('user_status_changed_normal_chats', { userId: socket.curUser._id, isOnline: false });
// Log a message when a user disconnects.
console.log('User has disconnected');
});
diff --git a/server/models/userModel.js b/server/models/userModel.js
index de9d840..65cf8ed 100644
--- a/server/models/userModel.js
+++ b/server/models/userModel.js
@@ -10,7 +10,8 @@ const userSchema = new Schema({
email: { type: String, required: true, unique: true }, // User's email field
displayName: { type: String, sparse: true },
googleId: { type: String, required: false, unique: true },
- provider: { type: String, required: false }
+ provider: { type: String, required: false },
+ isOnline: { type: Boolean, required: true },
// imageUrl: String, // Uncomment this line to include an imageUrl field
});
diff --git a/server/package.json b/server/package.json
index 113e72c..34d7b0b 100644
--- a/server/package.json
+++ b/server/package.json
@@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
- "dev": "nodemon --watch '**/*.js' --exec node index.js",
+ "dev": "nodemon index.js",
"test": "jest"
},
"type": "commonjs",
diff --git a/server/routes/messageRoutes.js b/server/routes/messageRoutes.js
index 93c102a..8e2a0e7 100644
--- a/server/routes/messageRoutes.js
+++ b/server/routes/messageRoutes.js
@@ -17,7 +17,6 @@ messageRouter.post('/', async (req, res) => {
// Endpoint to search for messages
const messages = await addMessage(req.body);
- console.log(messages);
res.json(messages);
});
diff --git a/server/seeders/userSeed.js b/server/seeders/userSeed.js
index 7e76856..cb7cd47 100644
--- a/server/seeders/userSeed.js
+++ b/server/seeders/userSeed.js
@@ -37,6 +37,7 @@ async function seed() {
username,
email,
displayName,
+ isOnline: false,
};
users.push(newUser);