Skip to content

Commit 564229e

Browse files
authored
Merge pull request #45 from CS3219-AY2324S1/matching-service
Matching service
2 parents 62f5cd9 + a1e7803 commit 564229e

File tree

9 files changed

+2117
-61
lines changed

9 files changed

+2117
-61
lines changed

backend/package-lock.json

Lines changed: 1883 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
"dotenv": "^16.3.1",
1515
"express": "~4.16.1",
1616
"firebase": "^10.4.0",
17-
"http-errors": "~1.6.3"
17+
"firebase-admin": "^11.11.0",
18+
"http": "^0.0.1-security",
19+
"http-errors": "~1.6.3",
20+
"socket.io": "^4.7.2"
1821
},
1922
"devDependencies": {
2023
"@types/express": "^4.17.18",

backend/src/index.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,34 @@ import {
55
handleLogout,
66
handleSignUp,
77
} from "./auth/auth.controller";
8+
import { handleCreateUser, handleGetUser } from "./user/user.controller";
9+
import { initializeApp } from 'firebase/app';
10+
import { getFirestore } from 'firebase/firestore';
11+
import { firebaseConfig } from "./firebase/firebase.config";
12+
import { initializeMatchingService } from './matching/matching.service'; // Import the matching service function
13+
import { Socket, Server } from 'socket.io';
14+
import { Server as ServerHttp } from 'http';
815
import { handleCreateUser, handleGetUser, handleUpdateUser } from "./user/user.controller";
916
const app = express();
1017
const port = 3001;
11-
1218
app.use(cors());
1319
app.use(express.json());
1420

21+
const httpServer = new ServerHttp(app);
22+
const io = new Server(httpServer, {
23+
cors: {
24+
origin: '*',
25+
},
26+
});
27+
1528
app.get("/", (req, res) => {
1629
res.send("Hello World!");
1730
});
1831

32+
// Initialize Firebase Firestore and other configurations here
33+
initializeApp(firebaseConfig);
34+
const db = getFirestore();
35+
1936
app.post("/signup", handleSignUp);
2037
app.post("/login", handleLogin);
2138
app.delete("/logout", handleLogout);
@@ -24,6 +41,17 @@ app.post("/user", handleCreateUser);
2441
app.get("/user", handleGetUser);
2542
app.put("/user", handleUpdateUser);
2643

27-
app.listen(port, () => {
44+
//Socket logic
45+
// Maintain an array to store active users seeking a match
46+
const activeUsers: { socket: Socket; preferences: any }[] = [];
47+
48+
// Initialize the matching service by passing the io object
49+
initializeMatchingService(io);
50+
51+
// httpServer.listen(socketPort, () => {
52+
// console.log(`socket listening on port ${socketPort}`);
53+
// });
54+
55+
httpServer.listen(port, () => {
2856
console.log(`Peerprep listening on port ${port}`);
2957
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Server, Socket } from 'socket.io';
2+
3+
// Your existing matching service logic
4+
export function initializeMatchingService(io: Server) {
5+
// Maintain an array to store active users seeking a match
6+
const activeUsers: { socket: Socket; preferences: any }[] = [];
7+
8+
io.on('connection', (socket: Socket) => {
9+
console.log('A user connected');
10+
11+
socket.on('startMatching', (preferences: any) => {
12+
// Add the user to the list of active users with their preferences
13+
activeUsers.push({ socket, preferences });
14+
15+
// Attempt to find a match for the user
16+
tryMatchForUser(socket, preferences);
17+
});
18+
19+
socket.on('disconnect', () => {
20+
console.log('A user disconnected');
21+
22+
// Remove the user from the list of active users when they disconnect
23+
removeUserFromActiveList(socket);
24+
});
25+
26+
// Other event handlers as needed
27+
});
28+
29+
function tryMatchForUser(socket: Socket, preferences: any) {
30+
const { difficulty, category } = preferences;
31+
32+
// Iterate through active users to find a match
33+
const matchedUser = activeUsers.find((user) => {
34+
return (
35+
user.socket !== socket && // Exclude the current user from matching with themselves
36+
user.preferences.difficulty === difficulty &&
37+
user.preferences.category === category
38+
);
39+
});
40+
41+
if (matchedUser) {
42+
// Remove both users from the active list
43+
removeUserFromActiveList(socket);
44+
removeUserFromActiveList(matchedUser.socket);
45+
46+
// Emit "matchFound" to both users
47+
socket.emit('matchFound', matchedUser.preferences);
48+
matchedUser.socket.emit('matchFound', preferences);
49+
} else {
50+
// Handle the case when no match is found for the user
51+
// You can emit a "noMatchFound" event or handle it differently
52+
}
53+
}
54+
55+
function removeUserFromActiveList(socket: Socket) {
56+
// Remove the user from the list of active users by socket ID
57+
const index = activeUsers.findIndex((user) => user.socket === socket);
58+
if (index !== -1) {
59+
activeUsers.splice(index, 1);
60+
}
61+
}
62+
}

backend/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
},
1111
"include": ["./**/*.ts"],
1212
"exclue": ["node_modules"]
13-
}
13+
}

frontend/package-lock.json

Lines changed: 80 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"react-router-dom": "^6.16.0",
2424
"react-scripts": "5.0.1",
2525
"react-simple-code-editor": "^0.13.1",
26+
"socket.io-client": "^4.7.2",
2627
"typescript": "^4.9.5",
2728
"web-vitals": "^2.1.4"
2829
},

frontend/src/components/MatchingService/MatchingForm.tsx

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import InputLabel from '@mui/material/InputLabel';
55
import MenuItem from '@mui/material/MenuItem';
66
import FormControl from '@mui/material/FormControl';
77
import Select, { SelectChangeEvent } from '@mui/material/Select';
8-
8+
import { useNavigate } from 'react-router-dom'; // Import useNavigate for redirection
9+
import socket from './socket';
910

1011
const style = {
1112
position: 'absolute' as 'absolute',
@@ -27,15 +28,46 @@ const style = {
2728
const MatchingForm = React.forwardRef(function MatchingForm() {
2829
const [difficulty, setDifficulty] = React.useState('');
2930
const [category, setCategory] = React.useState('');
31+
const [isMatching, setIsMatching] = React.useState(false); // Track matching state
32+
const navigate = useNavigate(); // Get navigate for redirection
33+
3034
const handleDiffChange = (event: SelectChangeEvent) => {
3135
setDifficulty(event.target.value);
3236
};
37+
3338
const handleCatChange = (event: SelectChangeEvent) => {
3439
setCategory(event.target.value);
3540
};
41+
3642
const handleConnect = () => {
37-
console.log('connecting...')
38-
}
43+
const preferences = {
44+
difficulty,
45+
category,
46+
};
47+
48+
// Emit the startMatching event to the server with user preferences
49+
socket.emit('startMatching', preferences);
50+
51+
// Set matching state to true when attempting to match
52+
setIsMatching(true);
53+
};
54+
55+
// Handle the "matchFound" event from the server
56+
React.useEffect(() => {
57+
socket.on('matchFound', (matchedUserPreferences) => {
58+
// Handle the matched user's preferences here
59+
console.log('Match Found:', matchedUserPreferences);
60+
setIsMatching(false); // Set matching state to false
61+
// Redirect to the question page with the code editor
62+
navigate('/question'); // Update the route as needed
63+
});
64+
65+
// Clean up the event listener when the component unmounts
66+
return () => {
67+
socket.off('matchFound');
68+
};
69+
}, [navigate]);
70+
3971
return (
4072
<Box sx={style}>
4173
<h2><center>Please select a difficulty and question category.</center></h2>
@@ -56,7 +88,6 @@ const MatchingForm = React.forwardRef(function MatchingForm() {
5688
<MenuItem value={'Medium'}>Medium</MenuItem>
5789
<MenuItem value={'Hard'}>Hard</MenuItem>
5890
</Select>
59-
{/* <FormHelperText>Difficulty of Question</FormHelperText> */}
6091
</FormControl>
6192
<FormControl sx={{ mt: 1, mb: 1, width: '100%' }}>
6293
<InputLabel id="demo-simple-select-helper-label">Category</InputLabel>
@@ -73,13 +104,16 @@ const MatchingForm = React.forwardRef(function MatchingForm() {
73104
<MenuItem value={'Algo'}>Algo</MenuItem>
74105
<MenuItem value={'ML'}>ML</MenuItem>
75106
</Select>
76-
{/* <FormHelperText>Difficulty of Question</FormHelperText> */}
77107
</FormControl>
78-
<Button sx={{ mt: '5%' }} variant="contained" onClick={handleConnect}>
79-
Connect
80-
</Button>
108+
{isMatching ? (
109+
<div>Loading...</div> // Show loading spinner
110+
) : (
111+
<Button sx={{ mt: '5%' }} variant="contained" onClick={handleConnect}>
112+
Connect
113+
</Button>
114+
)}
81115
</Box>
82116
);
83117
});
84118

85-
export default MatchingForm
119+
export default MatchingForm;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { io, Socket } from 'socket.io-client';
2+
3+
// Create a socket instance
4+
const baseUrl = process.env.REACT_APP_BASE_URL;
5+
const socket: Socket = baseUrl ? io(baseUrl) : io("http://localhost:3001"); // Replace with your server URL if defined, otherwise use default URL
6+
7+
// Event listener for when a match is found
8+
socket.on('matchFound', (user) => {
9+
// Handle the matched user data here, e.g., update state or trigger an action
10+
console.log('Match found!', user);
11+
});
12+
13+
export default socket;

0 commit comments

Comments
 (0)