Skip to content

Commit fbb5dbc

Browse files
authored
Merge branch 'main' into topic-implementation
2 parents 679efbb + e9ff6b4 commit fbb5dbc

File tree

12 files changed

+229
-77
lines changed

12 files changed

+229
-77
lines changed

Backend/CollabService/utils/roomManager.js

Lines changed: 33 additions & 18 deletions
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,22 +49,12 @@ 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":
53-
// remove from room
54-
rooms[roomId].sockets = rooms[roomId].sockets.filter(socket => socket !== ws);
55-
rooms[roomId].userIds = rooms[roomId].userIds.filter(user => user !== userId);
56-
57-
console.log(`User ${userId} left the room ${roomId}`);
58-
59-
// if no one in room delete room
60-
if (rooms[roomId].sockets.length === 0) {
61-
delete rooms[roomId];
62-
console.log(`Room ${roomId} is empty and deleted`);
63-
} else {
64-
broadcastUserListUpdate(roomId);
65-
}
57+
handleUserLeave(ws, roomId, userId);
6658
break;
6759
default:
6860
console.error(`Unknown room management type: ${type}`);
@@ -71,24 +63,29 @@ function manageRoom(ws, roomId, userId, type) {
7163

7264
// Remove user when they disconnect
7365
ws.on('close', () => {
66+
handleUserLeave(ws, roomId, userId)
67+
68+
});
69+
70+
function handleUserLeave(ws, roomId, userId, leaveType) {
71+
if (ws.hasLeft) return;
72+
ws.hasLeft = true;
7473

7574
if (rooms[roomId]) {
7675
rooms[roomId].sockets = rooms[roomId].sockets.filter(client => client !== ws);
77-
rooms[roomId].userIds = rooms[roomId].userIds.filter(user => user != userId);
76+
rooms[roomId].userIds = rooms[roomId].userIds.filter(user => user !== userId);
7877

7978
console.log(`User ${userId} left room ${roomId}`);
8079

8180
if (rooms[roomId].sockets.length === 0) {
8281
delete rooms[roomId];
8382
console.log(`Room ${roomId} is empty and deleted`);
8483
} else {
85-
// notify the remaining party about leave
8684
broadcastUserListUpdate(roomId);
85+
broadcastUserLeft(roomId, userId);
8786
}
88-
8987
}
90-
91-
});
88+
}
9289

9390
function broadcastUserListUpdate(roomId) {
9491
const userList = rooms[roomId].userIds;
@@ -99,6 +96,24 @@ function manageRoom(ws, roomId, userId, type) {
9996
}
10097
})
10198
}
99+
100+
function broadcastUserLeft(roomId, userId) {
101+
102+
rooms[roomId].sockets.forEach((client) => {
103+
if (client.readyState == WebSocket.OPEN) {
104+
client.send(JSON.stringify({ type: 'userLeft', user: userId}));
105+
}
106+
})
107+
}
108+
109+
function broadcastUserJoin(roomId, userId) {
110+
111+
rooms[roomId].sockets.forEach((client) => {
112+
if (client.readyState == WebSocket.OPEN) {
113+
client.send(JSON.stringify({ type: 'userJoin', user: userId}));
114+
}
115+
})
116+
}
102117
}
103118

104119
function getRoom(roomId) {

Backend/CollabService/websocket/roomManagerSocket.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ function setupWebSocket(server) {
3131
console.log('got message ', data.message);
3232
broadcastMessage(data.roomId, data.message);
3333
break;
34+
case 'languageChange':
35+
// send message to all users in room
36+
console.log('got language change ', data.language);
37+
broadcastLanguageChange(data.roomId, data.language, data.user);
38+
break;
3439
default:
3540
console.error('Unknown message type');
3641
}
@@ -52,6 +57,17 @@ function setupWebSocket(server) {
5257
}
5358
}
5459

60+
function broadcastLanguageChange(roomId, language, userId) {
61+
const room = getRoom(roomId)
62+
if (room) {
63+
room.sockets.forEach((client) => {
64+
if (client.readyState === WebSocket.OPEN) {
65+
client.send(JSON.stringify({ type: 'languageChange', language: language , user: userId}));
66+
}
67+
})
68+
}
69+
}
70+
5571
}
5672

5773
module.exports = { setupWebSocket };

Backend/user-service/controller/auth-controller.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ export async function handleVerifyToken(req, res) {
3939
return res.status(500).json({ message: err.message });
4040
}
4141
}
42+
43+
// Add function to handle verifying admin
44+
export async function handleVerifyAdmin(req, res) {
45+
try {
46+
const adminUser = req.user;
47+
return res.status(200).json({ message: "Admin status verified", data: adminUser});
48+
} catch (err) {
49+
return res.status(500).json({ message: err.message });
50+
}
51+
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import express from "express";
22

3-
import { handleLogin, handleVerifyToken } from "../controller/auth-controller.js";
4-
import { verifyAccessToken } from "../middleware/basic-access-control.js";
3+
import { handleLogin, handleVerifyAdmin, handleVerifyToken } from "../controller/auth-controller.js";
4+
import { verifyAccessToken, verifyIsAdmin } from "../middleware/basic-access-control.js";
55

66
const router = express.Router();
77

88
router.post("/login", handleLogin);
99

1010
router.get("/verify-token", verifyAccessToken, handleVerifyToken);
1111

12+
// add new route to check for admin
13+
router.get("/verify-admin", verifyAccessToken, verifyIsAdmin, handleVerifyAdmin);
14+
1215
export default router;

Frontend/src/App.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
color: #61dafb;
2929
}
3030

31+
.scrollable-accordion-body {
32+
max-height: 300px; /* Set the maximum height */
33+
overflow-y: auto; /* Enable vertical scrolling */
34+
}
35+
3136
@keyframes App-logo-spin {
3237
from {
3338
transform: rotate(0deg);

Frontend/src/components/collab/Chat.jsx

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import React, { useState } from 'react';
2-
import { Accordion, ListGroup, ListGroupItem, Form, Button } from 'react-bootstrap';
1+
import React, {useState, useRef, useEffect} from 'react';
2+
import {Accordion, ListGroup, ListGroupItem, Form, Button, Badge} from 'react-bootstrap';
33

44
const Chat = ({ currentUser, messages, sendMessage }) => {
55
const [text, setText] = useState('');
6+
const chatContainerRef = useRef(null);
7+
const [isAccordionOpen, setIsAccordionOpen] = useState(true);
68

79
const handleSend = () => {
810
console.log("I am sending")
@@ -12,27 +14,61 @@ const Chat = ({ currentUser, messages, sendMessage }) => {
1214
}
1315
};
1416

17+
useEffect(() => {
18+
if (chatContainerRef.current) {
19+
chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
20+
}
21+
}, [messages]);
22+
1523
return (
16-
<Accordion defaultActiveKey="0" className='mt-3'>
17-
<Accordion.Item eventKey="0">
18-
<Accordion.Header>
19-
Chat
20-
</Accordion.Header>
21-
<Accordion.Body>
24+
<Accordion defaultActiveKey="0" className='mt-3' onSelect={(eventKey) => setIsAccordionOpen(eventKey === "0")}>
25+
<Accordion.Item eventKey="0">
26+
<Accordion.Header>
27+
Chat
28+
</Accordion.Header>
29+
<Accordion.Body>
30+
<div
31+
ref={chatContainerRef}
32+
style={{
33+
maxHeight: '300px',
34+
overflowY: 'auto',
35+
marginBottom: '1rem',
36+
}}
37+
>
2238
<ListGroup>
2339
{messages.map((msg, idx) => (
24-
<ListGroupItem
25-
key={idx}
26-
style={{
27-
backgroundColor: msg.sender === currentUser ? "#d1e7dd" : "#50d7da",
28-
textAlign: msg.sender === currentUser ? "right" : "left"
29-
}}
30-
>
31-
{msg.text}
32-
</ListGroupItem>
40+
<>
41+
<ListGroupItem
42+
key={idx}
43+
style={{
44+
backgroundColor: msg.sender === currentUser ? "#d1e7dd" : "#50d7da",
45+
textAlign: msg.sender === currentUser ? "right" : "left"
46+
}}
47+
>
48+
{/* alternative: keep badge within same line*/}
49+
{/*{msg.sender !== currentUser && <><Badge bg="secondary">{msg.sender}</Badge> {msg.text}</>}*/}
50+
{/*{msg.sender === currentUser && <>{msg.text} <Badge bg="secondary">{msg.sender}</Badge></>}*/}
51+
52+
{msg.text}
53+
</ListGroupItem>
54+
<ListGroupItem
55+
key={idx}
56+
style={{
57+
backgroundColor: "white",
58+
textAlign: msg.sender === currentUser ? "right" : "left",
59+
border: "none",
60+
padding: "0",
61+
}}>
62+
<Badge bg="secondary">{msg.sender}</Badge>
63+
</ListGroupItem>
64+
{/* spacer */}
65+
<div style={{ height: '10px' }}></div>
66+
</>
3367
))}
3468
</ListGroup>
35-
<Form className="mt-3" onSubmit={(e) => { e.preventDefault(); handleSend(); }}>
69+
</div>
70+
{isAccordionOpen && ( // Only show message input when accordion is open
71+
<Form onSubmit={(e) => { e.preventDefault(); handleSend(); }}>
3672
<Form.Group controlId="messageInput">
3773
<Form.Control
3874
type="text"
@@ -45,8 +81,9 @@ const Chat = ({ currentUser, messages, sendMessage }) => {
4581
Send
4682
</Button>
4783
</Form>
48-
</Accordion.Body>
49-
</Accordion.Item>
84+
)}
85+
</Accordion.Body>
86+
</Accordion.Item>
5087
</Accordion>
5188
);
5289
};

Frontend/src/components/collab/CodeSpace.jsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react'
22
import { Editor } from '@monaco-editor/react'
3-
import { Container, Stack } from 'react-bootstrap'
3+
import { Container, Stack, Spinner } from 'react-bootstrap'
44

5-
const CodeSpace = ({ handleEditorChange, code, language, output }) => {
5+
const CodeSpace = ({ handleEditorChange, loading, code, language, output, isError }) => {
66
return (
77

88
<Stack gap={3} className='h-100'>
@@ -14,9 +14,18 @@ const CodeSpace = ({ handleEditorChange, code, language, output }) => {
1414
onChange={handleEditorChange}
1515
theme='vs-dark'
1616
/>
17-
<Container style={{ height: '200px', border: '1px solid #ccc', borderRadius: '0.5rem', padding: '1rem', backgroundColor: '#f8f9fa'}}>
18-
<h5>Output</h5>
19-
<p style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap'}}>{output}</p>
17+
<Container style={{ height: '225px', border: '1px solid #ccc', borderRadius: '0.5rem', padding: '1rem', backgroundColor: '#f8f9fa', padding: '20px'}}>
18+
<h4>Output</h4>
19+
{loading ? (
20+
<div className="d-flex justify-content-center align-items-center" style={{ height: '100%' }}>
21+
<Spinner animation="border" variant="primary" />
22+
<span className="ms-1">Running...</span>
23+
</div>
24+
): (
25+
<div style={{ height: '150px', maxHeight: '150px', overflowY: 'auto', fontFamily: 'monospace', whiteSpace: 'pre-wrap', border: '1px solid rgba(0, 0, 0, 0.2)', borderRadius: '0.5rem'}}>
26+
<p style={{ color: isError ? 'red' : '#212529' }}>{output}</p>
27+
</div>
28+
)}
2029
</Container>
2130
</Stack>
2231

Frontend/src/components/collab/CollabNavigationBar.jsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import React from 'react';
22
import { Button, Container, Navbar, Nav, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'react-bootstrap';
33

4-
const CollabNavigationBar = ({ handleExit, users, handleCodeRun, setLanguage, language }) => {
4+
const CollabNavigationBar = ({ handleExit, users, handleCodeRun, setLanguage, language, userLangChange }) => {
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 */}
1111
<Nav className='me-auto'>
12-
<Dropdown onSelect={(eventKey) => setLanguage(eventKey)}>
12+
<Dropdown onSelect={(eventKey) => {
13+
setLanguage(eventKey);
14+
userLangChange(eventKey);
15+
}}>
1316
<DropdownToggle style={{ backgroundColor: 'transparent', border: '1px solid #ccc', borderRadius: '0', color: '#000', padding: '8px 16px'}}>
1417
{language}
1518
</DropdownToggle>
1619
<DropdownMenu>
1720
<DropdownItem eventKey="python">Python</DropdownItem>
18-
<DropdownItem eventKey="c++">C++</DropdownItem>
21+
<DropdownItem eventKey="javascript">Javascript</DropdownItem>
1922
<DropdownItem eventKey="java">Java</DropdownItem>
2023
</DropdownMenu>
2124
</Dropdown>

0 commit comments

Comments
 (0)