Skip to content

Commit 97ba790

Browse files
committed
Add join/leave notifications
Update frontend and backend to show notifications when users leave/join rooms
1 parent d2c22fb commit 97ba790

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

Backend/CollabService/utils/roomManager.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const rooms = {}; // { roomId: [sockets] }
44
function manageRoom(ws, roomId, userId, type) {
55
console.log(`manageRoom function: ${type} ${userId}`)
66

7+
ws.hasLeft = false;
8+
79
switch (type) {
810
case "join":
911

@@ -47,10 +49,14 @@ function manageRoom(ws, roomId, userId, type) {
4749

4850
// notify users of roomId of updated user list to display in frontend
4951
broadcastUserListUpdate(roomId);
52+
// notify users of user joining the room
53+
broadcastUserJoin(roomId, userId);
5054
break;
5155

5256
case "leave":
5357
// remove from room
58+
59+
/*
5460
rooms[roomId].sockets = rooms[roomId].sockets.filter(socket => socket !== ws);
5561
rooms[roomId].userIds = rooms[roomId].userIds.filter(user => user !== userId);
5662
@@ -63,6 +69,8 @@ function manageRoom(ws, roomId, userId, type) {
6369
} else {
6470
broadcastUserListUpdate(roomId);
6571
}
72+
*/
73+
handleUserLeave(ws, roomId, userId);
6674
break;
6775
default:
6876
console.error(`Unknown room management type: ${type}`);
@@ -72,6 +80,7 @@ function manageRoom(ws, roomId, userId, type) {
7280
// Remove user when they disconnect
7381
ws.on('close', () => {
7482

83+
/*
7584
if (rooms[roomId]) {
7685
rooms[roomId].sockets = rooms[roomId].sockets.filter(client => client !== ws);
7786
rooms[roomId].userIds = rooms[roomId].userIds.filter(user => user != userId);
@@ -82,14 +91,38 @@ function manageRoom(ws, roomId, userId, type) {
8291
delete rooms[roomId];
8392
console.log(`Room ${roomId} is empty and deleted`);
8493
} else {
85-
// notify the remaining party about leave
94+
// notify users of roomId of updated user list to display in frontend
8695
broadcastUserListUpdate(roomId);
96+
// notify users of user leaving the room
97+
broadcastUserLeaveFromSocketClose(roomId, userId);
8798
}
8899
89100
}
101+
*/
102+
handleUserLeave(ws, roomId, userId)
90103

91104
});
92105

106+
function handleUserLeave(ws, roomId, userId, leaveType) {
107+
if (ws.hasLeft) return;
108+
ws.hasLeft = true;
109+
110+
if (rooms[roomId]) {
111+
rooms[roomId].sockets = rooms[roomId].sockets.filter(client => client !== ws);
112+
rooms[roomId].userIds = rooms[roomId].userIds.filter(user => user !== userId);
113+
114+
console.log(`User ${userId} left room ${roomId}`);
115+
116+
if (rooms[roomId].sockets.length === 0) {
117+
delete rooms[roomId];
118+
console.log(`Room ${roomId} is empty and deleted`);
119+
} else {
120+
broadcastUserListUpdate(roomId);
121+
broadcastUserLeft(roomId, userId);
122+
}
123+
}
124+
}
125+
93126
function broadcastUserListUpdate(roomId) {
94127
const userList = rooms[roomId].userIds;
95128

@@ -99,6 +132,24 @@ function manageRoom(ws, roomId, userId, type) {
99132
}
100133
})
101134
}
135+
136+
function broadcastUserLeft(roomId, userId) {
137+
138+
rooms[roomId].sockets.forEach((client) => {
139+
if (client.readyState == WebSocket.OPEN) {
140+
client.send(JSON.stringify({ type: 'userLeft', user: userId}));
141+
}
142+
})
143+
}
144+
145+
function broadcastUserJoin(roomId, userId) {
146+
147+
rooms[roomId].sockets.forEach((client) => {
148+
if (client.readyState == WebSocket.OPEN) {
149+
client.send(JSON.stringify({ type: 'userJoin', user: userId}));
150+
}
151+
})
152+
}
102153
}
103154

104155
function getRoom(roomId) {

Frontend/src/components/collab/CollabNavigationBar.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Button, Container, Navbar, Nav, Dropdown, DropdownToggle, DropdownMenu,
44
const CollabNavigationBar = ({ handleExit, users, handleCodeRun, setLanguage, language }) => {
55

66
return (
7-
<Navbar className='bg-light' sticky='top'>
7+
<Navbar className='bg-light' sticky='top' style={{ zIndex: 1040 }}>
88
<Container className='d-flex justify-content-between'>
99

1010
{/* Language Dropdown on left */}

Frontend/src/components/collab/CollaborationSpace.jsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const CollaborationSpace = () => {
3333
"c++": "10.2.0"
3434
};
3535

36+
{/* State management for access denied toast */}
3637
const [showAccessDeniedToast, setShowAccessDeniedToast] = useState(false);
3738
const [toastMessage, setToastMessage] = useState('');
3839
const [loading, setLoading] = useState(true);
@@ -42,6 +43,18 @@ const CollaborationSpace = () => {
4243
navigate("/home");
4344
};
4445

46+
{/* State management for user join/leave toast */}
47+
const [notifs, setNotifs] = useState([]);
48+
49+
const addNotif = (message) => {
50+
const id = Date.now(); // unique id based on timestamp
51+
setNotifs((prevNotifs) => [...prevNotifs, {id, message}]);
52+
53+
// Remove notif after 2 seconds
54+
setTimeout(() => {
55+
setNotifs((prevNotifs) => prevNotifs.filter((notif) => notif.id !== id))
56+
}, 1500);
57+
};
4558

4659
{/* Set up websockets for room management on client side, and collaboration for Yjs */}
4760
useEffect(() => {
@@ -87,7 +100,7 @@ const CollaborationSpace = () => {
87100
// on getting a reply from server
88101
websocket.onmessage = (event) => {
89102
const data = JSON.parse(event.data);
90-
console.log(`[FRONTEND] data message is ${JSON.stringify(data)}`);
103+
// console.log(`[FRONTEND] data message is ${JSON.stringify(data)}`);
91104
switch (data.type) {
92105
case 'usersListUpdate':
93106
setUsers(data.users); // Update the user list
@@ -101,7 +114,12 @@ const CollaborationSpace = () => {
101114
break;
102115
case 'newMessage':
103116
console.log("adding message", data.message)
104-
setMessages((prevMessages) => [...prevMessages, data.message]);
117+
break;
118+
case 'userJoin':
119+
addNotif(`User ${data.user} has joined.`)
120+
break;
121+
case 'userLeft':
122+
addNotif(`User ${data.user} has left`)
105123
break;
106124
default:
107125
console.log("No messages received from room management server");
@@ -205,6 +223,18 @@ const CollaborationSpace = () => {
205223
</ToastContainer>
206224
) : (
207225
<>
226+
{/* Toast Container for Join/Leave notifications */}
227+
<ToastContainer className='p-3' position='top-center' style={{ zIndex: 1050 }}>
228+
{notifs.map((notif) => (
229+
<Toast key={notif.id} className='mb-2' style={{ backgroundColor: 'rgba(255, 255, 255, 0.8)' }}>
230+
<Toast.Body>
231+
<strong className='text-black'>{notif.message}</strong>
232+
</Toast.Body>
233+
</Toast>
234+
))}
235+
</ToastContainer>
236+
237+
{/* Main component content */}
208238
<CollabNavigationBar handleExit={handleExit} handleCodeRun={handleCodeRun} users={users} setLanguage={setLanguage} language={language}/>
209239
<Container fluid style={{ marginTop: '20px' }}>
210240
<Row>

0 commit comments

Comments
 (0)